*** empty log message ***
[anjuta-git-plugin.git] / scintilla / LexCPP.cxx
blobc6ca0650585470cf431a24f9579f0f02c794b01a
1 // Scintilla source code edit control
2 /** @file LexCPP.cxx
3 ** Lexer for C++, C, Java, and Javascript.
4 **/
5 // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <stdarg.h>
14 #include "Platform.h"
16 #include "PropSet.h"
17 #include "Accessor.h"
18 #include "StyleContext.h"
19 #include "KeyWords.h"
20 #include "Scintilla.h"
21 #include "SciLexer.h"
23 #define KEYWORD_BOXHEADER 1
24 #define KEYWORD_FOLDCONTRACTED 2
26 static bool IsOKBeforeRE(const int ch) {
27 return (ch == '(') || (ch == '=') || (ch == ',');
30 static inline bool IsAWordChar(const int ch) {
31 return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
34 static inline bool IsAWordStart(const int ch) {
35 return (ch < 0x80) && (isalnum(ch) || ch == '_');
38 static inline bool IsADoxygenChar(const int ch) {
39 return (islower(ch) || ch == '$' || ch == '@' ||
40 ch == '\\' || ch == '&' || ch == '<' ||
41 ch == '>' || ch == '#' || ch == '{' ||
42 ch == '}' || ch == '[' || ch == ']');
45 static inline bool IsStateComment(const int state) {
46 return ((state == SCE_C_COMMENT) ||
47 (state == SCE_C_COMMENTLINE) ||
48 (state == SCE_C_COMMENTDOC) ||
49 (state == SCE_C_COMMENTDOCKEYWORD) ||
50 (state == SCE_C_COMMENTDOCKEYWORDERROR));
53 static inline bool IsStateString(const int state) {
54 return ((state == SCE_C_STRING) || (state == SCE_C_VERBATIM));
57 static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
58 Accessor &styler, bool caseSensitive) {
60 WordList &keywords = *keywordlists[0];
61 WordList &keywords2 = *keywordlists[1];
62 WordList &keywords3 = *keywordlists[2];
63 WordList &keywords4 = *keywordlists[3];
65 bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;
67 // Do not leak onto next line
68 if (initStyle == SCE_C_STRINGEOL)
69 initStyle = SCE_C_DEFAULT;
71 int chPrevNonWhite = ' ';
72 int visibleChars = 0;
73 bool lastWordWasUUID = false;
75 StyleContext sc(startPos, length, initStyle, styler);
77 for (; sc.More(); sc.Forward()) {
79 if (sc.atLineStart && (sc.state == SCE_C_STRING)) {
80 // Prevent SCE_C_STRINGEOL from leaking back to previous line
81 sc.SetState(SCE_C_STRING);
84 // Handle line continuation generically.
85 if (sc.ch == '\\') {
86 if (sc.chNext == '\n' || sc.chNext == '\r') {
87 sc.Forward();
88 if (sc.ch == '\r' && sc.chNext == '\n') {
89 sc.Forward();
91 continue;
95 // Determine if the current state should terminate.
96 if (sc.state == SCE_C_OPERATOR) {
97 sc.SetState(SCE_C_DEFAULT);
98 } else if (sc.state == SCE_C_NUMBER) {
99 if (!IsAWordChar(sc.ch)) {
100 sc.SetState(SCE_C_DEFAULT);
102 } else if (sc.state == SCE_C_IDENTIFIER) {
103 if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
104 char s[100];
105 if (caseSensitive) {
106 sc.GetCurrent(s, sizeof(s));
107 } else {
108 sc.GetCurrentLowered(s, sizeof(s));
110 if (keywords.InList(s)) {
111 lastWordWasUUID = strcmp(s, "uuid") == 0;
112 sc.ChangeState(SCE_C_WORD);
113 } else if (keywords2.InList(s)) {
114 sc.ChangeState(SCE_C_WORD2);
115 } else if (keywords4.InList(s)) {
116 sc.ChangeState(SCE_C_GLOBALCLASS);
118 sc.SetState(SCE_C_DEFAULT);
120 } else if (sc.state == SCE_C_PREPROCESSOR) {
121 if (stylingWithinPreprocessor) {
122 if (IsASpace(sc.ch)) {
123 sc.SetState(SCE_C_DEFAULT);
125 } else {
126 if ((sc.ch == '\r') || (sc.ch == '\n') || (sc.Match('/', '*')) || (sc.Match('/', '/'))) {
127 sc.SetState(SCE_C_DEFAULT);
130 } else if (sc.state == SCE_C_COMMENT) {
131 if (sc.Match('*', '/')) {
132 sc.Forward();
133 sc.ForwardSetState(SCE_C_DEFAULT);
135 } else if (sc.state == SCE_C_COMMENTDOC) {
136 if (sc.Match('*', '/')) {
137 sc.Forward();
138 sc.ForwardSetState(SCE_C_DEFAULT);
139 } else if (sc.ch == '@' || sc.ch == '\\') {
140 sc.SetState(SCE_C_COMMENTDOCKEYWORD);
142 } else if (sc.state == SCE_C_COMMENTLINE || sc.state == SCE_C_COMMENTLINEDOC) {
143 if (sc.ch == '\r' || sc.ch == '\n') {
144 sc.SetState(SCE_C_DEFAULT);
145 visibleChars = 0;
147 } else if (sc.state == SCE_C_COMMENTDOCKEYWORD) {
148 if (sc.Match('*', '/')) {
149 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
150 sc.Forward();
151 sc.ForwardSetState(SCE_C_DEFAULT);
152 } else if (!IsADoxygenChar(sc.ch)) {
153 char s[100];
154 if (caseSensitive) {
155 sc.GetCurrent(s, sizeof(s));
156 } else {
157 sc.GetCurrentLowered(s, sizeof(s));
159 if (!isspace(sc.ch) || !keywords3.InList(s + 1)) {
160 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
162 sc.SetState(SCE_C_COMMENTDOC);
164 } else if (sc.state == SCE_C_STRING) {
165 if (sc.ch == '\\') {
166 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
167 sc.Forward();
169 } else if (sc.ch == '\"') {
170 sc.ForwardSetState(SCE_C_DEFAULT);
171 } else if (sc.atLineEnd) {
172 sc.ChangeState(SCE_C_STRINGEOL);
173 sc.ForwardSetState(SCE_C_DEFAULT);
174 visibleChars = 0;
176 } else if (sc.state == SCE_C_CHARACTER) {
177 if (sc.atLineEnd) {
178 sc.ChangeState(SCE_C_STRINGEOL);
179 sc.ForwardSetState(SCE_C_DEFAULT);
180 visibleChars = 0;
181 } else if (sc.ch == '\\') {
182 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
183 sc.Forward();
185 } else if (sc.ch == '\'') {
186 sc.ForwardSetState(SCE_C_DEFAULT);
188 } else if (sc.state == SCE_C_REGEX) {
189 if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == '/') {
190 sc.ForwardSetState(SCE_C_DEFAULT);
191 } else if (sc.ch == '\\') {
192 // Gobble up the quoted character
193 if (sc.chNext == '\\' || sc.chNext == '/') {
194 sc.Forward();
197 } else if (sc.state == SCE_C_VERBATIM) {
198 if (sc.ch == '\"') {
199 if (sc.chNext == '\"') {
200 sc.Forward();
201 } else {
202 sc.ForwardSetState(SCE_C_DEFAULT);
205 } else if (sc.state == SCE_C_UUID) {
206 if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {
207 sc.SetState(SCE_C_DEFAULT);
211 // Determine if a new state should be entered.
212 if (sc.state == SCE_C_DEFAULT) {
213 if (sc.Match('@', '\"')) {
214 sc.SetState(SCE_C_VERBATIM);
215 sc.Forward();
216 } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
217 if (lastWordWasUUID) {
218 sc.SetState(SCE_C_UUID);
219 lastWordWasUUID = false;
220 } else {
221 sc.SetState(SCE_C_NUMBER);
223 } else if (IsAWordStart(sc.ch) || (sc.ch == '@')) {
224 if (lastWordWasUUID) {
225 sc.SetState(SCE_C_UUID);
226 lastWordWasUUID = false;
227 } else {
228 sc.SetState(SCE_C_IDENTIFIER);
230 } else if (sc.Match('/', '*')) {
231 if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
232 sc.SetState(SCE_C_COMMENTDOC);
233 } else {
234 sc.SetState(SCE_C_COMMENT);
236 sc.Forward(); // Eat the * so it isn't used for the end of the comment
237 } else if (sc.Match('/', '/')) {
238 if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!"))
239 // Support of Qt/Doxygen doc. style
240 sc.SetState(SCE_C_COMMENTLINEDOC);
241 else
242 sc.SetState(SCE_C_COMMENTLINE);
243 } else if (sc.ch == '/' && IsOKBeforeRE(chPrevNonWhite)) {
244 sc.SetState(SCE_C_REGEX);
245 } else if (sc.ch == '\"') {
246 sc.SetState(SCE_C_STRING);
247 } else if (sc.ch == '\'') {
248 sc.SetState(SCE_C_CHARACTER);
249 } else if (sc.ch == '#' && visibleChars == 0) {
250 // Preprocessor commands are alone on their line
251 sc.SetState(SCE_C_PREPROCESSOR);
252 // Skip whitespace between # and preprocessor word
253 do {
254 sc.Forward();
255 } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
256 if (sc.ch == '\r' || sc.ch == '\n') {
257 sc.SetState(SCE_C_DEFAULT);
259 } else if (isoperator(static_cast<char>(sc.ch))) {
260 sc.SetState(SCE_C_OPERATOR);
264 if (sc.atLineEnd) {
265 // Reset states to begining of colourise so no surprises
266 // if different sets of lines lexed.
267 chPrevNonWhite = ' ';
268 visibleChars = 0;
269 lastWordWasUUID = false;
271 if (!IsASpace(sc.ch)) {
272 chPrevNonWhite = sc.ch;
273 visibleChars++;
276 sc.Complete();
279 static bool IsStreamCommentStyle(int style) {
280 return style == SCE_C_COMMENT ||
281 style == SCE_C_COMMENTDOC ||
282 style == SCE_C_COMMENTDOCKEYWORD ||
283 style == SCE_C_COMMENTDOCKEYWORDERROR;
286 // Store both the current line's fold level and the next lines in the
287 // level store to make it easy to pick up with each increment
288 // and to make it possible to fiddle the current level for "} else {".
289 static void FoldNoBoxCppDoc(unsigned int startPos, int length, int initStyle,
290 Accessor &styler) {
291 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
292 bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
293 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
294 bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
295 unsigned int endPos = startPos + length;
296 int visibleChars = 0;
297 int lineCurrent = styler.GetLine(startPos);
298 int levelCurrent = SC_FOLDLEVELBASE;
299 if (lineCurrent > 0)
300 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
301 int levelMinCurrent = levelCurrent;
302 int levelNext = levelCurrent;
303 char chNext = styler[startPos];
304 int styleNext = styler.StyleAt(startPos);
305 int style = initStyle;
306 for (unsigned int i = startPos; i < endPos; i++) {
307 char ch = chNext;
308 chNext = styler.SafeGetCharAt(i + 1);
309 int stylePrev = style;
310 style = styleNext;
311 styleNext = styler.StyleAt(i + 1);
312 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
313 if (foldComment && IsStreamCommentStyle(style)) {
314 if (!IsStreamCommentStyle(stylePrev)) {
315 levelNext++;
316 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
317 // Comments don't end at end of line and the next character may be unstyled.
318 levelNext--;
321 if (foldComment && (style == SCE_C_COMMENTLINE)) {
322 if ((ch == '/') && (chNext == '/')) {
323 char chNext2 = styler.SafeGetCharAt(i + 2);
324 if (chNext2 == '{') {
325 levelNext++;
326 } else if (chNext2 == '}') {
327 levelNext--;
331 if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
332 if (ch == '#') {
333 unsigned int j = i + 1;
334 while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
335 j++;
337 if (styler.Match(j, "region") || styler.Match(j, "if")) {
338 levelNext++;
339 } else if (styler.Match(j, "end")) {
340 levelNext--;
344 if (style == SCE_C_OPERATOR) {
345 if (ch == '{') {
346 // Measure the minimum before a '{' to allow
347 // folding on "} else {"
348 if (levelMinCurrent > levelNext) {
349 levelMinCurrent = levelNext;
351 levelNext++;
352 } else if (ch == '}') {
353 levelNext--;
356 if (atEOL) {
357 int levelUse = levelCurrent;
358 if (foldAtElse) {
359 levelUse = levelMinCurrent;
361 int lev = levelUse | levelNext << 16;
362 if (visibleChars == 0 && foldCompact)
363 lev |= SC_FOLDLEVELWHITEFLAG;
364 if (levelUse < levelNext)
365 lev |= SC_FOLDLEVELHEADERFLAG;
366 if (lev != styler.LevelAt(lineCurrent)) {
367 styler.SetLevel(lineCurrent, lev);
369 lineCurrent++;
370 levelCurrent = levelNext;
371 levelMinCurrent = levelCurrent;
372 visibleChars = 0;
374 if (!isspacechar(ch))
375 visibleChars++;
379 static void FoldCppDoc(unsigned int startPos, int length, int initStyle, WordList *[],
380 Accessor &styler) {
381 FoldNoBoxCppDoc(startPos, length, initStyle, styler);
384 static const char * const cppWordLists[] = {
385 "Primary keywords and identifiers",
386 "Secondary keywords and identifiers",
387 "Documentation comment keywords",
388 "Unused",
389 "Global classes and typedefs",
393 static void ColouriseCppDocSensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
394 Accessor &styler) {
395 ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, true);
398 static void ColouriseCppDocInsensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
399 Accessor &styler) {
400 ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, false);
403 LexerModule lmCPP(SCLEX_CPP, ColouriseCppDocSensitive, "cpp", FoldCppDoc, cppWordLists);
404 LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, ColouriseCppDocInsensitive, "cppnocase", FoldCppDoc, cppWordLists);
405 LexerModule lmTCL(SCLEX_TCL, ColouriseCppDocSensitive, "tcl", FoldCppDoc, cppWordLists);