]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexCPP.cxx
c85664fd5b04e8bc8b2aa687a9c9dbbabe10296d
[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 // Handle line continuation generically.
76 if (sc.ch == '\\') {
77 if (sc.Match("\\\n")) {
78 sc.Forward();
79 sc.Forward();
80 continue;
81 }
82 if (sc.Match("\\\r\n")) {
83 sc.Forward();
84 sc.Forward();
85 sc.Forward();
86 continue;
87 }
88 }
89
90 // Determine if the current state should terminate.
91 if (sc.state == SCE_C_OPERATOR) {
92 sc.SetState(SCE_C_DEFAULT);
93 } else if (sc.state == SCE_C_NUMBER) {
94 if (!IsAWordChar(sc.ch)) {
95 sc.SetState(SCE_C_DEFAULT);
96 }
97 } else if (sc.state == SCE_C_IDENTIFIER) {
98 if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
99 char s[100];
100 sc.GetCurrent(s, sizeof(s));
101 if (keywords.InList(s)) {
102 lastWordWasUUID = strcmp(s, "uuid") == 0;
103 sc.ChangeState(SCE_C_WORD);
104 } else if (keywords2.InList(s)) {
105 sc.ChangeState(SCE_C_WORD2);
106 }
107 sc.SetState(SCE_C_DEFAULT);
108 }
109 } else if (sc.state == SCE_C_PREPROCESSOR) {
110 if (stylingWithinPreprocessor) {
111 if (IsASpace(sc.ch)) {
112 sc.SetState(SCE_C_DEFAULT);
113 }
114 } else {
115 if (sc.atLineEnd) {
116 sc.SetState(SCE_C_DEFAULT);
117 }
118 }
119 } else if (sc.state == SCE_C_COMMENT) {
120 if (sc.Match('*', '/')) {
121 sc.Forward();
122 sc.ForwardSetState(SCE_C_DEFAULT);
123 }
124 } else if (sc.state == SCE_C_COMMENTDOC) {
125 if (sc.Match('*', '/')) {
126 sc.Forward();
127 sc.ForwardSetState(SCE_C_DEFAULT);
128 } else if (sc.ch == '@' || sc.ch == '\\') {
129 sc.SetState(SCE_C_COMMENTDOCKEYWORD);
130 }
131 } else if (sc.state == SCE_C_COMMENTLINE || sc.state == SCE_C_COMMENTLINEDOC) {
132 if (sc.atLineEnd) {
133 sc.SetState(SCE_C_DEFAULT);
134 visibleChars = 0;
135 }
136 } else if (sc.state == SCE_C_COMMENTDOCKEYWORD) {
137 if (sc.Match('*', '/')) {
138 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
139 sc.Forward();
140 sc.ForwardSetState(SCE_C_DEFAULT);
141 } else if (!IsADoxygenChar(sc.ch)) {
142 char s[100];
143 sc.GetCurrent(s, sizeof(s));
144 if (!isspace(sc.ch) || !keywords3.InList(s+1)) {
145 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
146 }
147 sc.SetState(SCE_C_COMMENTDOC);
148 }
149 } else if (sc.state == SCE_C_STRING) {
150 if (sc.ch == '\\') {
151 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
152 sc.Forward();
153 }
154 } else if (sc.ch == '\"') {
155 sc.ForwardSetState(SCE_C_DEFAULT);
156 } else if (sc.atLineEnd) {
157 sc.ChangeState(SCE_C_STRINGEOL);
158 sc.ForwardSetState(SCE_C_DEFAULT);
159 visibleChars = 0;
160 }
161 } else if (sc.state == SCE_C_CHARACTER) {
162 if (sc.atLineEnd) {
163 sc.ChangeState(SCE_C_STRINGEOL);
164 sc.ForwardSetState(SCE_C_DEFAULT);
165 visibleChars = 0;
166 } else if (sc.ch == '\\') {
167 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
168 sc.Forward();
169 }
170 } else if (sc.ch == '\'') {
171 sc.ForwardSetState(SCE_C_DEFAULT);
172 }
173 } else if (sc.state == SCE_C_REGEX) {
174 if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == '/') {
175 sc.ForwardSetState(SCE_C_DEFAULT);
176 } else if (sc.ch == '\\') {
177 // Gobble up the quoted character
178 if (sc.chNext == '\\' || sc.chNext == '/') {
179 sc.Forward();
180 }
181 }
182 } else if (sc.state == SCE_C_VERBATIM) {
183 if (sc.ch == '\"') {
184 if (sc.chNext == '\"') {
185 sc.Forward();
186 } else {
187 sc.ForwardSetState(SCE_C_DEFAULT);
188 }
189 }
190 } else if (sc.state == SCE_C_UUID) {
191 if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {
192 sc.SetState(SCE_C_DEFAULT);
193 }
194 }
195
196 // Determine if a new state should be entered.
197 if (sc.state == SCE_C_DEFAULT) {
198 if (sc.Match('@', '\"')) {
199 sc.SetState(SCE_C_VERBATIM);
200 sc.Forward();
201 } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
202 if (lastWordWasUUID) {
203 sc.SetState(SCE_C_UUID);
204 lastWordWasUUID = false;
205 } else {
206 sc.SetState(SCE_C_NUMBER);
207 }
208 } else if (IsAWordStart(sc.ch) || (sc.ch == '@')) {
209 if (lastWordWasUUID) {
210 sc.SetState(SCE_C_UUID);
211 lastWordWasUUID = false;
212 } else {
213 sc.SetState(SCE_C_IDENTIFIER);
214 }
215 } else if (sc.Match('/', '*')) {
216 if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
217 sc.SetState(SCE_C_COMMENTDOC);
218 } else {
219 sc.SetState(SCE_C_COMMENT);
220 }
221 sc.Forward(); // Eat the * so it isn't used for the end of the comment
222 } else if (sc.Match('/', '/')) {
223 if (sc.Match("///") || sc.Match("//!")) // Support of Qt/Doxygen doc. style
224 sc.SetState(SCE_C_COMMENTLINEDOC);
225 else
226 sc.SetState(SCE_C_COMMENTLINE);
227 } else if (sc.ch == '/' && IsOKBeforeRE(chPrevNonWhite)) {
228 sc.SetState(SCE_C_REGEX);
229 } else if (sc.ch == '\"') {
230 sc.SetState(SCE_C_STRING);
231 } else if (sc.ch == '\'') {
232 sc.SetState(SCE_C_CHARACTER);
233 } else if (sc.ch == '#' && visibleChars == 0) {
234 // Preprocessor commands are alone on their line
235 sc.SetState(SCE_C_PREPROCESSOR);
236 // Skip whitespace between # and preprocessor word
237 do {
238 sc.Forward();
239 } while ((sc.ch == ' ') && (sc.ch == '\t') && sc.More());
240 if (sc.atLineEnd) {
241 sc.SetState(SCE_C_DEFAULT);
242 }
243 } else if (isoperator(static_cast<char>(sc.ch))) {
244 sc.SetState(SCE_C_OPERATOR);
245 }
246 }
247
248 if (sc.atLineEnd) {
249 // Reset states to begining of colourise so no surprises
250 // if different sets of lines lexed.
251 chPrevNonWhite = ' ';
252 visibleChars = 0;
253 lastWordWasUUID = false;
254 }
255 if (!IsASpace(sc.ch)) {
256 chPrevNonWhite = sc.ch;
257 visibleChars++;
258 }
259 }
260 sc.Complete();
261 }
262
263 static void FoldCppDoc(unsigned int startPos, int length, int initStyle, WordList *[],
264 Accessor &styler) {
265 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
266 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
267 unsigned int endPos = startPos + length;
268 int visibleChars = 0;
269 int lineCurrent = styler.GetLine(startPos);
270 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
271 int levelCurrent = levelPrev;
272 char chNext = styler[startPos];
273 int styleNext = styler.StyleAt(startPos);
274 int style = initStyle;
275 for (unsigned int i = startPos; i < endPos; i++) {
276 char ch = chNext;
277 chNext = styler.SafeGetCharAt(i + 1);
278 int stylePrev = style;
279 style = styleNext;
280 styleNext = styler.StyleAt(i + 1);
281 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
282 if (foldComment &&
283 (style == SCE_C_COMMENT || style == SCE_C_COMMENTDOC)) {
284 if (style != stylePrev) {
285 levelCurrent++;
286 } else if ((style != styleNext) && !atEOL) {
287 // Comments don't end at end of line and the next character may be unstyled.
288 levelCurrent--;
289 }
290 }
291 if (style == SCE_C_OPERATOR) {
292 if (ch == '{') {
293 levelCurrent++;
294 } else if (ch == '}') {
295 levelCurrent--;
296 }
297 }
298 if (atEOL) {
299 int lev = levelPrev;
300 if (visibleChars == 0 && foldCompact)
301 lev |= SC_FOLDLEVELWHITEFLAG;
302 if ((levelCurrent > levelPrev) && (visibleChars > 0))
303 lev |= SC_FOLDLEVELHEADERFLAG;
304 if (lev != styler.LevelAt(lineCurrent)) {
305 styler.SetLevel(lineCurrent, lev);
306 }
307 lineCurrent++;
308 levelPrev = levelCurrent;
309 visibleChars = 0;
310 }
311 if (!isspacechar(ch))
312 visibleChars++;
313 }
314 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
315 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
316 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
317 }
318
319 LexerModule lmCPP(SCLEX_CPP, ColouriseCppDoc, "cpp", FoldCppDoc);
320 LexerModule lmTCL(SCLEX_TCL, ColouriseCppDoc, "tcl", FoldCppDoc);