]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexCmake.cxx
1f51f474e80740aad4eedc85ce30da5d9db73f87
[wxWidgets.git] / src / stc / scintilla / src / LexCmake.cxx
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.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13
14 #include "Platform.h"
15
16 #include "CharClassify.h"
17 #include "PropSet.h"
18 #include "Accessor.h"
19 #include "KeyWords.h"
20 #include "Scintilla.h"
21 #include "SciLexer.h"
22
23 #ifdef SCI_NAMESPACE
24 using namespace Scintilla;
25 #endif
26
27 static bool isCmakeNumber(char ch)
28 {
29 return(ch >= '0' && ch <= '9');
30 }
31
32 static bool isCmakeChar(char ch)
33 {
34 return(ch == '.' ) || (ch == '_' ) || isCmakeNumber(ch) || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
35 }
36
37 static bool isCmakeLetter(char ch)
38 {
39 return(ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
40 }
41
42 static bool CmakeNextLineHasElse(unsigned int start, unsigned int end, Accessor &styler)
43 {
44 int nNextLine = -1;
45 for ( unsigned int i = start; i < end; i++ ) {
46 char cNext = styler.SafeGetCharAt( i );
47 if ( cNext == '\n' ) {
48 nNextLine = i+1;
49 break;
50 }
51 }
52
53 if ( nNextLine == -1 ) // We never foudn the next line...
54 return false;
55
56 for ( unsigned int firstChar = nNextLine; firstChar < end; firstChar++ ) {
57 char cNext = styler.SafeGetCharAt( firstChar );
58 if ( cNext == ' ' )
59 continue;
60 if ( cNext == '\t' )
61 continue;
62 if ( styler.Match(firstChar, "ELSE") || styler.Match(firstChar, "else"))
63 return true;
64 break;
65 }
66
67 return false;
68 }
69
70 static int calculateFoldCmake(unsigned int start, unsigned int end, int foldlevel, Accessor &styler, bool bElse)
71 {
72 // If the word is too long, it is not what we are looking for
73 if ( end - start > 20 )
74 return foldlevel;
75
76 int newFoldlevel = foldlevel;
77
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 ] );
81 s[i + 1] = '\0';
82 }
83
84 if ( CompareCaseInsensitive(s, "IF") == 0 || CompareCaseInsensitive(s, "WHILE") == 0
85 || CompareCaseInsensitive(s, "MACRO") == 0 || CompareCaseInsensitive(s, "FOREACH") == 0
86 || CompareCaseInsensitive(s, "ELSEIF") == 0 )
87 newFoldlevel++;
88 else if ( CompareCaseInsensitive(s, "ENDIF") == 0 || CompareCaseInsensitive(s, "ENDWHILE") == 0
89 || CompareCaseInsensitive(s, "ENDMACRO") == 0 || CompareCaseInsensitive(s, "ENDFOREACH") == 0)
90 newFoldlevel--;
91 else if ( bElse && CompareCaseInsensitive(s, "ELSEIF") == 0 )
92 newFoldlevel++;
93 else if ( bElse && CompareCaseInsensitive(s, "ELSE") == 0 )
94 newFoldlevel++;
95
96 return newFoldlevel;
97 }
98
99 static int classifyWordCmake(unsigned int start, unsigned int end, WordList *keywordLists[], Accessor &styler )
100 {
101 char word[100] = {0};
102 char lowercaseWord[100] = {0};
103
104 WordList &Commands = *keywordLists[0];
105 WordList &Parameters = *keywordLists[1];
106 WordList &UserDefined = *keywordLists[2];
107
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]));
111 }
112
113 // Check for special words...
114 if ( CompareCaseInsensitive(word, "MACRO") == 0 || CompareCaseInsensitive(word, "ENDMACRO") == 0 )
115 return SCE_CMAKE_MACRODEF;
116
117 if ( CompareCaseInsensitive(word, "IF") == 0 || CompareCaseInsensitive(word, "ENDIF") == 0 )
118 return SCE_CMAKE_IFDEFINEDEF;
119
120 if ( CompareCaseInsensitive(word, "ELSEIF") == 0 || CompareCaseInsensitive(word, "ELSE") == 0 )
121 return SCE_CMAKE_IFDEFINEDEF;
122
123 if ( CompareCaseInsensitive(word, "WHILE") == 0 || CompareCaseInsensitive(word, "ENDWHILE") == 0)
124 return SCE_CMAKE_WHILEDEF;
125
126 if ( CompareCaseInsensitive(word, "FOREACH") == 0 || CompareCaseInsensitive(word, "ENDFOREACH") == 0)
127 return SCE_CMAKE_FOREACHDEF;
128
129 if ( Commands.InList(lowercaseWord) )
130 return SCE_CMAKE_COMMANDS;
131
132 if ( Parameters.InList(word) )
133 return SCE_CMAKE_PARAMETERS;
134
135
136 if ( UserDefined.InList(word) )
137 return SCE_CMAKE_USERDEFINED;
138
139 if ( strlen(word) > 3 ) {
140 if ( word[1] == '{' && word[strlen(word)-1] == '}' )
141 return SCE_CMAKE_VARIABLE;
142 }
143
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;
150 break;
151 }
152 }
153
154 if ( bHasSimpleCmakeNumber )
155 return SCE_CMAKE_NUMBER;
156 }
157
158 return SCE_CMAKE_DEFAULT;
159 }
160
161 static void ColouriseCmakeDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler)
162 {
163 int state = SCE_CMAKE_DEFAULT;
164 if ( startPos > 0 )
165 state = styler.StyleAt(startPos-1); // Use the style from the previous line, usually default, but could be commentbox
166
167 styler.StartAt( startPos );
168 styler.GetLine( startPos );
169
170 unsigned int nLengthDoc = startPos + length;
171 styler.StartSegment( startPos );
172
173 char cCurrChar;
174 bool bVarInString = false;
175 bool bClassicVarInString = false;
176
177 unsigned int i;
178 for ( i = startPos; i < nLengthDoc; i++ ) {
179 cCurrChar = styler.SafeGetCharAt( i );
180 char cNextChar = styler.SafeGetCharAt(i+1);
181
182 switch (state) {
183 case SCE_CMAKE_DEFAULT:
184 if ( cCurrChar == '#' ) { // we have a comment line
185 styler.ColourTo(i-1, state );
186 state = SCE_CMAKE_COMMENT;
187 break;
188 }
189 if ( cCurrChar == '"' ) {
190 styler.ColourTo(i-1, state );
191 state = SCE_CMAKE_STRINGDQ;
192 bVarInString = false;
193 bClassicVarInString = false;
194 break;
195 }
196 if ( cCurrChar == '\'' ) {
197 styler.ColourTo(i-1, state );
198 state = SCE_CMAKE_STRINGRQ;
199 bVarInString = false;
200 bClassicVarInString = false;
201 break;
202 }
203 if ( cCurrChar == '`' ) {
204 styler.ColourTo(i-1, state );
205 state = SCE_CMAKE_STRINGLQ;
206 bVarInString = false;
207 bClassicVarInString = false;
208 break;
209 }
210
211 // CMake Variable
212 if ( cCurrChar == '$' || isCmakeChar(cCurrChar)) {
213 styler.ColourTo(i-1,state);
214 state = SCE_CMAKE_VARIABLE;
215
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);
219
220 break;
221 }
222
223 break;
224 case SCE_CMAKE_COMMENT:
225 if ( cNextChar == '\n' || cNextChar == '\r' ) {
226 // Special case:
227 if ( cCurrChar == '\\' ) {
228 styler.ColourTo(i-2,state);
229 styler.ColourTo(i,SCE_CMAKE_DEFAULT);
230 }
231 else {
232 styler.ColourTo(i,state);
233 state = SCE_CMAKE_DEFAULT;
234 }
235 }
236 break;
237 case SCE_CMAKE_STRINGDQ:
238 case SCE_CMAKE_STRINGLQ:
239 case SCE_CMAKE_STRINGRQ:
240
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
243
244 if ( cCurrChar == '"' && state == SCE_CMAKE_STRINGDQ ) {
245 styler.ColourTo(i,state);
246 state = SCE_CMAKE_DEFAULT;
247 break;
248 }
249
250 if ( cCurrChar == '`' && state == SCE_CMAKE_STRINGLQ ) {
251 styler.ColourTo(i,state);
252 state = SCE_CMAKE_DEFAULT;
253 break;
254 }
255
256 if ( cCurrChar == '\'' && state == SCE_CMAKE_STRINGRQ ) {
257 styler.ColourTo(i,state);
258 state = SCE_CMAKE_DEFAULT;
259 break;
260 }
261
262 if ( cNextChar == '\r' || cNextChar == '\n' ) {
263 int nCurLine = styler.GetLine(i+1);
264 int nBack = i;
265 // We need to check if the previous line has a \ in it...
266 bool bNextLine = false;
267
268 while ( nBack > 0 ) {
269 if ( styler.GetLine(nBack) != nCurLine )
270 break;
271
272 char cTemp = styler.SafeGetCharAt(nBack, 'a'); // Letter 'a' is safe here
273
274 if ( cTemp == '\\' ) {
275 bNextLine = true;
276 break;
277 }
278 if ( cTemp != '\r' && cTemp != '\n' && cTemp != '\t' && cTemp != ' ' )
279 break;
280
281 nBack--;
282 }
283
284 if ( bNextLine ) {
285 styler.ColourTo(i+1,state);
286 }
287 if ( bNextLine == false ) {
288 styler.ColourTo(i,state);
289 state = SCE_CMAKE_DEFAULT;
290 }
291 }
292 break;
293
294 case SCE_CMAKE_VARIABLE:
295
296 // 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;
305 }
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 );
309
310 state = SCE_CMAKE_DEFAULT;
311
312 if ( cCurrChar == '"' ) {
313 state = SCE_CMAKE_STRINGDQ;
314 bVarInString = false;
315 bClassicVarInString = false;
316 }
317 else if ( cCurrChar == '`' ) {
318 state = SCE_CMAKE_STRINGLQ;
319 bVarInString = false;
320 bClassicVarInString = false;
321 }
322 else if ( cCurrChar == '\'' ) {
323 state = SCE_CMAKE_STRINGRQ;
324 bVarInString = false;
325 bClassicVarInString = false;
326 }
327 else if ( cCurrChar == '#' ) {
328 state = SCE_CMAKE_COMMENT;
329 }
330 }
331 break;
332 }
333
334 if ( state == SCE_CMAKE_COMMENT) {
335 styler.ColourTo(i,state);
336 }
337 else if ( state == SCE_CMAKE_STRINGDQ || state == SCE_CMAKE_STRINGLQ || state == SCE_CMAKE_STRINGRQ ) {
338 bool bIngoreNextDollarSign = false;
339
340 if ( bVarInString && cCurrChar == '$' ) {
341 bVarInString = false;
342 bIngoreNextDollarSign = true;
343 }
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;
348 }
349
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;
355 }
356 // Covers "${TEST}..."
357 else if ( bClassicVarInString && cNextChar == '}' ) {
358 styler.ColourTo( i+1, SCE_CMAKE_STRINGVAR);
359 bClassicVarInString = false;
360 }
361
362 // Start of var in string
363 if ( !bIngoreNextDollarSign && cCurrChar == '$' && cNextChar == '{' ) {
364 styler.ColourTo( i-1, state);
365 bClassicVarInString = true;
366 bVarInString = false;
367 }
368 else if ( !bIngoreNextDollarSign && cCurrChar == '$' ) {
369 styler.ColourTo( i-1, state);
370 bVarInString = true;
371 bClassicVarInString = false;
372 }
373 }
374 }
375
376 // Colourise remaining document
377 styler.ColourTo(nLengthDoc-1,state);
378 }
379
380 static void FoldCmakeDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler)
381 {
382 // No folding enabled, no reason to continue...
383 if ( styler.GetPropertyInt("fold") == 0 )
384 return;
385
386 bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) == 1;
387
388 int lineCurrent = styler.GetLine(startPos);
389 unsigned int safeStartPos = styler.LineStart( lineCurrent );
390
391 bool bArg1 = true;
392 int nWordStart = -1;
393
394 int levelCurrent = SC_FOLDLEVELBASE;
395 if (lineCurrent > 0)
396 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
397 int levelNext = levelCurrent;
398
399 for (unsigned int i = safeStartPos; i < startPos + length; i++) {
400 char chCurr = styler.SafeGetCharAt(i);
401
402 if ( bArg1 ) {
403 if ( nWordStart == -1 && (isCmakeLetter(chCurr)) ) {
404 nWordStart = i;
405 }
406 else if ( isCmakeLetter(chCurr) == false && nWordStart > -1 ) {
407 int newLevel = calculateFoldCmake( nWordStart, i-1, levelNext, styler, foldAtElse);
408
409 if ( newLevel == levelNext ) {
410 if ( foldAtElse ) {
411 if ( CmakeNextLineHasElse(i, startPos + length, styler) )
412 levelNext--;
413 }
414 }
415 else
416 levelNext = newLevel;
417 bArg1 = false;
418 }
419 }
420
421 if ( chCurr == '\n' ) {
422 if ( bArg1 && foldAtElse) {
423 if ( CmakeNextLineHasElse(i, startPos + length, styler) )
424 levelNext--;
425 }
426
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);
434
435 lineCurrent++;
436 levelCurrent = levelNext;
437 bArg1 = true; // New line, lets look at first argument again
438 nWordStart = -1;
439 }
440 }
441
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);
448 }
449
450 static const char * const cmakeWordLists[] = {
451 "Commands",
452 "Parameters",
453 "UserDefined",
454 0,
455 0,};
456
457 LexerModule lmCmake(SCLEX_CMAKE, ColouriseCmakeDoc, "cmake", FoldCmakeDoc, cmakeWordLists);