]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexCPP.cxx
c1bb1ff9a30bace3c31ef25db5c558d9bee2b370
[wxWidgets.git] / src / stc / scintilla / src / LexCPP.cxx
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.
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13
14 #include "Platform.h"
15
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
23 static bool IsOKBeforeRE(const int ch) {
24 return (ch == '(') || (ch == '=') || (ch == ',');
25 }
26
27 static inline bool IsAWordChar(const int ch) {
28 return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
29 }
30
31 static inline bool IsAWordStart(const int ch) {
32 return (ch < 0x80) && (isalnum(ch) || ch == '_');
33 }
34
35 static inline bool IsADoxygenChar(const int ch) {
36 return (islower(ch) || ch == '$' || ch == '@' ||
37 ch == '\\' || ch == '&' || ch == '<' ||
38 ch == '>' || ch == '#' || ch == '{' ||
39 ch == '}' || ch == '[' || ch == ']');
40 }
41
42 static inline bool IsStateComment(const int state) {
43 return ((state == SCE_C_COMMENT) ||
44 (state == SCE_C_COMMENTLINE) ||
45 (state == SCE_C_COMMENTDOC) ||
46 (state == SCE_C_COMMENTDOCKEYWORD) ||
47 (state == SCE_C_COMMENTDOCKEYWORDERROR));
48 }
49
50 static inline bool IsStateString(const int state) {
51 return ((state == SCE_C_STRING) || (state == SCE_C_VERBATIM));
52 }
53
54 static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
55 Accessor &styler) {
56
57 WordList &keywords = *keywordlists[0];
58 WordList &keywords2 = *keywordlists[1];
59 WordList &keywords3 = *keywordlists[2];
60
61 bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;
62
63 // Do not leak onto next line
64 if (initStyle == SCE_C_STRINGEOL)
65 initStyle = SCE_C_DEFAULT;
66
67 int chPrevNonWhite = ' ';
68 int visibleChars = 0;
69 bool lastWordWasUUID = false;
70
71 StyleContext sc(startPos, length, initStyle, styler);
72
73 for (; sc.More(); sc.Forward()) {
74
75 if (sc.atLineStart && (sc.state == SCE_C_STRING)) {
76 // Prevent SCE_C_STRINGEOL from leaking back to previous line
77 sc.SetState(SCE_C_STRING);
78 }
79
80 // Handle line continuation generically.
81 if (sc.ch == '\\') {
82 if (sc.chNext == '\n' || sc.chNext == '\r') {
83 sc.Forward();
84 if (sc.ch == '\r' && sc.chNext == '\n') {
85 sc.Forward();
86 }
87 continue;
88 }
89 }
90
91 // Determine if the current state should terminate.
92 if (sc.state == SCE_C_OPERATOR) {
93 sc.SetState(SCE_C_DEFAULT);
94 } else if (sc.state == SCE_C_NUMBER) {
95 if (!IsAWordChar(sc.ch)) {
96 sc.SetState(SCE_C_DEFAULT);
97 }
98 } else if (sc.state == SCE_C_IDENTIFIER) {
99 if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
100 char s[100];
101 sc.GetCurrent(s, sizeof(s));
102 if (keywords.InList(s)) {
103 lastWordWasUUID = strcmp(s, "uuid") == 0;
104 sc.ChangeState(SCE_C_WORD);
105 } else if (keywords2.InList(s)) {
106 sc.ChangeState(SCE_C_WORD2);
107 }
108 sc.SetState(SCE_C_DEFAULT);
109 }
110 } else if (sc.state == SCE_C_PREPROCESSOR) {
111 if (stylingWithinPreprocessor) {
112 if (IsASpace(sc.ch)) {
113 sc.SetState(SCE_C_DEFAULT);
114 }
115 } else {
116 if (sc.atLineEnd) {
117 sc.SetState(SCE_C_DEFAULT);
118 }
119 }
120 } else if (sc.state == SCE_C_COMMENT) {
121 if (sc.Match('*', '/')) {
122 sc.Forward();
123 sc.ForwardSetState(SCE_C_DEFAULT);
124 }
125 } else if (sc.state == SCE_C_COMMENTDOC) {
126 if (sc.Match('*', '/')) {
127 sc.Forward();
128 sc.ForwardSetState(SCE_C_DEFAULT);
129 } else if (sc.ch == '@' || sc.ch == '\\') {
130 sc.SetState(SCE_C_COMMENTDOCKEYWORD);
131 }
132 } else if (sc.state == SCE_C_COMMENTLINE || sc.state == SCE_C_COMMENTLINEDOC) {
133 if (sc.atLineEnd) {
134 sc.SetState(SCE_C_DEFAULT);
135 visibleChars = 0;
136 }
137 } else if (sc.state == SCE_C_COMMENTDOCKEYWORD) {
138 if (sc.Match('*', '/')) {
139 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
140 sc.Forward();
141 sc.ForwardSetState(SCE_C_DEFAULT);
142 } else if (!IsADoxygenChar(sc.ch)) {
143 char s[100];
144 sc.GetCurrent(s, sizeof(s));
145 if (!isspace(sc.ch) || !keywords3.InList(s+1)) {
146 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
147 }
148 sc.SetState(SCE_C_COMMENTDOC);
149 }
150 } else if (sc.state == SCE_C_STRING) {
151 if (sc.ch == '\\') {
152 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
153 sc.Forward();
154 }
155 } else if (sc.ch == '\"') {
156 sc.ForwardSetState(SCE_C_DEFAULT);
157 } else if (sc.atLineEnd) {
158 sc.ChangeState(SCE_C_STRINGEOL);
159 sc.ForwardSetState(SCE_C_DEFAULT);
160 visibleChars = 0;
161 }
162 } else if (sc.state == SCE_C_CHARACTER) {
163 if (sc.atLineEnd) {
164 sc.ChangeState(SCE_C_STRINGEOL);
165 sc.ForwardSetState(SCE_C_DEFAULT);
166 visibleChars = 0;
167 } else if (sc.ch == '\\') {
168 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
169 sc.Forward();
170 }
171 } else if (sc.ch == '\'') {
172 sc.ForwardSetState(SCE_C_DEFAULT);
173 }
174 } else if (sc.state == SCE_C_REGEX) {
175 if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == '/') {
176 sc.ForwardSetState(SCE_C_DEFAULT);
177 } else if (sc.ch == '\\') {
178 // Gobble up the quoted character
179 if (sc.chNext == '\\' || sc.chNext == '/') {
180 sc.Forward();
181 }
182 }
183 } else if (sc.state == SCE_C_VERBATIM) {
184 if (sc.ch == '\"') {
185 if (sc.chNext == '\"') {
186 sc.Forward();
187 } else {
188 sc.ForwardSetState(SCE_C_DEFAULT);
189 }
190 }
191 } else if (sc.state == SCE_C_UUID) {
192 if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {
193 sc.SetState(SCE_C_DEFAULT);
194 }
195 }
196
197 // Determine if a new state should be entered.
198 if (sc.state == SCE_C_DEFAULT) {
199 if (sc.Match('@', '\"')) {
200 sc.SetState(SCE_C_VERBATIM);
201 sc.Forward();
202 } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
203 if (lastWordWasUUID) {
204 sc.SetState(SCE_C_UUID);
205 lastWordWasUUID = false;
206 } else {
207 sc.SetState(SCE_C_NUMBER);
208 }
209 } else if (IsAWordStart(sc.ch) || (sc.ch == '@')) {
210 if (lastWordWasUUID) {
211 sc.SetState(SCE_C_UUID);
212 lastWordWasUUID = false;
213 } else {
214 sc.SetState(SCE_C_IDENTIFIER);
215 }
216 } else if (sc.Match('/', '*')) {
217 if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
218 sc.SetState(SCE_C_COMMENTDOC);
219 } else {
220 sc.SetState(SCE_C_COMMENT);
221 }
222 sc.Forward(); // Eat the * so it isn't used for the end of the comment
223 } else if (sc.Match('/', '/')) {
224 if (sc.Match("///") || sc.Match("//!")) // Support of Qt/Doxygen doc. style
225 sc.SetState(SCE_C_COMMENTLINEDOC);
226 else
227 sc.SetState(SCE_C_COMMENTLINE);
228 } else if (sc.ch == '/' && IsOKBeforeRE(chPrevNonWhite)) {
229 sc.SetState(SCE_C_REGEX);
230 } else if (sc.ch == '\"') {
231 sc.SetState(SCE_C_STRING);
232 } else if (sc.ch == '\'') {
233 sc.SetState(SCE_C_CHARACTER);
234 } else if (sc.ch == '#' && visibleChars == 0) {
235 // Preprocessor commands are alone on their line
236 sc.SetState(SCE_C_PREPROCESSOR);
237 // Skip whitespace between # and preprocessor word
238 do {
239 sc.Forward();
240 } while ((sc.ch == ' ') && (sc.ch == '\t') && sc.More());
241 if (sc.atLineEnd) {
242 sc.SetState(SCE_C_DEFAULT);
243 }
244 } else if (isoperator(static_cast<char>(sc.ch))) {
245 sc.SetState(SCE_C_OPERATOR);
246 }
247 }
248
249 if (sc.atLineEnd) {
250 // Reset states to begining of colourise so no surprises
251 // if different sets of lines lexed.
252 chPrevNonWhite = ' ';
253 visibleChars = 0;
254 lastWordWasUUID = false;
255 }
256 if (!IsASpace(sc.ch)) {
257 chPrevNonWhite = sc.ch;
258 visibleChars++;
259 }
260 }
261 sc.Complete();
262 }
263
264 static bool IsStreamCommentStyle(int style) {
265 return style == SCE_C_COMMENT ||
266 style == SCE_C_COMMENTDOC ||
267 style == SCE_C_COMMENTDOCKEYWORD ||
268 style == SCE_C_COMMENTDOCKEYWORDERROR;
269 }
270
271 static void FoldCppDoc(unsigned int startPos, int length, int initStyle, WordList *[],
272 Accessor &styler) {
273 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
274 bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
275 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
276 unsigned int endPos = startPos + length;
277 int visibleChars = 0;
278 int lineCurrent = styler.GetLine(startPos);
279 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
280 int levelCurrent = levelPrev;
281 char chNext = styler[startPos];
282 int styleNext = styler.StyleAt(startPos);
283 int style = initStyle;
284 for (unsigned int i = startPos; i < endPos; i++) {
285 char ch = chNext;
286 chNext = styler.SafeGetCharAt(i + 1);
287 int stylePrev = style;
288 style = styleNext;
289 styleNext = styler.StyleAt(i + 1);
290 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
291 if (foldComment && IsStreamCommentStyle(style)) {
292 if (!IsStreamCommentStyle(stylePrev)) {
293 levelCurrent++;
294 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
295 // Comments don't end at end of line and the next character may be unstyled.
296 levelCurrent--;
297 }
298 }
299 if (foldComment && (style == SCE_C_COMMENTLINE)) {
300 if ((ch == '/') && (chNext == '/')) {
301 char chNext2 = styler.SafeGetCharAt(i + 2);
302 if (chNext2 == '{') {
303 levelCurrent++;
304 } else if (chNext2 == '}') {
305 levelCurrent--;
306 }
307 }
308 }
309 if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
310 if (ch == '#') {
311 unsigned int j=i+1;
312 while ((j<endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
313 j++;
314 }
315 if (styler.Match(j, "region") || styler.Match(j, "if")) {
316 levelCurrent++;
317 } else if (styler.Match(j, "end")) {
318 levelCurrent--;
319 }
320 }
321 }
322 if (style == SCE_C_OPERATOR) {
323 if (ch == '{') {
324 levelCurrent++;
325 } else if (ch == '}') {
326 levelCurrent--;
327 }
328 }
329 if (atEOL) {
330 int lev = levelPrev;
331 if (visibleChars == 0 && foldCompact)
332 lev |= SC_FOLDLEVELWHITEFLAG;
333 if ((levelCurrent > levelPrev) && (visibleChars > 0))
334 lev |= SC_FOLDLEVELHEADERFLAG;
335 if (lev != styler.LevelAt(lineCurrent)) {
336 styler.SetLevel(lineCurrent, lev);
337 }
338 lineCurrent++;
339 levelPrev = levelCurrent;
340 visibleChars = 0;
341 }
342 if (!isspacechar(ch))
343 visibleChars++;
344 }
345 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
346 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
347 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
348 }
349
350 static const char * const cppWordLists[] = {
351 "Primary keywords and identifiers",
352 "Secondary keywords and identifiers",
353 "Documentation comment keywords",
354 0,
355 };
356
357 LexerModule lmCPP(SCLEX_CPP, ColouriseCppDoc, "cpp", FoldCppDoc, cppWordLists);
358 LexerModule lmTCL(SCLEX_TCL, ColouriseCppDoc, "tcl", FoldCppDoc, cppWordLists);