Fix a typo in NEWS
[geany-mirror.git] / scintilla / lexers / LexCmake.cxx
blob1cbca361135579255d0dbd8d52b6c71b14924775
1 // Scintilla source code edit control
2 /** @file LexCmake.cxx
3 ** Lexer for Cmake
4 **/
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.
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include <ctype.h>
16 #include "ILexer.h"
17 #include "Scintilla.h"
18 #include "SciLexer.h"
20 #include "WordList.h"
21 #include "LexAccessor.h"
22 #include "Accessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
27 #ifdef SCI_NAMESPACE
28 using namespace Scintilla;
29 #endif
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' ) {
52 nNextLine = i+1;
53 break;
57 if ( nNextLine == -1 ) // We never foudn the next line...
58 return false;
60 for ( Sci_PositionU firstChar = nNextLine; firstChar < end; firstChar++ ) {
61 char cNext = styler.SafeGetCharAt( firstChar );
62 if ( cNext == ' ' )
63 continue;
64 if ( cNext == '\t' )
65 continue;
66 if ( styler.Match(firstChar, "ELSE") || styler.Match(firstChar, "else"))
67 return true;
68 break;
71 return false;
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 )
78 return foldlevel;
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 ] );
85 s[i + 1] = '\0';
88 if ( CompareCaseInsensitive(s, "IF") == 0 || CompareCaseInsensitive(s, "WHILE") == 0
89 || CompareCaseInsensitive(s, "MACRO") == 0 || CompareCaseInsensitive(s, "FOREACH") == 0
90 || CompareCaseInsensitive(s, "ELSEIF") == 0 )
91 newFoldlevel++;
92 else if ( CompareCaseInsensitive(s, "ENDIF") == 0 || CompareCaseInsensitive(s, "ENDWHILE") == 0
93 || CompareCaseInsensitive(s, "ENDMACRO") == 0 || CompareCaseInsensitive(s, "ENDFOREACH") == 0)
94 newFoldlevel--;
95 else if ( bElse && CompareCaseInsensitive(s, "ELSEIF") == 0 )
96 newFoldlevel++;
97 else if ( bElse && CompareCaseInsensitive(s, "ELSE") == 0 )
98 newFoldlevel++;
100 return newFoldlevel;
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;
154 break;
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;
168 if ( startPos > 0 )
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 );
177 char cCurrChar;
178 bool bVarInString = false;
179 bool bClassicVarInString = false;
181 Sci_PositionU i;
182 for ( i = startPos; i < nLengthDoc; i++ ) {
183 cCurrChar = styler.SafeGetCharAt( i );
184 char cNextChar = styler.SafeGetCharAt(i+1);
186 switch (state) {
187 case SCE_CMAKE_DEFAULT:
188 if ( cCurrChar == '#' ) { // we have a comment line
189 styler.ColourTo(i-1, state );
190 state = SCE_CMAKE_COMMENT;
191 break;
193 if ( cCurrChar == '"' ) {
194 styler.ColourTo(i-1, state );
195 state = SCE_CMAKE_STRINGDQ;
196 bVarInString = false;
197 bClassicVarInString = false;
198 break;
200 if ( cCurrChar == '\'' ) {
201 styler.ColourTo(i-1, state );
202 state = SCE_CMAKE_STRINGRQ;
203 bVarInString = false;
204 bClassicVarInString = false;
205 break;
207 if ( cCurrChar == '`' ) {
208 styler.ColourTo(i-1, state );
209 state = SCE_CMAKE_STRINGLQ;
210 bVarInString = false;
211 bClassicVarInString = false;
212 break;
215 // CMake Variable
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);
224 break;
227 break;
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);
234 else {
235 styler.ColourTo(i-1,state);
236 state = SCE_CMAKE_DEFAULT;
239 break;
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;
250 break;
253 if ( cCurrChar == '`' && state == SCE_CMAKE_STRINGLQ ) {
254 styler.ColourTo(i,state);
255 state = SCE_CMAKE_DEFAULT;
256 break;
259 if ( cCurrChar == '\'' && state == SCE_CMAKE_STRINGRQ ) {
260 styler.ColourTo(i,state);
261 state = SCE_CMAKE_DEFAULT;
262 break;
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 )
273 break;
275 char cTemp = styler.SafeGetCharAt(nBack, 'a'); // Letter 'a' is safe here
277 if ( cTemp == '\\' ) {
278 bNextLine = true;
279 break;
281 if ( cTemp != '\r' && cTemp != '\n' && cTemp != '\t' && cTemp != ' ' )
282 break;
284 nBack--;
287 if ( bNextLine ) {
288 styler.ColourTo(i+1,state);
290 if ( bNextLine == false ) {
291 styler.ColourTo(i,state);
292 state = SCE_CMAKE_DEFAULT;
295 break;
297 case SCE_CMAKE_VARIABLE:
299 // 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;
334 break;
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);
370 bVarInString = true;
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 )
384 return;
386 bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) == 1;
388 Sci_Position lineCurrent = styler.GetLine(startPos);
389 Sci_PositionU safeStartPos = styler.LineStart( lineCurrent );
391 bool bArg1 = true;
392 Sci_Position nWordStart = -1;
394 int levelCurrent = SC_FOLDLEVELBASE;
395 if (lineCurrent > 0)
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);
402 if ( bArg1 ) {
403 if ( nWordStart == -1 && (isCmakeLetter(chCurr)) ) {
404 nWordStart = i;
406 else if ( isCmakeLetter(chCurr) == false && nWordStart > -1 ) {
407 int newLevel = calculateFoldCmake( nWordStart, i-1, levelNext, styler, foldAtElse);
409 if ( newLevel == levelNext ) {
410 if ( foldAtElse ) {
411 if ( CmakeNextLineHasElse(i, startPos + length, styler) )
412 levelNext--;
415 else
416 levelNext = newLevel;
417 bArg1 = false;
421 if ( chCurr == '\n' ) {
422 if ( bArg1 && foldAtElse) {
423 if ( CmakeNextLineHasElse(i, startPos + length, styler) )
424 levelNext--;
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);
435 lineCurrent++;
436 levelCurrent = levelNext;
437 bArg1 = true; // New line, lets look at first argument again
438 nWordStart = -1;
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[] = {
451 "Commands",
452 "Parameters",
453 "UserDefined",
455 0,};
457 LexerModule lmCmake(SCLEX_CMAKE, ColouriseCmakeDoc, "cmake", FoldCmakeDoc, cmakeWordLists);