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.
16 #include "CharClassify.h"
20 #include "Scintilla.h"
24 using namespace Scintilla
;
27 static bool isCmakeNumber(char ch
)
29 return(ch
>= '0' && ch
<= '9');
32 static bool isCmakeChar(char ch
)
34 return(ch
== '.' ) || (ch
== '_' ) || isCmakeNumber(ch
) || (ch
>= 'A' && ch
<= 'Z') || (ch
>= 'a' && ch
<= 'z');
37 static bool isCmakeLetter(char ch
)
39 return(ch
>= 'A' && ch
<= 'Z') || (ch
>= 'a' && ch
<= 'z');
42 static bool CmakeNextLineHasElse(unsigned int start
, unsigned int end
, Accessor
&styler
)
45 for ( unsigned int i
= start
; i
< end
; i
++ ) {
46 char cNext
= styler
.SafeGetCharAt( i
);
47 if ( cNext
== '\n' ) {
53 if ( nNextLine
== -1 ) // We never foudn the next line...
56 for ( unsigned int firstChar
= nNextLine
; firstChar
< end
; firstChar
++ ) {
57 char cNext
= styler
.SafeGetCharAt( firstChar
);
62 if ( styler
.Match(firstChar
, "ELSE") || styler
.Match(firstChar
, "else"))
70 static int calculateFoldCmake(unsigned int start
, unsigned int end
, int foldlevel
, Accessor
&styler
, bool bElse
)
72 // If the word is too long, it is not what we are looking for
73 if ( end
- start
> 20 )
76 int newFoldlevel
= foldlevel
;
78 char s
[20]; // The key word we are looking for has atmost 13 characters
79 for (unsigned int i
= 0; i
< end
- start
+ 1 && i
< 19; i
++) {
80 s
[i
] = static_cast<char>( styler
[ start
+ i
] );
84 if ( CompareCaseInsensitive(s
, "IF") == 0 || CompareCaseInsensitive(s
, "WHILE") == 0
85 || CompareCaseInsensitive(s
, "MACRO") == 0 || CompareCaseInsensitive(s
, "FOREACH") == 0
86 || CompareCaseInsensitive(s
, "ELSEIF") == 0 )
88 else if ( CompareCaseInsensitive(s
, "ENDIF") == 0 || CompareCaseInsensitive(s
, "ENDWHILE") == 0
89 || CompareCaseInsensitive(s
, "ENDMACRO") == 0 || CompareCaseInsensitive(s
, "ENDFOREACH") == 0)
91 else if ( bElse
&& CompareCaseInsensitive(s
, "ELSEIF") == 0 )
93 else if ( bElse
&& CompareCaseInsensitive(s
, "ELSE") == 0 )
99 static int classifyWordCmake(unsigned int start
, unsigned int end
, WordList
*keywordLists
[], Accessor
&styler
)
101 char word
[100] = {0};
102 char lowercaseWord
[100] = {0};
104 WordList
&Commands
= *keywordLists
[0];
105 WordList
&Parameters
= *keywordLists
[1];
106 WordList
&UserDefined
= *keywordLists
[2];
108 for (unsigned int i
= 0; i
< end
- start
+ 1 && i
< 99; i
++) {
109 word
[i
] = static_cast<char>( styler
[ start
+ i
] );
110 lowercaseWord
[i
] = static_cast<char>(tolower(word
[i
]));
113 // Check for special words...
114 if ( CompareCaseInsensitive(word
, "MACRO") == 0 || CompareCaseInsensitive(word
, "ENDMACRO") == 0 )
115 return SCE_CMAKE_MACRODEF
;
117 if ( CompareCaseInsensitive(word
, "IF") == 0 || CompareCaseInsensitive(word
, "ENDIF") == 0 )
118 return SCE_CMAKE_IFDEFINEDEF
;
120 if ( CompareCaseInsensitive(word
, "ELSEIF") == 0 || CompareCaseInsensitive(word
, "ELSE") == 0 )
121 return SCE_CMAKE_IFDEFINEDEF
;
123 if ( CompareCaseInsensitive(word
, "WHILE") == 0 || CompareCaseInsensitive(word
, "ENDWHILE") == 0)
124 return SCE_CMAKE_WHILEDEF
;
126 if ( CompareCaseInsensitive(word
, "FOREACH") == 0 || CompareCaseInsensitive(word
, "ENDFOREACH") == 0)
127 return SCE_CMAKE_FOREACHDEF
;
129 if ( Commands
.InList(lowercaseWord
) )
130 return SCE_CMAKE_COMMANDS
;
132 if ( Parameters
.InList(word
) )
133 return SCE_CMAKE_PARAMETERS
;
136 if ( UserDefined
.InList(word
) )
137 return SCE_CMAKE_USERDEFINED
;
139 if ( strlen(word
) > 3 ) {
140 if ( word
[1] == '{' && word
[strlen(word
)-1] == '}' )
141 return SCE_CMAKE_VARIABLE
;
144 // To check for numbers
145 if ( isCmakeNumber( word
[0] ) ) {
146 bool bHasSimpleCmakeNumber
= true;
147 for (unsigned int j
= 1; j
< end
- start
+ 1 && j
< 99; j
++) {
148 if ( !isCmakeNumber( word
[j
] ) ) {
149 bHasSimpleCmakeNumber
= false;
154 if ( bHasSimpleCmakeNumber
)
155 return SCE_CMAKE_NUMBER
;
158 return SCE_CMAKE_DEFAULT
;
161 static void ColouriseCmakeDoc(unsigned int startPos
, int length
, int, WordList
*keywordLists
[], Accessor
&styler
)
163 int state
= SCE_CMAKE_DEFAULT
;
165 state
= styler
.StyleAt(startPos
-1); // Use the style from the previous line, usually default, but could be commentbox
167 styler
.StartAt( startPos
);
168 styler
.GetLine( startPos
);
170 unsigned int nLengthDoc
= startPos
+ length
;
171 styler
.StartSegment( startPos
);
174 bool bVarInString
= false;
175 bool bClassicVarInString
= false;
178 for ( i
= startPos
; i
< nLengthDoc
; i
++ ) {
179 cCurrChar
= styler
.SafeGetCharAt( i
);
180 char cNextChar
= styler
.SafeGetCharAt(i
+1);
183 case SCE_CMAKE_DEFAULT
:
184 if ( cCurrChar
== '#' ) { // we have a comment line
185 styler
.ColourTo(i
-1, state
);
186 state
= SCE_CMAKE_COMMENT
;
189 if ( cCurrChar
== '"' ) {
190 styler
.ColourTo(i
-1, state
);
191 state
= SCE_CMAKE_STRINGDQ
;
192 bVarInString
= false;
193 bClassicVarInString
= false;
196 if ( cCurrChar
== '\'' ) {
197 styler
.ColourTo(i
-1, state
);
198 state
= SCE_CMAKE_STRINGRQ
;
199 bVarInString
= false;
200 bClassicVarInString
= false;
203 if ( cCurrChar
== '`' ) {
204 styler
.ColourTo(i
-1, state
);
205 state
= SCE_CMAKE_STRINGLQ
;
206 bVarInString
= false;
207 bClassicVarInString
= false;
212 if ( cCurrChar
== '$' || isCmakeChar(cCurrChar
)) {
213 styler
.ColourTo(i
-1,state
);
214 state
= SCE_CMAKE_VARIABLE
;
216 // If it is a number, we must check and set style here first...
217 if ( isCmakeNumber(cCurrChar
) && (cNextChar
== '\t' || cNextChar
== ' ' || cNextChar
== '\r' || cNextChar
== '\n' ) )
218 styler
.ColourTo( i
, SCE_CMAKE_NUMBER
);
224 case SCE_CMAKE_COMMENT
:
225 if ( cNextChar
== '\n' || cNextChar
== '\r' ) {
227 if ( cCurrChar
== '\\' ) {
228 styler
.ColourTo(i
-2,state
);
229 styler
.ColourTo(i
,SCE_CMAKE_DEFAULT
);
232 styler
.ColourTo(i
,state
);
233 state
= SCE_CMAKE_DEFAULT
;
237 case SCE_CMAKE_STRINGDQ
:
238 case SCE_CMAKE_STRINGLQ
:
239 case SCE_CMAKE_STRINGRQ
:
241 if ( styler
.SafeGetCharAt(i
-1) == '\\' && styler
.SafeGetCharAt(i
-2) == '$' )
242 break; // Ignore the next character, even if it is a quote of some sort
244 if ( cCurrChar
== '"' && state
== SCE_CMAKE_STRINGDQ
) {
245 styler
.ColourTo(i
,state
);
246 state
= SCE_CMAKE_DEFAULT
;
250 if ( cCurrChar
== '`' && state
== SCE_CMAKE_STRINGLQ
) {
251 styler
.ColourTo(i
,state
);
252 state
= SCE_CMAKE_DEFAULT
;
256 if ( cCurrChar
== '\'' && state
== SCE_CMAKE_STRINGRQ
) {
257 styler
.ColourTo(i
,state
);
258 state
= SCE_CMAKE_DEFAULT
;
262 if ( cNextChar
== '\r' || cNextChar
== '\n' ) {
263 int nCurLine
= styler
.GetLine(i
+1);
265 // We need to check if the previous line has a \ in it...
266 bool bNextLine
= false;
268 while ( nBack
> 0 ) {
269 if ( styler
.GetLine(nBack
) != nCurLine
)
272 char cTemp
= styler
.SafeGetCharAt(nBack
, 'a'); // Letter 'a' is safe here
274 if ( cTemp
== '\\' ) {
278 if ( cTemp
!= '\r' && cTemp
!= '\n' && cTemp
!= '\t' && cTemp
!= ' ' )
285 styler
.ColourTo(i
+1,state
);
287 if ( bNextLine
== false ) {
288 styler
.ColourTo(i
,state
);
289 state
= SCE_CMAKE_DEFAULT
;
294 case SCE_CMAKE_VARIABLE
:
297 if ( cCurrChar
== '$' )
298 state
= SCE_CMAKE_DEFAULT
;
299 else if ( cCurrChar
== '\\' && (cNextChar
== 'n' || cNextChar
== 'r' || cNextChar
== 't' ) )
300 state
= SCE_CMAKE_DEFAULT
;
301 else if ( (isCmakeChar(cCurrChar
) && !isCmakeChar( cNextChar
) && cNextChar
!= '}') || cCurrChar
== '}' ) {
302 state
= classifyWordCmake( styler
.GetStartSegment(), i
, keywordLists
, styler
);
303 styler
.ColourTo( i
, state
);
304 state
= SCE_CMAKE_DEFAULT
;
306 else if ( !isCmakeChar( cCurrChar
) && cCurrChar
!= '{' && cCurrChar
!= '}' ) {
307 if ( classifyWordCmake( styler
.GetStartSegment(), i
-1, keywordLists
, styler
) == SCE_CMAKE_NUMBER
)
308 styler
.ColourTo( i
-1, SCE_CMAKE_NUMBER
);
310 state
= SCE_CMAKE_DEFAULT
;
312 if ( cCurrChar
== '"' ) {
313 state
= SCE_CMAKE_STRINGDQ
;
314 bVarInString
= false;
315 bClassicVarInString
= false;
317 else if ( cCurrChar
== '`' ) {
318 state
= SCE_CMAKE_STRINGLQ
;
319 bVarInString
= false;
320 bClassicVarInString
= false;
322 else if ( cCurrChar
== '\'' ) {
323 state
= SCE_CMAKE_STRINGRQ
;
324 bVarInString
= false;
325 bClassicVarInString
= false;
327 else if ( cCurrChar
== '#' ) {
328 state
= SCE_CMAKE_COMMENT
;
334 if ( state
== SCE_CMAKE_COMMENT
) {
335 styler
.ColourTo(i
,state
);
337 else 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(unsigned int startPos
, int 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 int lineCurrent
= styler
.GetLine(startPos
);
389 unsigned int safeStartPos
= styler
.LineStart( lineCurrent
);
394 int levelCurrent
= SC_FOLDLEVELBASE
;
396 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
397 int levelNext
= levelCurrent
;
399 for (unsigned int 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
);