]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexCPP.cxx
19aa329bafdc2cf76f762de6be183260f54e8d54
[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-2001 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 "KeyWords.h"
19 #include "Scintilla.h"
20 #include "SciLexer.h"
21
22 static bool IsOKBeforeRE(int ch) {
23 return (ch == '(') || (ch == '=') || (ch == ',');
24 }
25
26 static void getRange(unsigned int start,
27 unsigned int end,
28 Accessor &styler,
29 char *s,
30 unsigned int len) {
31 unsigned int i = 0;
32 while ((i < end - start + 1) && (i < len-1)) {
33 s[i] = styler[start + i];
34 i++;
35 }
36 s[i] = '\0';
37 }
38
39 inline bool IsASpace(int ch) {
40 return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));
41 }
42
43 inline bool IsAWordChar(int ch) {
44 return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
45 }
46
47 inline bool IsAWordStart(int ch) {
48 return (ch < 0x80) && (isalnum(ch) || ch == '_');
49 }
50
51 inline bool IsADigit(int ch) {
52 return (ch >= '0') && (ch <= '9');
53 }
54
55 // All languages handled so far can treat all characters >= 0x80 as one class
56 // which just continues the current token or starts an identifier if in default.
57 // DBCS treated specially as the second character can be < 0x80 and hence
58 // syntactically significant. UTF-8 avoids this as all trail bytes are >= 0x80
59 class ColouriseContext {
60 Accessor &styler;
61 int lengthDoc;
62 int currentPos;
63 ColouriseContext& operator=(const ColouriseContext&) {
64 return *this;
65 }
66 public:
67 bool atEOL;
68 int state;
69 int chPrev;
70 int ch;
71 int chNext;
72
73 ColouriseContext(unsigned int startPos, int length,
74 int initStyle, Accessor &styler_) :
75 styler(styler_),
76 lengthDoc(startPos + length),
77 currentPos(startPos),
78 atEOL(false),
79 state(initStyle),
80 chPrev(0),
81 ch(0),
82 chNext(0) {
83 styler.StartAt(startPos);
84 styler.StartSegment(startPos);
85 int pos = currentPos;
86 ch = static_cast<unsigned char>(styler.SafeGetCharAt(pos));
87 if (styler.IsLeadByte(static_cast<char>(ch))) {
88 pos++;
89 ch = ch << 8;
90 ch |= static_cast<unsigned char>(styler.SafeGetCharAt(pos));
91 }
92 chNext = static_cast<unsigned char>(styler.SafeGetCharAt(pos+1));
93 if (styler.IsLeadByte(static_cast<char>(chNext))) {
94 chNext = chNext << 8;
95 chNext |= static_cast<unsigned char>(styler.SafeGetCharAt(pos+2));
96 }
97 atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
98 }
99 void Complete() {
100 styler.ColourTo(currentPos - 1, state);
101 }
102 bool More() {
103 return currentPos <= lengthDoc;
104 }
105 void Forward() {
106 // A lot of this is repeated from the constructor - TODO: merge code
107 chPrev = ch;
108 currentPos++;
109 if (ch >= 0x100)
110 currentPos++;
111 ch = chNext;
112 chNext = static_cast<unsigned char>(styler.SafeGetCharAt(currentPos+1));
113 if (styler.IsLeadByte(static_cast<char>(chNext))) {
114 chNext = chNext << 8;
115 chNext |= static_cast<unsigned char>(styler.SafeGetCharAt(currentPos + 2));
116 }
117 // Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix)
118 // Avoid triggering two times on Dos/Win
119 // End of line
120 atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
121 }
122 void ChangeState(int state_) {
123 state = state_;
124 }
125 void SetState(int state_) {
126 styler.ColourTo(currentPos - 1, state);
127 state = state_;
128 }
129 void ForwardSetState(int state_) {
130 Forward();
131 styler.ColourTo(currentPos - 1, state);
132 state = state_;
133 }
134 void GetCurrent(char *s, int len) {
135 getRange(styler.GetStartSegment(), currentPos - 1, styler, s, len);
136 }
137 int LengthCurrent() {
138 return currentPos - styler.GetStartSegment();
139 }
140 bool Match(char ch0) {
141 return ch == ch0;
142 }
143 bool Match(char ch0, char ch1) {
144 return (ch == ch0) && (chNext == ch1);
145 }
146 bool Match(const char *s) {
147 if (ch != *s)
148 return false;
149 s++;
150 if (chNext != *s)
151 return false;
152 s++;
153 for (int n=2; *s; n++) {
154 if (*s != styler.SafeGetCharAt(currentPos+n))
155 return false;
156 s++;
157 }
158 return true;
159 }
160 };
161
162 static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
163 Accessor &styler) {
164
165 WordList &keywords = *keywordlists[0];
166 WordList &keywords2 = *keywordlists[1];
167
168 bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor");
169
170 if (initStyle == SCE_C_STRINGEOL) // Does not leak onto next line
171 initStyle = SCE_C_DEFAULT;
172
173 int chPrevNonWhite = ' ';
174 int visibleChars = 0;
175 bool lastWordWasUUID = false;
176
177 ColouriseContext cc(startPos, length, initStyle, styler);
178
179 for (; cc.More(); cc.Forward()) {
180
181 if (cc.state == SCE_C_STRINGEOL) {
182 if (cc.atEOL) {
183 cc.SetState(SCE_C_DEFAULT);
184 }
185 } else if (cc.state == SCE_C_OPERATOR) {
186 cc.SetState(SCE_C_DEFAULT);
187 } else if (cc.state == SCE_C_NUMBER) {
188 if (!IsAWordChar(cc.ch)) {
189 cc.SetState(SCE_C_DEFAULT);
190 }
191 } else if (cc.state == SCE_C_IDENTIFIER) {
192 if (!IsAWordChar(cc.ch) || (cc.ch == '.')) {
193 char s[100];
194 cc.GetCurrent(s, sizeof(s));
195 if (keywords.InList(s)) {
196 lastWordWasUUID = strcmp(s, "uuid") == 0;
197 cc.ChangeState(SCE_C_WORD);
198 } else if (keywords2.InList(s)) {
199 cc.ChangeState(SCE_C_WORD2);
200 }
201 cc.SetState(SCE_C_DEFAULT);
202 }
203 } if (cc.state == SCE_C_PREPROCESSOR) {
204 if (stylingWithinPreprocessor) {
205 if (IsASpace(cc.ch)) {
206 cc.SetState(SCE_C_DEFAULT);
207 }
208 } else {
209 if (cc.atEOL && (cc.chPrev != '\\')) {
210 cc.SetState(SCE_C_DEFAULT);
211 }
212 }
213 } else if (cc.state == SCE_C_COMMENT) {
214 if (cc.Match('*', '/')) {
215 cc.Forward();
216 cc.ForwardSetState(SCE_C_DEFAULT);
217 }
218 } else if (cc.state == SCE_C_COMMENTDOC) {
219 if (cc.Match('*', '/')) {
220 cc.Forward();
221 cc.ForwardSetState(SCE_C_DEFAULT);
222 }
223 } else if (cc.state == SCE_C_COMMENTLINE || cc.state == SCE_C_COMMENTLINEDOC) {
224 if (cc.ch == '\r' || cc.ch == '\n') {
225 cc.SetState(SCE_C_DEFAULT);
226 }
227 } else if (cc.state == SCE_C_STRING) {
228 if (cc.ch == '\\') {
229 if (cc.chNext == '\"' || cc.chNext == '\'' || cc.chNext == '\\') {
230 cc.Forward();
231 }
232 } else if (cc.ch == '\"') {
233 cc.ForwardSetState(SCE_C_DEFAULT);
234 } else if ((cc.atEOL) && (cc.chPrev != '\\')) {
235 cc.ChangeState(SCE_C_STRINGEOL);
236 }
237 } else if (cc.state == SCE_C_CHARACTER) {
238 if ((cc.ch == '\r' || cc.ch == '\n') && (cc.chPrev != '\\')) {
239 cc.ChangeState(SCE_C_STRINGEOL);
240 } else if (cc.ch == '\\') {
241 if (cc.chNext == '\"' || cc.chNext == '\'' || cc.chNext == '\\') {
242 cc.Forward();
243 }
244 } else if (cc.ch == '\'') {
245 cc.ForwardSetState(SCE_C_DEFAULT);
246 }
247 } else if (cc.state == SCE_C_REGEX) {
248 if (cc.ch == '\r' || cc.ch == '\n' || cc.ch == '/') {
249 cc.ForwardSetState(SCE_C_DEFAULT);
250 } else if (cc.ch == '\\') {
251 // Gobble up the quoted character
252 if (cc.chNext == '\\' || cc.chNext == '/') {
253 cc.Forward();
254 }
255 }
256 } else if (cc.state == SCE_C_VERBATIM) {
257 if (cc.ch == '\"') {
258 if (cc.chNext == '\"') {
259 cc.Forward();
260 } else {
261 cc.ForwardSetState(SCE_C_DEFAULT);
262 }
263 }
264 } else if (cc.state == SCE_C_UUID) {
265 if (cc.ch == '\r' || cc.ch == '\n' || cc.ch == ')') {
266 cc.SetState(SCE_C_DEFAULT);
267 }
268 }
269
270 if (cc.state == SCE_C_DEFAULT) {
271 if (cc.Match('@', '\"')) {
272 cc.SetState(SCE_C_VERBATIM);
273 cc.Forward();
274 } else if (IsADigit(cc.ch) || (cc.ch == '.' && IsADigit(cc.chNext))) {
275 if (lastWordWasUUID) {
276 cc.SetState(SCE_C_UUID);
277 lastWordWasUUID = false;
278 } else {
279 cc.SetState(SCE_C_NUMBER);
280 }
281 } else if (IsAWordStart(cc.ch) || (cc.ch == '@')) {
282 if (lastWordWasUUID) {
283 cc.SetState(SCE_C_UUID);
284 lastWordWasUUID = false;
285 } else {
286 cc.SetState(SCE_C_IDENTIFIER);
287 }
288 } else if (cc.Match('/', '*')) {
289 if (cc.Match("/**") || cc.Match("/*!")) // Support of Qt/Doxygen doc. style
290 cc.SetState(SCE_C_COMMENTDOC);
291 else
292 cc.SetState(SCE_C_COMMENT);
293 cc.Forward(); // Eat the * so it isn't used for the end of the comment
294 } else if (cc.Match('/', '/')) {
295 if (cc.Match("///") || cc.Match("//!")) // Support of Qt/Doxygen doc. style
296 cc.SetState(SCE_C_COMMENTLINEDOC);
297 else
298 cc.SetState(SCE_C_COMMENTLINE);
299 } else if (cc.ch == '/' && IsOKBeforeRE(chPrevNonWhite)) {
300 cc.SetState(SCE_C_REGEX);
301 } else if (cc.ch == '\"') {
302 cc.SetState(SCE_C_STRING);
303 } else if (cc.ch == '\'') {
304 cc.SetState(SCE_C_CHARACTER);
305 } else if (cc.ch == '#' && visibleChars == 0) {
306 // Preprocessor commands are alone on their line
307 cc.SetState(SCE_C_PREPROCESSOR);
308 // Skip whitespace between # and preprocessor word
309 do {
310 cc.Forward();
311 } while (IsASpace(cc.ch) && cc.More());
312 } else if (isoperator(static_cast<char>(cc.ch))) {
313 cc.SetState(SCE_C_OPERATOR);
314 }
315 }
316 if (cc.atEOL) {
317 // Reset states to begining of colourise so no surprises
318 // if different sets of lines lexed.
319 chPrevNonWhite = ' ';
320 visibleChars = 0;
321 lastWordWasUUID = false;
322 }
323 if (!IsASpace(cc.ch)) {
324 chPrevNonWhite = cc.ch;
325 visibleChars++;
326 }
327 }
328 cc.Complete();
329 }
330
331 static void FoldCppDoc(unsigned int startPos, int length, int initStyle, WordList *[],
332 Accessor &styler) {
333 bool foldComment = styler.GetPropertyInt("fold.comment");
334 bool foldCompact = styler.GetPropertyInt("fold.compact", 1);
335 unsigned int endPos = startPos + length;
336 int visibleChars = 0;
337 int lineCurrent = styler.GetLine(startPos);
338 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
339 int levelCurrent = levelPrev;
340 char chNext = styler[startPos];
341 int styleNext = styler.StyleAt(startPos);
342 int style = initStyle;
343 for (unsigned int i = startPos; i < endPos; i++) {
344 char ch = chNext;
345 chNext = styler.SafeGetCharAt(i + 1);
346 int stylePrev = style;
347 style = styleNext;
348 styleNext = styler.StyleAt(i + 1);
349 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
350 if (foldComment &&
351 (style == SCE_C_COMMENT || style == SCE_C_COMMENTDOC)) {
352 if (style != stylePrev) {
353 levelCurrent++;
354 } else if ((style != styleNext) && !atEOL) {
355 // Comments don't end at end of line and the next character may be unstyled.
356 levelCurrent--;
357 }
358 }
359 if (style == SCE_C_OPERATOR) {
360 if (ch == '{') {
361 levelCurrent++;
362 } else if (ch == '}') {
363 levelCurrent--;
364 }
365 }
366 if (atEOL) {
367 int lev = levelPrev;
368 if (visibleChars == 0 && foldCompact)
369 lev |= SC_FOLDLEVELWHITEFLAG;
370 if ((levelCurrent > levelPrev) && (visibleChars > 0))
371 lev |= SC_FOLDLEVELHEADERFLAG;
372 if (lev != styler.LevelAt(lineCurrent)) {
373 styler.SetLevel(lineCurrent, lev);
374 }
375 lineCurrent++;
376 levelPrev = levelCurrent;
377 visibleChars = 0;
378 }
379 if (!isspacechar(ch))
380 visibleChars++;
381 }
382 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
383 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
384 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
385 }
386
387 LexerModule lmCPP(SCLEX_CPP, ColouriseCppDoc, "cpp", FoldCppDoc);
388 LexerModule lmTCL(SCLEX_TCL, ColouriseCppDoc, "tcl", FoldCppDoc);