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