1 // Scintilla source code edit control
3 ** Lexer for C++, C, Java, and Javascript.
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.
19 #include "Scintilla.h"
22 static bool IsOKBeforeRE(int ch
) {
23 return (ch
== '(') || (ch
== '=') || (ch
== ',');
26 static void getRange(unsigned int start
,
32 while ((i
< end
- start
+ 1) && (i
< len
-1)) {
33 s
[i
] = styler
[start
+ i
];
39 inline bool IsASpace(int ch
) {
40 return (ch
== ' ') || ((ch
>= 0x09) && (ch
<= 0x0d));
43 inline bool IsAWordChar(int ch
) {
44 return (ch
< 0x80) && (isalnum(ch
) || ch
== '.' || ch
== '_');
47 inline bool IsAWordStart(int ch
) {
48 return (ch
< 0x80) && (isalnum(ch
) || ch
== '_');
51 inline bool IsADigit(int ch
) {
52 return (ch
>= '0') && (ch
<= '9');
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
{
63 ColouriseContext
& operator=(const ColouriseContext
&) {
73 ColouriseContext(unsigned int startPos
, int length
,
74 int initStyle
, Accessor
&styler_
) :
76 lengthDoc(startPos
+ length
),
83 styler
.StartAt(startPos
);
84 styler
.StartSegment(startPos
);
86 ch
= static_cast<unsigned char>(styler
.SafeGetCharAt(pos
));
87 if (styler
.IsLeadByte(static_cast<char>(ch
))) {
90 ch
|= static_cast<unsigned char>(styler
.SafeGetCharAt(pos
));
92 chNext
= static_cast<unsigned char>(styler
.SafeGetCharAt(pos
+1));
93 if (styler
.IsLeadByte(static_cast<char>(chNext
))) {
95 chNext
|= static_cast<unsigned char>(styler
.SafeGetCharAt(pos
+2));
97 atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
100 styler
.ColourTo(currentPos
- 1, state
);
103 return currentPos
<= lengthDoc
;
106 // A lot of this is repeated from the constructor - TODO: merge code
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));
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
120 atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
122 void ChangeState(int state_
) {
125 void SetState(int state_
) {
126 styler
.ColourTo(currentPos
- 1, state
);
129 void ForwardSetState(int state_
) {
131 styler
.ColourTo(currentPos
- 1, state
);
134 void GetCurrent(char *s
, int len
) {
135 getRange(styler
.GetStartSegment(), currentPos
- 1, styler
, s
, len
);
137 int LengthCurrent() {
138 return currentPos
- styler
.GetStartSegment();
140 bool Match(char ch0
) {
143 bool Match(char ch0
, char ch1
) {
144 return (ch
== ch0
) && (chNext
== ch1
);
146 bool Match(const char *s
) {
153 for (int n
=2; *s
; n
++) {
154 if (*s
!= styler
.SafeGetCharAt(currentPos
+n
))
162 static void ColouriseCppDoc(unsigned int startPos
, int length
, int initStyle
, WordList
*keywordlists
[],
165 WordList
&keywords
= *keywordlists
[0];
166 WordList
&keywords2
= *keywordlists
[1];
168 bool stylingWithinPreprocessor
= styler
.GetPropertyInt("styling.within.preprocessor");
170 if (initStyle
== SCE_C_STRINGEOL
) // Does not leak onto next line
171 initStyle
= SCE_C_DEFAULT
;
173 int chPrevNonWhite
= ' ';
174 int visibleChars
= 0;
175 bool lastWordWasUUID
= false;
177 ColouriseContext
cc(startPos
, length
, initStyle
, styler
);
179 for (; cc
.More(); cc
.Forward()) {
181 if (cc
.state
== SCE_C_STRINGEOL
) {
183 cc
.SetState(SCE_C_DEFAULT
);
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
);
191 } else if (cc
.state
== SCE_C_IDENTIFIER
) {
192 if (!IsAWordChar(cc
.ch
) || (cc
.ch
== '.')) {
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
);
201 cc
.SetState(SCE_C_DEFAULT
);
203 } if (cc
.state
== SCE_C_PREPROCESSOR
) {
204 if (stylingWithinPreprocessor
) {
205 if (IsASpace(cc
.ch
)) {
206 cc
.SetState(SCE_C_DEFAULT
);
209 if (cc
.atEOL
&& (cc
.chPrev
!= '\\')) {
210 cc
.SetState(SCE_C_DEFAULT
);
213 } else if (cc
.state
== SCE_C_COMMENT
) {
214 if (cc
.Match('*', '/')) {
216 cc
.ForwardSetState(SCE_C_DEFAULT
);
218 } else if (cc
.state
== SCE_C_COMMENTDOC
) {
219 if (cc
.Match('*', '/')) {
221 cc
.ForwardSetState(SCE_C_DEFAULT
);
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
);
227 } else if (cc
.state
== SCE_C_STRING
) {
229 if (cc
.chNext
== '\"' || cc
.chNext
== '\'' || cc
.chNext
== '\\') {
232 } else if (cc
.ch
== '\"') {
233 cc
.ForwardSetState(SCE_C_DEFAULT
);
234 } else if ((cc
.atEOL
) && (cc
.chPrev
!= '\\')) {
235 cc
.ChangeState(SCE_C_STRINGEOL
);
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
== '\\') {
244 } else if (cc
.ch
== '\'') {
245 cc
.ForwardSetState(SCE_C_DEFAULT
);
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
== '/') {
256 } else if (cc
.state
== SCE_C_VERBATIM
) {
258 if (cc
.chNext
== '\"') {
261 cc
.ForwardSetState(SCE_C_DEFAULT
);
264 } else if (cc
.state
== SCE_C_UUID
) {
265 if (cc
.ch
== '\r' || cc
.ch
== '\n' || cc
.ch
== ')') {
266 cc
.SetState(SCE_C_DEFAULT
);
270 if (cc
.state
== SCE_C_DEFAULT
) {
271 if (cc
.Match('@', '\"')) {
272 cc
.SetState(SCE_C_VERBATIM
);
274 } else if (IsADigit(cc
.ch
) || (cc
.ch
== '.' && IsADigit(cc
.chNext
))) {
275 if (lastWordWasUUID
) {
276 cc
.SetState(SCE_C_UUID
);
277 lastWordWasUUID
= false;
279 cc
.SetState(SCE_C_NUMBER
);
281 } else if (IsAWordStart(cc
.ch
) || (cc
.ch
== '@')) {
282 if (lastWordWasUUID
) {
283 cc
.SetState(SCE_C_UUID
);
284 lastWordWasUUID
= false;
286 cc
.SetState(SCE_C_IDENTIFIER
);
288 } else if (cc
.Match('/', '*')) {
289 if (cc
.Match("/**") || cc
.Match("/*!")) // Support of Qt/Doxygen doc. style
290 cc
.SetState(SCE_C_COMMENTDOC
);
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
);
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
311 } while (IsASpace(cc
.ch
) && cc
.More());
312 } else if (isoperator(static_cast<char>(cc
.ch
))) {
313 cc
.SetState(SCE_C_OPERATOR
);
317 // Reset states to begining of colourise so no surprises
318 // if different sets of lines lexed.
319 chPrevNonWhite
= ' ';
321 lastWordWasUUID
= false;
323 if (!IsASpace(cc
.ch
)) {
324 chPrevNonWhite
= cc
.ch
;
331 static void FoldCppDoc(unsigned int startPos
, int length
, int initStyle
, WordList
*[],
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
++) {
345 chNext
= styler
.SafeGetCharAt(i
+ 1);
346 int stylePrev
= style
;
348 styleNext
= styler
.StyleAt(i
+ 1);
349 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
351 (style
== SCE_C_COMMENT
|| style
== SCE_C_COMMENTDOC
)) {
352 if (style
!= stylePrev
) {
354 } else if ((style
!= styleNext
) && !atEOL
) {
355 // Comments don't end at end of line and the next character may be unstyled.
359 if (style
== SCE_C_OPERATOR
) {
362 } else if (ch
== '}') {
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
);
376 levelPrev
= levelCurrent
;
379 if (!isspacechar(ch
))
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
);
387 LexerModule
lmCPP(SCLEX_CPP
, ColouriseCppDoc
, "cpp", FoldCppDoc
);
388 LexerModule
lmTCL(SCLEX_TCL
, ColouriseCppDoc
, "tcl", FoldCppDoc
);