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