1 // Scintilla source code edit control
3 ** Lexer for C++, C, Java, and Javascript.
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.
18 #include "StyleContext.h"
20 #include "Scintilla.h"
23 static bool IsOKBeforeRE(const int ch
) {
24 return (ch
== '(') || (ch
== '=') || (ch
== ',');
27 static inline bool IsAWordChar(const int ch
) {
28 return (ch
< 0x80) && (isalnum(ch
) || ch
== '.' || ch
== '_');
31 static inline bool IsAWordStart(const int ch
) {
32 return (ch
< 0x80) && (isalnum(ch
) || ch
== '_');
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
== ']');
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
));
50 static inline bool IsStateString(const int state
) {
51 return ((state
== SCE_C_STRING
) || (state
== SCE_C_VERBATIM
));
54 static void ColouriseCppDoc(unsigned int startPos
, int length
, int initStyle
, WordList
*keywordlists
[],
57 WordList
&keywords
= *keywordlists
[0];
58 WordList
&keywords2
= *keywordlists
[1];
59 WordList
&keywords3
= *keywordlists
[2];
61 bool stylingWithinPreprocessor
= styler
.GetPropertyInt("styling.within.preprocessor") != 0;
63 // Do not leak onto next line
64 if (initStyle
== SCE_C_STRINGEOL
)
65 initStyle
= SCE_C_DEFAULT
;
67 int chPrevNonWhite
= ' ';
69 bool lastWordWasUUID
= false;
71 StyleContext
sc(startPos
, length
, initStyle
, styler
);
73 for (; sc
.More(); sc
.Forward()) {
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
);
80 // Handle line continuation generically.
82 if (sc
.chNext
== '\n' || sc
.chNext
== '\r') {
84 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
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
);
98 } else if (sc
.state
== SCE_C_IDENTIFIER
) {
99 if (!IsAWordChar(sc
.ch
) || (sc
.ch
== '.')) {
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
);
108 sc
.SetState(SCE_C_DEFAULT
);
110 } else if (sc
.state
== SCE_C_PREPROCESSOR
) {
111 if (stylingWithinPreprocessor
) {
112 if (IsASpace(sc
.ch
)) {
113 sc
.SetState(SCE_C_DEFAULT
);
117 sc
.SetState(SCE_C_DEFAULT
);
120 } else if (sc
.state
== SCE_C_COMMENT
) {
121 if (sc
.Match('*', '/')) {
123 sc
.ForwardSetState(SCE_C_DEFAULT
);
125 } else if (sc
.state
== SCE_C_COMMENTDOC
) {
126 if (sc
.Match('*', '/')) {
128 sc
.ForwardSetState(SCE_C_DEFAULT
);
129 } else if (sc
.ch
== '@' || sc
.ch
== '\\') {
130 sc
.SetState(SCE_C_COMMENTDOCKEYWORD
);
132 } else if (sc
.state
== SCE_C_COMMENTLINE
|| sc
.state
== SCE_C_COMMENTLINEDOC
) {
134 sc
.SetState(SCE_C_DEFAULT
);
137 } else if (sc
.state
== SCE_C_COMMENTDOCKEYWORD
) {
138 if (sc
.Match('*', '/')) {
139 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
);
141 sc
.ForwardSetState(SCE_C_DEFAULT
);
142 } else if (!IsADoxygenChar(sc
.ch
)) {
144 sc
.GetCurrent(s
, sizeof(s
));
145 if (!isspace(sc
.ch
) || !keywords3
.InList(s
+1)) {
146 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
);
148 sc
.SetState(SCE_C_COMMENTDOC
);
150 } else if (sc
.state
== SCE_C_STRING
) {
152 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
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
);
162 } else if (sc
.state
== SCE_C_CHARACTER
) {
164 sc
.ChangeState(SCE_C_STRINGEOL
);
165 sc
.ForwardSetState(SCE_C_DEFAULT
);
167 } else if (sc
.ch
== '\\') {
168 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
171 } else if (sc
.ch
== '\'') {
172 sc
.ForwardSetState(SCE_C_DEFAULT
);
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
== '/') {
183 } else if (sc
.state
== SCE_C_VERBATIM
) {
185 if (sc
.chNext
== '\"') {
188 sc
.ForwardSetState(SCE_C_DEFAULT
);
191 } else if (sc
.state
== SCE_C_UUID
) {
192 if (sc
.ch
== '\r' || sc
.ch
== '\n' || sc
.ch
== ')') {
193 sc
.SetState(SCE_C_DEFAULT
);
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
);
202 } else if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
203 if (lastWordWasUUID
) {
204 sc
.SetState(SCE_C_UUID
);
205 lastWordWasUUID
= false;
207 sc
.SetState(SCE_C_NUMBER
);
209 } else if (IsAWordStart(sc
.ch
) || (sc
.ch
== '@')) {
210 if (lastWordWasUUID
) {
211 sc
.SetState(SCE_C_UUID
);
212 lastWordWasUUID
= false;
214 sc
.SetState(SCE_C_IDENTIFIER
);
216 } else if (sc
.Match('/', '*')) {
217 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Qt/Doxygen doc. style
218 sc
.SetState(SCE_C_COMMENTDOC
);
220 sc
.SetState(SCE_C_COMMENT
);
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
);
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
240 } while ((sc
.ch
== ' ') && (sc
.ch
== '\t') && sc
.More());
242 sc
.SetState(SCE_C_DEFAULT
);
244 } else if (isoperator(static_cast<char>(sc
.ch
))) {
245 sc
.SetState(SCE_C_OPERATOR
);
250 // Reset states to begining of colourise so no surprises
251 // if different sets of lines lexed.
252 chPrevNonWhite
= ' ';
254 lastWordWasUUID
= false;
256 if (!IsASpace(sc
.ch
)) {
257 chPrevNonWhite
= sc
.ch
;
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
;
271 static void FoldCppDoc(unsigned int startPos
, int length
, int initStyle
, WordList
*[],
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
++) {
286 chNext
= styler
.SafeGetCharAt(i
+ 1);
287 int stylePrev
= style
;
289 styleNext
= styler
.StyleAt(i
+ 1);
290 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
291 if (foldComment
&& IsStreamCommentStyle(style
)) {
292 if (!IsStreamCommentStyle(stylePrev
)) {
294 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
295 // Comments don't end at end of line and the next character may be unstyled.
299 if (foldComment
&& (style
== SCE_C_COMMENTLINE
)) {
300 if ((ch
== '/') && (chNext
== '/')) {
301 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
302 if (chNext2
== '{') {
304 } else if (chNext2
== '}') {
309 if (foldPreprocessor
&& (style
== SCE_C_PREPROCESSOR
)) {
312 while ((j
<endPos
) && IsASpaceOrTab(styler
.SafeGetCharAt(j
))) {
315 if (styler
.Match(j
, "region") || styler
.Match(j
, "if")) {
317 } else if (styler
.Match(j
, "end")) {
322 if (style
== SCE_C_OPERATOR
) {
325 } else if (ch
== '}') {
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
);
339 levelPrev
= levelCurrent
;
342 if (!isspacechar(ch
))
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
);
350 static const char * const cppWordLists
[] = {
351 "Primary keywords and identifiers",
352 "Secondary keywords and identifiers",
353 "Documentation comment keywords",
357 LexerModule
lmCPP(SCLEX_CPP
, ColouriseCppDoc
, "cpp", FoldCppDoc
, cppWordLists
);
358 LexerModule
lmTCL(SCLEX_TCL
, ColouriseCppDoc
, "tcl", FoldCppDoc
, cppWordLists
);