1 // Scintilla source code edit control
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.
16 #pragma warning(disable: 4786)
19 // Borland C++ displays warnings in vector header without this
20 #pragma option -w-ccc -w-rch
29 #include "Scintilla.h"
32 #include "PropSetSimple.h"
34 #include "LexAccessor.h"
36 #include "StyleContext.h"
37 #include "CharacterSet.h"
38 #include "LexerModule.h"
39 #include "OptionSet.h"
41 #define SET_LOWER "abcdefghijklmnopqrstuvwxyz"
42 #define SET_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
43 #define SET_DIGITS "0123456789"
45 using namespace Scintilla
;
47 static bool IsSpaceEquiv(int state
) {
51 case SCE_ECL_COMMENTLINE
:
52 case SCE_ECL_COMMENTLINEDOC
:
53 case SCE_ECL_COMMENTDOCKEYWORD
:
54 case SCE_ECL_COMMENTDOCKEYWORDERROR
:
55 case SCE_ECL_COMMENTDOC
:
63 static void ColouriseEclDoc(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, WordList
*keywordlists
[],
65 WordList
&keywords0
= *keywordlists
[0];
66 WordList
&keywords1
= *keywordlists
[1];
67 WordList
&keywords2
= *keywordlists
[2];
68 WordList
&keywords3
= *keywordlists
[3]; //Value Types
69 WordList
&keywords4
= *keywordlists
[4];
70 WordList
&keywords5
= *keywordlists
[5];
71 WordList
&keywords6
= *keywordlists
[6]; //Javadoc Tags
73 cplusplus
.Set("beginc endc");
75 bool stylingWithinPreprocessor
= false;
77 CharacterSet
setOKBeforeRE(CharacterSet::setNone
, "(=,");
78 CharacterSet
setDoxygen(CharacterSet::setLower
, "$@\\&<>#{}[]");
79 CharacterSet
setWordStart(CharacterSet::setAlpha
, "_", 0x80, true);
80 CharacterSet
setWord(CharacterSet::setAlphaNum
, "._", 0x80, true);
81 CharacterSet
setQualified(CharacterSet::setNone
, "uUxX");
83 int chPrevNonWhite
= ' ';
85 bool lastWordWasUUID
= false;
86 int styleBeforeDCKeyword
= SCE_ECL_DEFAULT
;
87 bool continuationLine
= false;
89 if (initStyle
== SCE_ECL_PREPROCESSOR
) {
90 // Set continuationLine if last character of previous line is '\'
91 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
92 if (lineCurrent
> 0) {
93 int chBack
= styler
.SafeGetCharAt(startPos
-1, 0);
94 int chBack2
= styler
.SafeGetCharAt(startPos
-2, 0);
95 int lineEndChar
= '!';
96 if (chBack2
== '\r' && chBack
== '\n') {
97 lineEndChar
= styler
.SafeGetCharAt(startPos
-3, 0);
98 } else if (chBack
== '\n' || chBack
== '\r') {
99 lineEndChar
= chBack2
;
101 continuationLine
= lineEndChar
== '\\';
105 // look back to set chPrevNonWhite properly for better regex colouring
107 Sci_Position back
= startPos
;
108 while (--back
&& IsSpaceEquiv(styler
.StyleAt(back
)))
110 if (styler
.StyleAt(back
) == SCE_ECL_OPERATOR
) {
111 chPrevNonWhite
= styler
.SafeGetCharAt(back
);
115 StyleContext
sc(startPos
, length
, initStyle
, styler
);
117 for (; sc
.More(); sc
.Forward()) {
118 if (sc
.atLineStart
) {
119 if (sc
.state
== SCE_ECL_STRING
) {
120 // Prevent SCE_ECL_STRINGEOL from leaking back to previous line which
121 // ends with a line continuation by locking in the state upto this position.
122 sc
.SetState(SCE_ECL_STRING
);
124 // Reset states to begining of colourise so no surprises
125 // if different sets of lines lexed.
127 lastWordWasUUID
= false;
130 // Handle line continuation generically.
132 if (sc
.chNext
== '\n' || sc
.chNext
== '\r') {
134 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
137 continuationLine
= true;
142 // Determine if the current state should terminate.
145 case SCE_ECL_DELETED
:
146 case SCE_ECL_CHANGED
:
149 sc
.SetState(SCE_ECL_DEFAULT
);
151 case SCE_ECL_OPERATOR
:
152 sc
.SetState(SCE_ECL_DEFAULT
);
155 // We accept almost anything because of hex. and number suffixes
156 if (!setWord
.Contains(sc
.ch
)) {
157 sc
.SetState(SCE_ECL_DEFAULT
);
160 case SCE_ECL_IDENTIFIER
:
161 if (!setWord
.Contains(sc
.ch
) || (sc
.ch
== '.')) {
163 sc
.GetCurrentLowered(s
, sizeof(s
));
164 if (keywords0
.InList(s
)) {
165 lastWordWasUUID
= strcmp(s
, "uuid") == 0;
166 sc
.ChangeState(SCE_ECL_WORD0
);
167 } else if (keywords1
.InList(s
)) {
168 sc
.ChangeState(SCE_ECL_WORD1
);
169 } else if (keywords2
.InList(s
)) {
170 sc
.ChangeState(SCE_ECL_WORD2
);
171 } else if (keywords4
.InList(s
)) {
172 sc
.ChangeState(SCE_ECL_WORD4
);
173 } else if (keywords5
.InList(s
)) {
174 sc
.ChangeState(SCE_ECL_WORD5
);
176 else //Data types are of from KEYWORD##
178 int i
= static_cast<int>(strlen(s
)) - 1;
179 while(i
>= 0 && (isdigit(s
[i
]) || s
[i
] == '_'))
183 strncpy(s2
, s
, i
+ 1);
185 if (keywords3
.InList(s2
)) {
186 sc
.ChangeState(SCE_ECL_WORD3
);
189 sc
.SetState(SCE_ECL_DEFAULT
);
192 case SCE_ECL_PREPROCESSOR
:
193 if (sc
.atLineStart
&& !continuationLine
) {
194 sc
.SetState(SCE_ECL_DEFAULT
);
195 } else if (stylingWithinPreprocessor
) {
196 if (IsASpace(sc
.ch
)) {
197 sc
.SetState(SCE_ECL_DEFAULT
);
200 if (sc
.Match('/', '*') || sc
.Match('/', '/')) {
201 sc
.SetState(SCE_ECL_DEFAULT
);
205 case SCE_ECL_COMMENT
:
206 if (sc
.Match('*', '/')) {
208 sc
.ForwardSetState(SCE_ECL_DEFAULT
);
211 case SCE_ECL_COMMENTDOC
:
212 if (sc
.Match('*', '/')) {
214 sc
.ForwardSetState(SCE_ECL_DEFAULT
);
215 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
216 // Verify that we have the conditions to mark a comment-doc-keyword
217 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
218 styleBeforeDCKeyword
= SCE_ECL_COMMENTDOC
;
219 sc
.SetState(SCE_ECL_COMMENTDOCKEYWORD
);
223 case SCE_ECL_COMMENTLINE
:
224 if (sc
.atLineStart
) {
225 sc
.SetState(SCE_ECL_DEFAULT
);
228 case SCE_ECL_COMMENTLINEDOC
:
229 if (sc
.atLineStart
) {
230 sc
.SetState(SCE_ECL_DEFAULT
);
231 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
232 // Verify that we have the conditions to mark a comment-doc-keyword
233 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '/' || sc
.chPrev
== '!') && (!IsASpace(sc
.chNext
))) {
234 styleBeforeDCKeyword
= SCE_ECL_COMMENTLINEDOC
;
235 sc
.SetState(SCE_ECL_COMMENTDOCKEYWORD
);
239 case SCE_ECL_COMMENTDOCKEYWORD
:
240 if ((styleBeforeDCKeyword
== SCE_ECL_COMMENTDOC
) && sc
.Match('*', '/')) {
241 sc
.ChangeState(SCE_ECL_COMMENTDOCKEYWORDERROR
);
243 sc
.ForwardSetState(SCE_ECL_DEFAULT
);
244 } else if (!setDoxygen
.Contains(sc
.ch
)) {
246 sc
.GetCurrentLowered(s
, sizeof(s
));
247 if (!IsASpace(sc
.ch
) || !keywords6
.InList(s
+1)) {
248 sc
.ChangeState(SCE_ECL_COMMENTDOCKEYWORDERROR
);
250 sc
.SetState(styleBeforeDCKeyword
);
255 sc
.ChangeState(SCE_ECL_STRINGEOL
);
256 } else if (sc
.ch
== '\\') {
257 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
260 } else if (sc
.ch
== '\"') {
261 sc
.ForwardSetState(SCE_ECL_DEFAULT
);
264 case SCE_ECL_CHARACTER
:
266 sc
.ChangeState(SCE_ECL_STRINGEOL
);
267 } else if (sc
.ch
== '\\') {
268 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
271 } else if (sc
.ch
== '\'') {
272 sc
.ForwardSetState(SCE_ECL_DEFAULT
);
276 if (sc
.atLineStart
) {
277 sc
.SetState(SCE_ECL_DEFAULT
);
278 } else if (sc
.ch
== '/') {
280 while ((sc
.ch
< 0x80) && islower(sc
.ch
))
281 sc
.Forward(); // gobble regex flags
282 sc
.SetState(SCE_ECL_DEFAULT
);
283 } else if (sc
.ch
== '\\') {
284 // Gobble up the quoted character
285 if (sc
.chNext
== '\\' || sc
.chNext
== '/') {
290 case SCE_ECL_STRINGEOL
:
291 if (sc
.atLineStart
) {
292 sc
.SetState(SCE_ECL_DEFAULT
);
295 case SCE_ECL_VERBATIM
:
297 if (sc
.chNext
== '\"') {
300 sc
.ForwardSetState(SCE_ECL_DEFAULT
);
305 if (sc
.ch
== '\r' || sc
.ch
== '\n' || sc
.ch
== ')') {
306 sc
.SetState(SCE_ECL_DEFAULT
);
311 // Determine if a new state should be entered.
312 Sci_Position lineCurrent
= styler
.GetLine(sc
.currentPos
);
313 int lineState
= styler
.GetLineState(lineCurrent
);
314 if (sc
.state
== SCE_ECL_DEFAULT
) {
316 sc
.SetState(lineState
);
318 else if (sc
.Match('@', '\"')) {
319 sc
.SetState(SCE_ECL_VERBATIM
);
321 } else if (setQualified
.Contains(sc
.ch
) && sc
.chNext
== '\'') {
322 sc
.SetState(SCE_ECL_CHARACTER
);
324 } else if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
325 if (lastWordWasUUID
) {
326 sc
.SetState(SCE_ECL_UUID
);
327 lastWordWasUUID
= false;
329 sc
.SetState(SCE_ECL_NUMBER
);
331 } else if (setWordStart
.Contains(sc
.ch
) || (sc
.ch
== '@')) {
332 if (lastWordWasUUID
) {
333 sc
.SetState(SCE_ECL_UUID
);
334 lastWordWasUUID
= false;
336 sc
.SetState(SCE_ECL_IDENTIFIER
);
338 } else if (sc
.Match('/', '*')) {
339 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Qt/Doxygen doc. style
340 sc
.SetState(SCE_ECL_COMMENTDOC
);
342 sc
.SetState(SCE_ECL_COMMENT
);
344 sc
.Forward(); // Eat the * so it isn't used for the end of the comment
345 } else if (sc
.Match('/', '/')) {
346 if ((sc
.Match("///") && !sc
.Match("////")) || sc
.Match("//!"))
347 // Support of Qt/Doxygen doc. style
348 sc
.SetState(SCE_ECL_COMMENTLINEDOC
);
350 sc
.SetState(SCE_ECL_COMMENTLINE
);
351 } else if (sc
.ch
== '/' && setOKBeforeRE
.Contains(chPrevNonWhite
)) {
352 sc
.SetState(SCE_ECL_REGEX
); // JavaScript's RegEx
353 // } else if (sc.ch == '\"') {
354 // sc.SetState(SCE_ECL_STRING);
355 } else if (sc
.ch
== '\'') {
356 sc
.SetState(SCE_ECL_CHARACTER
);
357 } else if (sc
.ch
== '#' && visibleChars
== 0) {
358 // Preprocessor commands are alone on their line
359 sc
.SetState(SCE_ECL_PREPROCESSOR
);
360 // Skip whitespace between # and preprocessor word
363 } while ((sc
.ch
== ' ' || sc
.ch
== '\t') && sc
.More());
365 sc
.SetState(SCE_ECL_DEFAULT
);
367 } else if (isoperator(static_cast<char>(sc
.ch
))) {
368 sc
.SetState(SCE_ECL_OPERATOR
);
372 if (!IsASpace(sc
.ch
) && !IsSpaceEquiv(sc
.state
)) {
373 chPrevNonWhite
= sc
.ch
;
376 continuationLine
= false;
382 static bool IsStreamCommentStyle(int style
) {
383 return style
== SCE_ECL_COMMENT
||
384 style
== SCE_ECL_COMMENTDOC
||
385 style
== SCE_ECL_COMMENTDOCKEYWORD
||
386 style
== SCE_ECL_COMMENTDOCKEYWORDERROR
;
389 static bool MatchNoCase(Accessor
& styler
, Sci_PositionU
& pos
, const char *s
) {
392 char compare_char
= tolower(*s
);
393 char styler_char
= tolower(styler
.SafeGetCharAt(pos
+i
));
394 if (compare_char
!= styler_char
)
403 // Store both the current line's fold level and the next lines in the
404 // level store to make it easy to pick up with each increment
405 // and to make it possible to fiddle the current level for "} else {".
406 static void FoldEclDoc(Sci_PositionU startPos
, Sci_Position length
, int initStyle
,
407 WordList
*[], Accessor
&styler
) {
408 bool foldComment
= true;
409 bool foldPreprocessor
= true;
410 bool foldCompact
= true;
411 bool foldAtElse
= true;
412 Sci_PositionU endPos
= startPos
+ length
;
413 int visibleChars
= 0;
414 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
415 int levelCurrent
= SC_FOLDLEVELBASE
;
417 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
418 int levelMinCurrent
= levelCurrent
;
419 int levelNext
= levelCurrent
;
420 char chNext
= styler
[startPos
];
421 int styleNext
= styler
.StyleAt(startPos
);
422 int style
= initStyle
;
423 for (Sci_PositionU i
= startPos
; i
< endPos
; i
++) {
425 chNext
= styler
.SafeGetCharAt(i
+ 1);
426 int stylePrev
= style
;
428 styleNext
= styler
.StyleAt(i
+ 1);
429 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
430 if (foldComment
&& IsStreamCommentStyle(style
)) {
431 if (!IsStreamCommentStyle(stylePrev
) && (stylePrev
!= SCE_ECL_COMMENTLINEDOC
)) {
433 } else if (!IsStreamCommentStyle(styleNext
) && (styleNext
!= SCE_ECL_COMMENTLINEDOC
) && !atEOL
) {
434 // Comments don't end at end of line and the next character may be unstyled.
438 if (foldComment
&& (style
== SCE_ECL_COMMENTLINE
)) {
439 if ((ch
== '/') && (chNext
== '/')) {
440 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
441 if (chNext2
== '{') {
443 } else if (chNext2
== '}') {
448 if (foldPreprocessor
&& (style
== SCE_ECL_PREPROCESSOR
)) {
450 Sci_PositionU j
= i
+ 1;
451 while ((j
< endPos
) && IsASpaceOrTab(styler
.SafeGetCharAt(j
))) {
454 if (MatchNoCase(styler
, j
, "region") || MatchNoCase(styler
, j
, "if")) {
456 } else if (MatchNoCase(styler
, j
, "endregion") || MatchNoCase(styler
, j
, "end")) {
461 if (style
== SCE_ECL_OPERATOR
) {
463 // Measure the minimum before a '{' to allow
464 // folding on "} else {"
465 if (levelMinCurrent
> levelNext
) {
466 levelMinCurrent
= levelNext
;
469 } else if (ch
== '}') {
473 if (style
== SCE_ECL_WORD2
) {
474 if (MatchNoCase(styler
, i
, "record") || MatchNoCase(styler
, i
, "transform") || MatchNoCase(styler
, i
, "type") || MatchNoCase(styler
, i
, "function") ||
475 MatchNoCase(styler
, i
, "module") || MatchNoCase(styler
, i
, "service") || MatchNoCase(styler
, i
, "interface") || MatchNoCase(styler
, i
, "ifblock") ||
476 MatchNoCase(styler
, i
, "macro") || MatchNoCase(styler
, i
, "beginc++")) {
478 } else if (MatchNoCase(styler
, i
, "endmacro") || MatchNoCase(styler
, i
, "endc++") || MatchNoCase(styler
, i
, "end")) {
482 if (atEOL
|| (i
== endPos
-1)) {
483 int levelUse
= levelCurrent
;
485 levelUse
= levelMinCurrent
;
487 int lev
= levelUse
| levelNext
<< 16;
488 if (visibleChars
== 0 && foldCompact
)
489 lev
|= SC_FOLDLEVELWHITEFLAG
;
490 if (levelUse
< levelNext
)
491 lev
|= SC_FOLDLEVELHEADERFLAG
;
492 if (lev
!= styler
.LevelAt(lineCurrent
)) {
493 styler
.SetLevel(lineCurrent
, lev
);
496 levelCurrent
= levelNext
;
497 levelMinCurrent
= levelCurrent
;
498 if (atEOL
&& (i
== static_cast<Sci_PositionU
>(styler
.Length()-1))) {
499 // There is an empty line at end of file so give it same level and empty
500 styler
.SetLevel(lineCurrent
, (levelCurrent
| levelCurrent
<< 16) | SC_FOLDLEVELWHITEFLAG
);
509 static const char * const EclWordListDesc
[] = {