1 // Scintilla source code edit control
5 // Copyright 2007 by Cristian Adam <cristian [dot] adam [at] gmx [dot] net>
6 // based on the NSIS lexer
7 // The License.txt file describes the conditions under which this software may be distributed.
17 #include "Scintilla.h"
21 #include "LexAccessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
28 using namespace Scintilla
;
31 static bool isCmakeNumber(char ch
)
33 return(ch
>= '0' && ch
<= '9');
36 static bool isCmakeChar(char ch
)
38 return(ch
== '.' ) || (ch
== '_' ) || isCmakeNumber(ch
) || (ch
>= 'A' && ch
<= 'Z') || (ch
>= 'a' && ch
<= 'z');
41 static bool isCmakeLetter(char ch
)
43 return(ch
>= 'A' && ch
<= 'Z') || (ch
>= 'a' && ch
<= 'z');
46 static bool CmakeNextLineHasElse(Sci_PositionU start
, Sci_PositionU end
, Accessor
&styler
)
48 Sci_Position nNextLine
= -1;
49 for ( Sci_PositionU i
= start
; i
< end
; i
++ ) {
50 char cNext
= styler
.SafeGetCharAt( i
);
51 if ( cNext
== '\n' ) {
57 if ( nNextLine
== -1 ) // We never foudn the next line...
60 for ( Sci_PositionU firstChar
= nNextLine
; firstChar
< end
; firstChar
++ ) {
61 char cNext
= styler
.SafeGetCharAt( firstChar
);
66 if ( styler
.Match(firstChar
, "ELSE") || styler
.Match(firstChar
, "else"))
74 static int calculateFoldCmake(Sci_PositionU start
, Sci_PositionU end
, int foldlevel
, Accessor
&styler
, bool bElse
)
76 // If the word is too long, it is not what we are looking for
77 if ( end
- start
> 20 )
80 int newFoldlevel
= foldlevel
;
82 char s
[20]; // The key word we are looking for has atmost 13 characters
83 for (unsigned int i
= 0; i
< end
- start
+ 1 && i
< 19; i
++) {
84 s
[i
] = static_cast<char>( styler
[ start
+ i
] );
88 if ( CompareCaseInsensitive(s
, "IF") == 0 || CompareCaseInsensitive(s
, "WHILE") == 0
89 || CompareCaseInsensitive(s
, "MACRO") == 0 || CompareCaseInsensitive(s
, "FOREACH") == 0
90 || CompareCaseInsensitive(s
, "ELSEIF") == 0 )
92 else if ( CompareCaseInsensitive(s
, "ENDIF") == 0 || CompareCaseInsensitive(s
, "ENDWHILE") == 0
93 || CompareCaseInsensitive(s
, "ENDMACRO") == 0 || CompareCaseInsensitive(s
, "ENDFOREACH") == 0)
95 else if ( bElse
&& CompareCaseInsensitive(s
, "ELSEIF") == 0 )
97 else if ( bElse
&& CompareCaseInsensitive(s
, "ELSE") == 0 )
103 static int classifyWordCmake(Sci_PositionU start
, Sci_PositionU end
, WordList
*keywordLists
[], Accessor
&styler
)
105 char word
[100] = {0};
106 char lowercaseWord
[100] = {0};
108 WordList
&Commands
= *keywordLists
[0];
109 WordList
&Parameters
= *keywordLists
[1];
110 WordList
&UserDefined
= *keywordLists
[2];
112 for (Sci_PositionU i
= 0; i
< end
- start
+ 1 && i
< 99; i
++) {
113 word
[i
] = static_cast<char>( styler
[ start
+ i
] );
114 lowercaseWord
[i
] = static_cast<char>(tolower(word
[i
]));
117 // Check for special words...
118 if ( CompareCaseInsensitive(word
, "MACRO") == 0 || CompareCaseInsensitive(word
, "ENDMACRO") == 0 )
119 return SCE_CMAKE_MACRODEF
;
121 if ( CompareCaseInsensitive(word
, "IF") == 0 || CompareCaseInsensitive(word
, "ENDIF") == 0 )
122 return SCE_CMAKE_IFDEFINEDEF
;
124 if ( CompareCaseInsensitive(word
, "ELSEIF") == 0 || CompareCaseInsensitive(word
, "ELSE") == 0 )
125 return SCE_CMAKE_IFDEFINEDEF
;
127 if ( CompareCaseInsensitive(word
, "WHILE") == 0 || CompareCaseInsensitive(word
, "ENDWHILE") == 0)
128 return SCE_CMAKE_WHILEDEF
;
130 if ( CompareCaseInsensitive(word
, "FOREACH") == 0 || CompareCaseInsensitive(word
, "ENDFOREACH") == 0)
131 return SCE_CMAKE_FOREACHDEF
;
133 if ( Commands
.InList(lowercaseWord
) )
134 return SCE_CMAKE_COMMANDS
;
136 if ( Parameters
.InList(word
) )
137 return SCE_CMAKE_PARAMETERS
;
140 if ( UserDefined
.InList(word
) )
141 return SCE_CMAKE_USERDEFINED
;
143 if ( strlen(word
) > 3 ) {
144 if ( word
[1] == '{' && word
[strlen(word
)-1] == '}' )
145 return SCE_CMAKE_VARIABLE
;
148 // To check for numbers
149 if ( isCmakeNumber( word
[0] ) ) {
150 bool bHasSimpleCmakeNumber
= true;
151 for (unsigned int j
= 1; j
< end
- start
+ 1 && j
< 99; j
++) {
152 if ( !isCmakeNumber( word
[j
] ) ) {
153 bHasSimpleCmakeNumber
= false;
158 if ( bHasSimpleCmakeNumber
)
159 return SCE_CMAKE_NUMBER
;
162 return SCE_CMAKE_DEFAULT
;
165 static void ColouriseCmakeDoc(Sci_PositionU startPos
, Sci_Position length
, int, WordList
*keywordLists
[], Accessor
&styler
)
167 int state
= SCE_CMAKE_DEFAULT
;
169 state
= styler
.StyleAt(startPos
-1); // Use the style from the previous line, usually default, but could be commentbox
171 styler
.StartAt( startPos
);
172 styler
.GetLine( startPos
);
174 Sci_PositionU nLengthDoc
= startPos
+ length
;
175 styler
.StartSegment( startPos
);
178 bool bVarInString
= false;
179 bool bClassicVarInString
= false;
182 for ( i
= startPos
; i
< nLengthDoc
; i
++ ) {
183 cCurrChar
= styler
.SafeGetCharAt( i
);
184 char cNextChar
= styler
.SafeGetCharAt(i
+1);
187 case SCE_CMAKE_DEFAULT
:
188 if ( cCurrChar
== '#' ) { // we have a comment line
189 styler
.ColourTo(i
-1, state
);
190 state
= SCE_CMAKE_COMMENT
;
193 if ( cCurrChar
== '"' ) {
194 styler
.ColourTo(i
-1, state
);
195 state
= SCE_CMAKE_STRINGDQ
;
196 bVarInString
= false;
197 bClassicVarInString
= false;
200 if ( cCurrChar
== '\'' ) {
201 styler
.ColourTo(i
-1, state
);
202 state
= SCE_CMAKE_STRINGRQ
;
203 bVarInString
= false;
204 bClassicVarInString
= false;
207 if ( cCurrChar
== '`' ) {
208 styler
.ColourTo(i
-1, state
);
209 state
= SCE_CMAKE_STRINGLQ
;
210 bVarInString
= false;
211 bClassicVarInString
= false;
216 if ( cCurrChar
== '$' || isCmakeChar(cCurrChar
)) {
217 styler
.ColourTo(i
-1,state
);
218 state
= SCE_CMAKE_VARIABLE
;
220 // If it is a number, we must check and set style here first...
221 if ( isCmakeNumber(cCurrChar
) && (cNextChar
== '\t' || cNextChar
== ' ' || cNextChar
== '\r' || cNextChar
== '\n' ) )
222 styler
.ColourTo( i
, SCE_CMAKE_NUMBER
);
228 case SCE_CMAKE_COMMENT
:
229 if ( cCurrChar
== '\n' || cCurrChar
== '\r' ) {
230 if ( styler
.SafeGetCharAt(i
-1) == '\\' ) {
231 styler
.ColourTo(i
-2,state
);
232 styler
.ColourTo(i
-1,SCE_CMAKE_DEFAULT
);
235 styler
.ColourTo(i
-1,state
);
236 state
= SCE_CMAKE_DEFAULT
;
240 case SCE_CMAKE_STRINGDQ
:
241 case SCE_CMAKE_STRINGLQ
:
242 case SCE_CMAKE_STRINGRQ
:
244 if ( styler
.SafeGetCharAt(i
-1) == '\\' && styler
.SafeGetCharAt(i
-2) == '$' )
245 break; // Ignore the next character, even if it is a quote of some sort
247 if ( cCurrChar
== '"' && state
== SCE_CMAKE_STRINGDQ
) {
248 styler
.ColourTo(i
,state
);
249 state
= SCE_CMAKE_DEFAULT
;
253 if ( cCurrChar
== '`' && state
== SCE_CMAKE_STRINGLQ
) {
254 styler
.ColourTo(i
,state
);
255 state
= SCE_CMAKE_DEFAULT
;
259 if ( cCurrChar
== '\'' && state
== SCE_CMAKE_STRINGRQ
) {
260 styler
.ColourTo(i
,state
);
261 state
= SCE_CMAKE_DEFAULT
;
265 if ( cNextChar
== '\r' || cNextChar
== '\n' ) {
266 Sci_Position nCurLine
= styler
.GetLine(i
+1);
267 Sci_Position nBack
= i
;
268 // We need to check if the previous line has a \ in it...
269 bool bNextLine
= false;
271 while ( nBack
> 0 ) {
272 if ( styler
.GetLine(nBack
) != nCurLine
)
275 char cTemp
= styler
.SafeGetCharAt(nBack
, 'a'); // Letter 'a' is safe here
277 if ( cTemp
== '\\' ) {
281 if ( cTemp
!= '\r' && cTemp
!= '\n' && cTemp
!= '\t' && cTemp
!= ' ' )
288 styler
.ColourTo(i
+1,state
);
290 if ( bNextLine
== false ) {
291 styler
.ColourTo(i
,state
);
292 state
= SCE_CMAKE_DEFAULT
;
297 case SCE_CMAKE_VARIABLE
:
300 if ( cCurrChar
== '$' )
301 state
= SCE_CMAKE_DEFAULT
;
302 else if ( cCurrChar
== '\\' && (cNextChar
== 'n' || cNextChar
== 'r' || cNextChar
== 't' ) )
303 state
= SCE_CMAKE_DEFAULT
;
304 else if ( (isCmakeChar(cCurrChar
) && !isCmakeChar( cNextChar
) && cNextChar
!= '}') || cCurrChar
== '}' ) {
305 state
= classifyWordCmake( styler
.GetStartSegment(), i
, keywordLists
, styler
);
306 styler
.ColourTo( i
, state
);
307 state
= SCE_CMAKE_DEFAULT
;
309 else if ( !isCmakeChar( cCurrChar
) && cCurrChar
!= '{' && cCurrChar
!= '}' ) {
310 if ( classifyWordCmake( styler
.GetStartSegment(), i
-1, keywordLists
, styler
) == SCE_CMAKE_NUMBER
)
311 styler
.ColourTo( i
-1, SCE_CMAKE_NUMBER
);
313 state
= SCE_CMAKE_DEFAULT
;
315 if ( cCurrChar
== '"' ) {
316 state
= SCE_CMAKE_STRINGDQ
;
317 bVarInString
= false;
318 bClassicVarInString
= false;
320 else if ( cCurrChar
== '`' ) {
321 state
= SCE_CMAKE_STRINGLQ
;
322 bVarInString
= false;
323 bClassicVarInString
= false;
325 else if ( cCurrChar
== '\'' ) {
326 state
= SCE_CMAKE_STRINGRQ
;
327 bVarInString
= false;
328 bClassicVarInString
= false;
330 else if ( cCurrChar
== '#' ) {
331 state
= SCE_CMAKE_COMMENT
;
337 if ( state
== SCE_CMAKE_STRINGDQ
|| state
== SCE_CMAKE_STRINGLQ
|| state
== SCE_CMAKE_STRINGRQ
) {
338 bool bIngoreNextDollarSign
= false;
340 if ( bVarInString
&& cCurrChar
== '$' ) {
341 bVarInString
= false;
342 bIngoreNextDollarSign
= true;
344 else if ( bVarInString
&& cCurrChar
== '\\' && (cNextChar
== 'n' || cNextChar
== 'r' || cNextChar
== 't' || cNextChar
== '"' || cNextChar
== '`' || cNextChar
== '\'' ) ) {
345 styler
.ColourTo( i
+1, SCE_CMAKE_STRINGVAR
);
346 bVarInString
= false;
347 bIngoreNextDollarSign
= false;
350 else if ( bVarInString
&& !isCmakeChar(cNextChar
) ) {
351 int nWordState
= classifyWordCmake( styler
.GetStartSegment(), i
, keywordLists
, styler
);
352 if ( nWordState
== SCE_CMAKE_VARIABLE
)
353 styler
.ColourTo( i
, SCE_CMAKE_STRINGVAR
);
354 bVarInString
= false;
356 // Covers "${TEST}..."
357 else if ( bClassicVarInString
&& cNextChar
== '}' ) {
358 styler
.ColourTo( i
+1, SCE_CMAKE_STRINGVAR
);
359 bClassicVarInString
= false;
362 // Start of var in string
363 if ( !bIngoreNextDollarSign
&& cCurrChar
== '$' && cNextChar
== '{' ) {
364 styler
.ColourTo( i
-1, state
);
365 bClassicVarInString
= true;
366 bVarInString
= false;
368 else if ( !bIngoreNextDollarSign
&& cCurrChar
== '$' ) {
369 styler
.ColourTo( i
-1, state
);
371 bClassicVarInString
= false;
376 // Colourise remaining document
377 styler
.ColourTo(nLengthDoc
-1,state
);
380 static void FoldCmakeDoc(Sci_PositionU startPos
, Sci_Position length
, int, WordList
*[], Accessor
&styler
)
382 // No folding enabled, no reason to continue...
383 if ( styler
.GetPropertyInt("fold") == 0 )
386 bool foldAtElse
= styler
.GetPropertyInt("fold.at.else", 0) == 1;
388 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
389 Sci_PositionU safeStartPos
= styler
.LineStart( lineCurrent
);
392 Sci_Position nWordStart
= -1;
394 int levelCurrent
= SC_FOLDLEVELBASE
;
396 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
397 int levelNext
= levelCurrent
;
399 for (Sci_PositionU i
= safeStartPos
; i
< startPos
+ length
; i
++) {
400 char chCurr
= styler
.SafeGetCharAt(i
);
403 if ( nWordStart
== -1 && (isCmakeLetter(chCurr
)) ) {
406 else if ( isCmakeLetter(chCurr
) == false && nWordStart
> -1 ) {
407 int newLevel
= calculateFoldCmake( nWordStart
, i
-1, levelNext
, styler
, foldAtElse
);
409 if ( newLevel
== levelNext
) {
411 if ( CmakeNextLineHasElse(i
, startPos
+ length
, styler
) )
416 levelNext
= newLevel
;
421 if ( chCurr
== '\n' ) {
422 if ( bArg1
&& foldAtElse
) {
423 if ( CmakeNextLineHasElse(i
, startPos
+ length
, styler
) )
427 // If we are on a new line...
428 int levelUse
= levelCurrent
;
429 int lev
= levelUse
| levelNext
<< 16;
430 if (levelUse
< levelNext
)
431 lev
|= SC_FOLDLEVELHEADERFLAG
;
432 if (lev
!= styler
.LevelAt(lineCurrent
))
433 styler
.SetLevel(lineCurrent
, lev
);
436 levelCurrent
= levelNext
;
437 bArg1
= true; // New line, lets look at first argument again
442 int levelUse
= levelCurrent
;
443 int lev
= levelUse
| levelNext
<< 16;
444 if (levelUse
< levelNext
)
445 lev
|= SC_FOLDLEVELHEADERFLAG
;
446 if (lev
!= styler
.LevelAt(lineCurrent
))
447 styler
.SetLevel(lineCurrent
, lev
);
450 static const char * const cmakeWordLists
[] = {
457 LexerModule
lmCmake(SCLEX_CMAKE
, ColouriseCmakeDoc
, "cmake", FoldCmakeDoc
, cmakeWordLists
);