Add an UI to enable/disable specific overlay handlers.
[TortoiseGit.git] / ext / scintilla / src / LexCPP.cxx
blobdc704ea9581916b35f152a114de1cdcf6970967f
1 // Scintilla source code edit control
2 /** @file LexCPP.cxx
3 ** Lexer for C++, C, Java, and JavaScript.
4 **/
5 // Copyright 1998-2005 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"
22 #include "CharacterSet.h"
24 #ifdef SCI_NAMESPACE
25 using namespace Scintilla;
26 #endif
28 static bool IsSpaceEquiv(int state) {
29 return (state <= SCE_C_COMMENTDOC) ||
30 // including SCE_C_DEFAULT, SCE_C_COMMENT, SCE_C_COMMENTLINE
31 (state == SCE_C_COMMENTLINEDOC) || (state == SCE_C_COMMENTDOCKEYWORD) ||
32 (state == SCE_C_COMMENTDOCKEYWORDERROR);
35 // Preconditions: sc.currentPos points to a character after '+' or '-'.
36 // The test for pos reaching 0 should be redundant,
37 // and is in only for safety measures.
38 // Limitation: this code will give the incorrect answer for code like
39 // a = b+++/ptn/...
40 // Putting a space between the '++' post-inc operator and the '+' binary op
41 // fixes this, and is highly recommended for readability anyway.
42 static bool FollowsPostfixOperator(StyleContext &sc, Accessor &styler) {
43 int pos = (int) sc.currentPos;
44 while (--pos > 0) {
45 char ch = styler[pos];
46 if (ch == '+' || ch == '-') {
47 return styler[pos - 1] == ch;
50 return false;
53 static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
54 Accessor &styler, bool caseSensitive) {
56 WordList &keywords = *keywordlists[0];
57 WordList &keywords2 = *keywordlists[1];
58 WordList &keywords3 = *keywordlists[2];
59 WordList &keywords4 = *keywordlists[3];
61 bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;
63 CharacterSet setOKBeforeRE(CharacterSet::setNone, "([{=,:;!%^&*|?~+-");
64 CharacterSet setCouldBePostOp(CharacterSet::setNone, "+-");
66 CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]");
68 CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);
69 CharacterSet setWord(CharacterSet::setAlphaNum, "._", 0x80, true);
70 if (styler.GetPropertyInt("lexer.cpp.allow.dollars", 1) != 0) {
71 setWordStart.Add('$');
72 setWord.Add('$');
75 int chPrevNonWhite = ' ';
76 int visibleChars = 0;
77 bool lastWordWasUUID = false;
78 int styleBeforeDCKeyword = SCE_C_DEFAULT;
79 bool continuationLine = false;
81 if (initStyle == SCE_C_PREPROCESSOR) {
82 // Set continuationLine if last character of previous line is '\'
83 int lineCurrent = styler.GetLine(startPos);
84 if (lineCurrent > 0) {
85 int chBack = styler.SafeGetCharAt(startPos-1, 0);
86 int chBack2 = styler.SafeGetCharAt(startPos-2, 0);
87 int lineEndChar = '!';
88 if (chBack2 == '\r' && chBack == '\n') {
89 lineEndChar = styler.SafeGetCharAt(startPos-3, 0);
90 } else if (chBack == '\n' || chBack == '\r') {
91 lineEndChar = chBack2;
93 continuationLine = lineEndChar == '\\';
97 // look back to set chPrevNonWhite properly for better regex colouring
98 if (startPos > 0) {
99 int back = startPos;
100 while (--back && IsSpaceEquiv(styler.StyleAt(back)))
102 if (styler.StyleAt(back) == SCE_C_OPERATOR) {
103 chPrevNonWhite = styler.SafeGetCharAt(back);
107 StyleContext sc(startPos, length, initStyle, styler);
109 for (; sc.More(); sc.Forward()) {
111 if (sc.atLineStart) {
112 if (sc.state == SCE_C_STRING) {
113 // Prevent SCE_C_STRINGEOL from leaking back to previous line which
114 // ends with a line continuation by locking in the state upto this position.
115 sc.SetState(SCE_C_STRING);
117 // Reset states to begining of colourise so no surprises
118 // if different sets of lines lexed.
119 visibleChars = 0;
120 lastWordWasUUID = false;
123 // Handle line continuation generically.
124 if (sc.ch == '\\') {
125 if (sc.chNext == '\n' || sc.chNext == '\r') {
126 sc.Forward();
127 if (sc.ch == '\r' && sc.chNext == '\n') {
128 sc.Forward();
130 continuationLine = true;
131 continue;
135 // Determine if the current state should terminate.
136 switch (sc.state) {
137 case SCE_C_OPERATOR:
138 sc.SetState(SCE_C_DEFAULT);
139 break;
140 case SCE_C_NUMBER:
141 // We accept almost anything because of hex. and number suffixes
142 if (!setWord.Contains(sc.ch)) {
143 sc.SetState(SCE_C_DEFAULT);
145 break;
146 case SCE_C_IDENTIFIER:
147 if (!setWord.Contains(sc.ch) || (sc.ch == '.')) {
148 char s[1000];
149 if (caseSensitive) {
150 sc.GetCurrent(s, sizeof(s));
151 } else {
152 sc.GetCurrentLowered(s, sizeof(s));
154 if (keywords.InList(s)) {
155 lastWordWasUUID = strcmp(s, "uuid") == 0;
156 sc.ChangeState(SCE_C_WORD);
157 } else if (keywords2.InList(s)) {
158 sc.ChangeState(SCE_C_WORD2);
159 } else if (keywords4.InList(s)) {
160 sc.ChangeState(SCE_C_GLOBALCLASS);
162 sc.SetState(SCE_C_DEFAULT);
164 break;
165 case SCE_C_PREPROCESSOR:
166 if (sc.atLineStart && !continuationLine) {
167 sc.SetState(SCE_C_DEFAULT);
168 } else if (stylingWithinPreprocessor) {
169 if (IsASpace(sc.ch)) {
170 sc.SetState(SCE_C_DEFAULT);
172 } else {
173 if (sc.Match('/', '*') || sc.Match('/', '/')) {
174 sc.SetState(SCE_C_DEFAULT);
177 break;
178 case SCE_C_COMMENT:
179 if (sc.Match('*', '/')) {
180 sc.Forward();
181 sc.ForwardSetState(SCE_C_DEFAULT);
183 break;
184 case SCE_C_COMMENTDOC:
185 if (sc.Match('*', '/')) {
186 sc.Forward();
187 sc.ForwardSetState(SCE_C_DEFAULT);
188 } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
189 // Verify that we have the conditions to mark a comment-doc-keyword
190 if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
191 styleBeforeDCKeyword = SCE_C_COMMENTDOC;
192 sc.SetState(SCE_C_COMMENTDOCKEYWORD);
195 break;
196 case SCE_C_COMMENTLINE:
197 if (sc.atLineStart) {
198 sc.SetState(SCE_C_DEFAULT);
200 break;
201 case SCE_C_COMMENTLINEDOC:
202 if (sc.atLineStart) {
203 sc.SetState(SCE_C_DEFAULT);
204 } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
205 // Verify that we have the conditions to mark a comment-doc-keyword
206 if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) {
207 styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC;
208 sc.SetState(SCE_C_COMMENTDOCKEYWORD);
211 break;
212 case SCE_C_COMMENTDOCKEYWORD:
213 if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) {
214 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
215 sc.Forward();
216 sc.ForwardSetState(SCE_C_DEFAULT);
217 } else if (!setDoxygen.Contains(sc.ch)) {
218 char s[100];
219 if (caseSensitive) {
220 sc.GetCurrent(s, sizeof(s));
221 } else {
222 sc.GetCurrentLowered(s, sizeof(s));
224 if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {
225 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
227 sc.SetState(styleBeforeDCKeyword);
229 break;
230 case SCE_C_STRING:
231 if (sc.atLineEnd) {
232 sc.ChangeState(SCE_C_STRINGEOL);
233 } else if (sc.ch == '\\') {
234 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
235 sc.Forward();
237 } else if (sc.ch == '\"') {
238 sc.ForwardSetState(SCE_C_DEFAULT);
240 break;
241 case SCE_C_CHARACTER:
242 if (sc.atLineEnd) {
243 sc.ChangeState(SCE_C_STRINGEOL);
244 } else if (sc.ch == '\\') {
245 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
246 sc.Forward();
248 } else if (sc.ch == '\'') {
249 sc.ForwardSetState(SCE_C_DEFAULT);
251 break;
252 case SCE_C_REGEX:
253 if (sc.atLineStart) {
254 sc.SetState(SCE_C_DEFAULT);
255 } else if (sc.ch == '/') {
256 sc.Forward();
257 while ((sc.ch < 0x80) && islower(sc.ch))
258 sc.Forward(); // gobble regex flags
259 sc.SetState(SCE_C_DEFAULT);
260 } else if (sc.ch == '\\') {
261 // Gobble up the quoted character
262 if (sc.chNext == '\\' || sc.chNext == '/') {
263 sc.Forward();
266 break;
267 case SCE_C_STRINGEOL:
268 if (sc.atLineStart) {
269 sc.SetState(SCE_C_DEFAULT);
271 break;
272 case SCE_C_VERBATIM:
273 if (sc.ch == '\"') {
274 if (sc.chNext == '\"') {
275 sc.Forward();
276 } else {
277 sc.ForwardSetState(SCE_C_DEFAULT);
280 break;
281 case SCE_C_UUID:
282 if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {
283 sc.SetState(SCE_C_DEFAULT);
287 // Determine if a new state should be entered.
288 if (sc.state == SCE_C_DEFAULT) {
289 if (sc.Match('@', '\"')) {
290 sc.SetState(SCE_C_VERBATIM);
291 sc.Forward();
292 } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
293 if (lastWordWasUUID) {
294 sc.SetState(SCE_C_UUID);
295 lastWordWasUUID = false;
296 } else {
297 sc.SetState(SCE_C_NUMBER);
299 } else if (setWordStart.Contains(sc.ch) || (sc.ch == '@')) {
300 if (lastWordWasUUID) {
301 sc.SetState(SCE_C_UUID);
302 lastWordWasUUID = false;
303 } else {
304 sc.SetState(SCE_C_IDENTIFIER);
306 } else if (sc.Match('/', '*')) {
307 if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
308 sc.SetState(SCE_C_COMMENTDOC);
309 } else {
310 sc.SetState(SCE_C_COMMENT);
312 sc.Forward(); // Eat the * so it isn't used for the end of the comment
313 } else if (sc.Match('/', '/')) {
314 if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!"))
315 // Support of Qt/Doxygen doc. style
316 sc.SetState(SCE_C_COMMENTLINEDOC);
317 else
318 sc.SetState(SCE_C_COMMENTLINE);
319 } else if (sc.ch == '/' && setOKBeforeRE.Contains(chPrevNonWhite) &&
320 (!setCouldBePostOp.Contains(chPrevNonWhite) || !FollowsPostfixOperator(sc, styler))) {
321 sc.SetState(SCE_C_REGEX); // JavaScript's RegEx
322 } else if (sc.ch == '\"') {
323 sc.SetState(SCE_C_STRING);
324 } else if (sc.ch == '\'') {
325 sc.SetState(SCE_C_CHARACTER);
326 } else if (sc.ch == '#' && visibleChars == 0) {
327 // Preprocessor commands are alone on their line
328 sc.SetState(SCE_C_PREPROCESSOR);
329 // Skip whitespace between # and preprocessor word
330 do {
331 sc.Forward();
332 } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
333 if (sc.atLineEnd) {
334 sc.SetState(SCE_C_DEFAULT);
336 } else if (isoperator(static_cast<char>(sc.ch))) {
337 sc.SetState(SCE_C_OPERATOR);
341 if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) {
342 chPrevNonWhite = sc.ch;
343 visibleChars++;
345 continuationLine = false;
347 sc.Complete();
350 static bool IsStreamCommentStyle(int style) {
351 return style == SCE_C_COMMENT ||
352 style == SCE_C_COMMENTDOC ||
353 style == SCE_C_COMMENTDOCKEYWORD ||
354 style == SCE_C_COMMENTDOCKEYWORDERROR;
357 // Store both the current line's fold level and the next lines in the
358 // level store to make it easy to pick up with each increment
359 // and to make it possible to fiddle the current level for "} else {".
360 static void FoldCppDoc(unsigned int startPos, int length, int initStyle,
361 WordList *[], Accessor &styler) {
362 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
363 bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
364 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
365 bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
366 unsigned int endPos = startPos + length;
367 int visibleChars = 0;
368 int lineCurrent = styler.GetLine(startPos);
369 int levelCurrent = SC_FOLDLEVELBASE;
370 if (lineCurrent > 0)
371 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
372 int levelMinCurrent = levelCurrent;
373 int levelNext = levelCurrent;
374 char chNext = styler[startPos];
375 int styleNext = styler.StyleAt(startPos);
376 int style = initStyle;
377 for (unsigned int i = startPos; i < endPos; i++) {
378 char ch = chNext;
379 chNext = styler.SafeGetCharAt(i + 1);
380 int stylePrev = style;
381 style = styleNext;
382 styleNext = styler.StyleAt(i + 1);
383 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
384 if (foldComment && IsStreamCommentStyle(style)) {
385 if (!IsStreamCommentStyle(stylePrev) && (stylePrev != SCE_C_COMMENTLINEDOC)) {
386 levelNext++;
387 } else if (!IsStreamCommentStyle(styleNext) && (styleNext != SCE_C_COMMENTLINEDOC) && !atEOL) {
388 // Comments don't end at end of line and the next character may be unstyled.
389 levelNext--;
392 if (foldComment && (style == SCE_C_COMMENTLINE)) {
393 if ((ch == '/') && (chNext == '/')) {
394 char chNext2 = styler.SafeGetCharAt(i + 2);
395 if (chNext2 == '{') {
396 levelNext++;
397 } else if (chNext2 == '}') {
398 levelNext--;
402 if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
403 if (ch == '#') {
404 unsigned int j = i + 1;
405 while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
406 j++;
408 if (styler.Match(j, "region") || styler.Match(j, "if")) {
409 levelNext++;
410 } else if (styler.Match(j, "end")) {
411 levelNext--;
415 if (style == SCE_C_OPERATOR) {
416 if (ch == '{') {
417 // Measure the minimum before a '{' to allow
418 // folding on "} else {"
419 if (levelMinCurrent > levelNext) {
420 levelMinCurrent = levelNext;
422 levelNext++;
423 } else if (ch == '}') {
424 levelNext--;
427 if (!IsASpace(ch))
428 visibleChars++;
429 if (atEOL || (i == endPos-1)) {
430 int levelUse = levelCurrent;
431 if (foldAtElse) {
432 levelUse = levelMinCurrent;
434 int lev = levelUse | levelNext << 16;
435 if (visibleChars == 0 && foldCompact)
436 lev |= SC_FOLDLEVELWHITEFLAG;
437 if (levelUse < levelNext)
438 lev |= SC_FOLDLEVELHEADERFLAG;
439 if (lev != styler.LevelAt(lineCurrent)) {
440 styler.SetLevel(lineCurrent, lev);
442 lineCurrent++;
443 levelCurrent = levelNext;
444 levelMinCurrent = levelCurrent;
445 visibleChars = 0;
450 static const char * const cppWordLists[] = {
451 "Primary keywords and identifiers",
452 "Secondary keywords and identifiers",
453 "Documentation comment keywords",
454 "Unused",
455 "Global classes and typedefs",
459 static void ColouriseCppDocSensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
460 Accessor &styler) {
461 ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, true);
464 static void ColouriseCppDocInsensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
465 Accessor &styler) {
466 ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, false);
469 LexerModule lmCPP(SCLEX_CPP, ColouriseCppDocSensitive, "cpp", FoldCppDoc, cppWordLists);
470 LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, ColouriseCppDocInsensitive, "cppnocase", FoldCppDoc, cppWordLists);