updated Scintilla to 2.29
[TortoiseGit.git] / ext / scintilla / lexers / LexNsis.cxx
blob35c5cdb4f566ac214623d9d6a472d0fcc5c7deca
1 // Scintilla source code edit control
2 /** @file LexNsis.cxx
3 ** Lexer for NSIS
4 **/
5 // Copyright 2003 - 2005 by Angelo Mandato <angelo [at] spaceblue [dot] com>
6 // Last Updated: 03/13/2005
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
32 // located in SciLexer.h
33 #define SCLEX_NSIS 43
35 #define SCE_NSIS_DEFAULT 0
36 #define SCE_NSIS_COMMENT 1
37 #define SCE_NSIS_STRINGDQ 2
38 #define SCE_NSIS_STRINGLQ 3
39 #define SCE_NSIS_STRINGRQ 4
40 #define SCE_NSIS_FUNCTION 5
41 #define SCE_NSIS_VARIABLE 6
42 #define SCE_NSIS_LABEL 7
43 #define SCE_NSIS_USERDEFINED 8
44 #define SCE_NSIS_SECTIONDEF 9
45 #define SCE_NSIS_SUBSECTIONDEF 10
46 #define SCE_NSIS_IFDEFINEDEF 11
47 #define SCE_NSIS_MACRODEF 12
48 #define SCE_NSIS_STRINGVAR 13
49 #define SCE_NSIS_NUMBER 14
50 // ADDED for Scintilla v1.63
51 #define SCE_NSIS_SECTIONGROUP 15
52 #define SCE_NSIS_PAGEEX 16
53 #define SCE_NSIS_FUNCTIONDEF 17
54 #define SCE_NSIS_COMMENTBOX 18
57 static bool isNsisNumber(char ch)
59 return (ch >= '0' && ch <= '9');
62 static bool isNsisChar(char ch)
64 return (ch == '.' ) || (ch == '_' ) || isNsisNumber(ch) || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
67 static bool isNsisLetter(char ch)
69 return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
72 static bool NsisNextLineHasElse(unsigned int start, unsigned int end, Accessor &styler)
74 int nNextLine = -1;
75 for( unsigned int i = start; i < end; i++ )
77 char cNext = styler.SafeGetCharAt( i );
78 if( cNext == '\n' )
80 nNextLine = i+1;
81 break;
85 if( nNextLine == -1 ) // We never found the next line...
86 return false;
88 for( unsigned int firstChar = nNextLine; firstChar < end; firstChar++ )
90 char cNext = styler.SafeGetCharAt( firstChar );
91 if( cNext == ' ' )
92 continue;
93 if( cNext == '\t' )
94 continue;
95 if( cNext == '!' )
97 if( styler.Match(firstChar, "!else") )
98 return true;
100 break;
103 return false;
106 static int NsisCmp( const char *s1, const char *s2, bool bIgnoreCase )
108 if( bIgnoreCase )
109 return CompareCaseInsensitive( s1, s2);
111 return strcmp( s1, s2 );
114 static int calculateFoldNsis(unsigned int start, unsigned int end, int foldlevel, Accessor &styler, bool bElse, bool foldUtilityCmd )
116 int style = styler.StyleAt(end);
118 // If the word is too long, it is not what we are looking for
119 if( end - start > 20 )
120 return foldlevel;
122 if( foldUtilityCmd )
124 // Check the style at this point, if it is not valid, then return zero
125 if( style != SCE_NSIS_FUNCTIONDEF && style != SCE_NSIS_SECTIONDEF &&
126 style != SCE_NSIS_SUBSECTIONDEF && style != SCE_NSIS_IFDEFINEDEF &&
127 style != SCE_NSIS_MACRODEF && style != SCE_NSIS_SECTIONGROUP &&
128 style != SCE_NSIS_PAGEEX )
129 return foldlevel;
131 else
133 if( style != SCE_NSIS_FUNCTIONDEF && style != SCE_NSIS_SECTIONDEF &&
134 style != SCE_NSIS_SUBSECTIONDEF && style != SCE_NSIS_SECTIONGROUP &&
135 style != SCE_NSIS_PAGEEX )
136 return foldlevel;
139 int newFoldlevel = foldlevel;
140 bool bIgnoreCase = false;
141 if( styler.GetPropertyInt("nsis.ignorecase") == 1 )
142 bIgnoreCase = true;
144 char s[20]; // The key word we are looking for has atmost 13 characters
145 for (unsigned int i = 0; i < end - start + 1 && i < 19; i++)
147 s[i] = static_cast<char>( styler[ start + i ] );
148 s[i + 1] = '\0';
151 if( s[0] == '!' )
153 if( NsisCmp(s, "!ifndef", bIgnoreCase) == 0 || NsisCmp(s, "!ifdef", bIgnoreCase ) == 0 || NsisCmp(s, "!ifmacrodef", bIgnoreCase ) == 0 || NsisCmp(s, "!ifmacrondef", bIgnoreCase ) == 0 || NsisCmp(s, "!if", bIgnoreCase ) == 0 || NsisCmp(s, "!macro", bIgnoreCase ) == 0 )
154 newFoldlevel++;
155 else if( NsisCmp(s, "!endif", bIgnoreCase) == 0 || NsisCmp(s, "!macroend", bIgnoreCase ) == 0 )
156 newFoldlevel--;
157 else if( bElse && NsisCmp(s, "!else", bIgnoreCase) == 0 )
158 newFoldlevel++;
160 else
162 if( NsisCmp(s, "Section", bIgnoreCase ) == 0 || NsisCmp(s, "SectionGroup", bIgnoreCase ) == 0 || NsisCmp(s, "Function", bIgnoreCase) == 0 || NsisCmp(s, "SubSection", bIgnoreCase ) == 0 || NsisCmp(s, "PageEx", bIgnoreCase ) == 0 )
163 newFoldlevel++;
164 else if( NsisCmp(s, "SectionGroupEnd", bIgnoreCase ) == 0 || NsisCmp(s, "SubSectionEnd", bIgnoreCase ) == 0 || NsisCmp(s, "FunctionEnd", bIgnoreCase) == 0 || NsisCmp(s, "SectionEnd", bIgnoreCase ) == 0 || NsisCmp(s, "PageExEnd", bIgnoreCase ) == 0 )
165 newFoldlevel--;
168 return newFoldlevel;
171 static int classifyWordNsis(unsigned int start, unsigned int end, WordList *keywordLists[], Accessor &styler )
173 bool bIgnoreCase = false;
174 if( styler.GetPropertyInt("nsis.ignorecase") == 1 )
175 bIgnoreCase = true;
177 bool bUserVars = false;
178 if( styler.GetPropertyInt("nsis.uservars") == 1 )
179 bUserVars = true;
181 char s[100];
183 WordList &Functions = *keywordLists[0];
184 WordList &Variables = *keywordLists[1];
185 WordList &Lables = *keywordLists[2];
186 WordList &UserDefined = *keywordLists[3];
188 for (unsigned int i = 0; i < end - start + 1 && i < 99; i++)
190 if( bIgnoreCase )
191 s[i] = static_cast<char>( tolower(styler[ start + i ] ) );
192 else
193 s[i] = static_cast<char>( styler[ start + i ] );
194 s[i + 1] = '\0';
197 // Check for special words...
198 if( NsisCmp(s, "!macro", bIgnoreCase ) == 0 || NsisCmp(s, "!macroend", bIgnoreCase) == 0 ) // Covers !macro and !macroend
199 return SCE_NSIS_MACRODEF;
201 if( NsisCmp(s, "!ifdef", bIgnoreCase ) == 0 || NsisCmp(s, "!ifndef", bIgnoreCase) == 0 || NsisCmp(s, "!endif", bIgnoreCase) == 0 ) // Covers !ifdef, !ifndef and !endif
202 return SCE_NSIS_IFDEFINEDEF;
204 if( NsisCmp(s, "!if", bIgnoreCase ) == 0 || NsisCmp(s, "!else", bIgnoreCase ) == 0 ) // Covers !if and else
205 return SCE_NSIS_IFDEFINEDEF;
207 if (NsisCmp(s, "!ifmacrodef", bIgnoreCase ) == 0 || NsisCmp(s, "!ifmacrondef", bIgnoreCase ) == 0 ) // Covers !ifmacrodef and !ifnmacrodef
208 return SCE_NSIS_IFDEFINEDEF;
210 if( NsisCmp(s, "SectionGroup", bIgnoreCase) == 0 || NsisCmp(s, "SectionGroupEnd", bIgnoreCase) == 0 ) // Covers SectionGroup and SectionGroupEnd
211 return SCE_NSIS_SECTIONGROUP;
213 if( NsisCmp(s, "Section", bIgnoreCase ) == 0 || NsisCmp(s, "SectionEnd", bIgnoreCase) == 0 ) // Covers Section and SectionEnd
214 return SCE_NSIS_SECTIONDEF;
216 if( NsisCmp(s, "SubSection", bIgnoreCase) == 0 || NsisCmp(s, "SubSectionEnd", bIgnoreCase) == 0 ) // Covers SubSection and SubSectionEnd
217 return SCE_NSIS_SUBSECTIONDEF;
219 if( NsisCmp(s, "PageEx", bIgnoreCase) == 0 || NsisCmp(s, "PageExEnd", bIgnoreCase) == 0 ) // Covers PageEx and PageExEnd
220 return SCE_NSIS_PAGEEX;
222 if( NsisCmp(s, "Function", bIgnoreCase) == 0 || NsisCmp(s, "FunctionEnd", bIgnoreCase) == 0 ) // Covers Function and FunctionEnd
223 return SCE_NSIS_FUNCTIONDEF;
225 if ( Functions.InList(s) )
226 return SCE_NSIS_FUNCTION;
228 if ( Variables.InList(s) )
229 return SCE_NSIS_VARIABLE;
231 if ( Lables.InList(s) )
232 return SCE_NSIS_LABEL;
234 if( UserDefined.InList(s) )
235 return SCE_NSIS_USERDEFINED;
237 if( strlen(s) > 3 )
239 if( s[1] == '{' && s[strlen(s)-1] == '}' )
240 return SCE_NSIS_VARIABLE;
243 // See if the variable is a user defined variable
244 if( s[0] == '$' && bUserVars )
246 bool bHasSimpleNsisChars = true;
247 for (unsigned int j = 1; j < end - start + 1 && j < 99; j++)
249 if( !isNsisChar( s[j] ) )
251 bHasSimpleNsisChars = false;
252 break;
256 if( bHasSimpleNsisChars )
257 return SCE_NSIS_VARIABLE;
260 // To check for numbers
261 if( isNsisNumber( s[0] ) )
263 bool bHasSimpleNsisNumber = true;
264 for (unsigned int j = 1; j < end - start + 1 && j < 99; j++)
266 if( !isNsisNumber( s[j] ) )
268 bHasSimpleNsisNumber = false;
269 break;
273 if( bHasSimpleNsisNumber )
274 return SCE_NSIS_NUMBER;
277 return SCE_NSIS_DEFAULT;
280 static void ColouriseNsisDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler)
282 int state = SCE_NSIS_DEFAULT;
283 if( startPos > 0 )
284 state = styler.StyleAt(startPos-1); // Use the style from the previous line, usually default, but could be commentbox
286 styler.StartAt( startPos );
287 styler.GetLine( startPos );
289 unsigned int nLengthDoc = startPos + length;
290 styler.StartSegment( startPos );
292 char cCurrChar;
293 bool bVarInString = false;
294 bool bClassicVarInString = false;
296 unsigned int i;
297 for( i = startPos; i < nLengthDoc; i++ )
299 cCurrChar = styler.SafeGetCharAt( i );
300 char cNextChar = styler.SafeGetCharAt(i+1);
302 switch(state)
304 case SCE_NSIS_DEFAULT:
305 if( cCurrChar == ';' || cCurrChar == '#' ) // we have a comment line
307 styler.ColourTo(i-1, state );
308 state = SCE_NSIS_COMMENT;
309 break;
311 if( cCurrChar == '"' )
313 styler.ColourTo(i-1, state );
314 state = SCE_NSIS_STRINGDQ;
315 bVarInString = false;
316 bClassicVarInString = false;
317 break;
319 if( cCurrChar == '\'' )
321 styler.ColourTo(i-1, state );
322 state = SCE_NSIS_STRINGRQ;
323 bVarInString = false;
324 bClassicVarInString = false;
325 break;
327 if( cCurrChar == '`' )
329 styler.ColourTo(i-1, state );
330 state = SCE_NSIS_STRINGLQ;
331 bVarInString = false;
332 bClassicVarInString = false;
333 break;
336 // NSIS KeyWord,Function, Variable, UserDefined:
337 if( cCurrChar == '$' || isNsisChar(cCurrChar) || cCurrChar == '!' )
339 styler.ColourTo(i-1,state);
340 state = SCE_NSIS_FUNCTION;
342 // If it is a number, we must check and set style here first...
343 if( isNsisNumber(cCurrChar) && (cNextChar == '\t' || cNextChar == ' ' || cNextChar == '\r' || cNextChar == '\n' ) )
344 styler.ColourTo( i, SCE_NSIS_NUMBER);
346 break;
349 if( cCurrChar == '/' && cNextChar == '*' )
351 styler.ColourTo(i-1,state);
352 state = SCE_NSIS_COMMENTBOX;
353 break;
356 break;
357 case SCE_NSIS_COMMENT:
358 if( cNextChar == '\n' || cNextChar == '\r' )
360 // Special case:
361 if( cCurrChar == '\\' )
363 styler.ColourTo(i-2,state);
364 styler.ColourTo(i,SCE_NSIS_DEFAULT);
366 else
368 styler.ColourTo(i,state);
369 state = SCE_NSIS_DEFAULT;
372 break;
373 case SCE_NSIS_STRINGDQ:
374 case SCE_NSIS_STRINGLQ:
375 case SCE_NSIS_STRINGRQ:
377 if( styler.SafeGetCharAt(i-1) == '\\' && styler.SafeGetCharAt(i-2) == '$' )
378 break; // Ignore the next character, even if it is a quote of some sort
380 if( cCurrChar == '"' && state == SCE_NSIS_STRINGDQ )
382 styler.ColourTo(i,state);
383 state = SCE_NSIS_DEFAULT;
384 break;
387 if( cCurrChar == '`' && state == SCE_NSIS_STRINGLQ )
389 styler.ColourTo(i,state);
390 state = SCE_NSIS_DEFAULT;
391 break;
394 if( cCurrChar == '\'' && state == SCE_NSIS_STRINGRQ )
396 styler.ColourTo(i,state);
397 state = SCE_NSIS_DEFAULT;
398 break;
401 if( cNextChar == '\r' || cNextChar == '\n' )
403 int nCurLine = styler.GetLine(i+1);
404 int nBack = i;
405 // We need to check if the previous line has a \ in it...
406 bool bNextLine = false;
408 while( nBack > 0 )
410 if( styler.GetLine(nBack) != nCurLine )
411 break;
413 char cTemp = styler.SafeGetCharAt(nBack, 'a'); // Letter 'a' is safe here
415 if( cTemp == '\\' )
417 bNextLine = true;
418 break;
420 if( cTemp != '\r' && cTemp != '\n' && cTemp != '\t' && cTemp != ' ' )
421 break;
423 nBack--;
426 if( bNextLine )
428 styler.ColourTo(i+1,state);
430 if( bNextLine == false )
432 styler.ColourTo(i,state);
433 state = SCE_NSIS_DEFAULT;
436 break;
438 case SCE_NSIS_FUNCTION:
440 // NSIS KeyWord:
441 if( cCurrChar == '$' )
442 state = SCE_NSIS_DEFAULT;
443 else if( cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' ) )
444 state = SCE_NSIS_DEFAULT;
445 else if( (isNsisChar(cCurrChar) && !isNsisChar( cNextChar) && cNextChar != '}') || cCurrChar == '}' )
447 state = classifyWordNsis( styler.GetStartSegment(), i, keywordLists, styler );
448 styler.ColourTo( i, state);
449 state = SCE_NSIS_DEFAULT;
451 else if( !isNsisChar( cCurrChar ) && cCurrChar != '{' && cCurrChar != '}' )
453 if( classifyWordNsis( styler.GetStartSegment(), i-1, keywordLists, styler) == SCE_NSIS_NUMBER )
454 styler.ColourTo( i-1, SCE_NSIS_NUMBER );
456 state = SCE_NSIS_DEFAULT;
458 if( cCurrChar == '"' )
460 state = SCE_NSIS_STRINGDQ;
461 bVarInString = false;
462 bClassicVarInString = false;
464 else if( cCurrChar == '`' )
466 state = SCE_NSIS_STRINGLQ;
467 bVarInString = false;
468 bClassicVarInString = false;
470 else if( cCurrChar == '\'' )
472 state = SCE_NSIS_STRINGRQ;
473 bVarInString = false;
474 bClassicVarInString = false;
476 else if( cCurrChar == '#' || cCurrChar == ';' )
478 state = SCE_NSIS_COMMENT;
481 break;
482 case SCE_NSIS_COMMENTBOX:
484 if( styler.SafeGetCharAt(i-1) == '*' && cCurrChar == '/' )
486 styler.ColourTo(i,state);
487 state = SCE_NSIS_DEFAULT;
489 break;
492 if( state == SCE_NSIS_COMMENT || state == SCE_NSIS_COMMENTBOX )
494 styler.ColourTo(i,state);
496 else if( state == SCE_NSIS_STRINGDQ || state == SCE_NSIS_STRINGLQ || state == SCE_NSIS_STRINGRQ )
498 bool bIngoreNextDollarSign = false;
499 bool bUserVars = false;
500 if( styler.GetPropertyInt("nsis.uservars") == 1 )
501 bUserVars = true;
503 if( bVarInString && cCurrChar == '$' )
505 bVarInString = false;
506 bIngoreNextDollarSign = true;
508 else if( bVarInString && cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' || cNextChar == '"' || cNextChar == '`' || cNextChar == '\'' ) )
510 styler.ColourTo( i+1, SCE_NSIS_STRINGVAR);
511 bVarInString = false;
512 bIngoreNextDollarSign = false;
515 // Covers "$INSTDIR and user vars like $MYVAR"
516 else if( bVarInString && !isNsisChar(cNextChar) )
518 int nWordState = classifyWordNsis( styler.GetStartSegment(), i, keywordLists, styler);
519 if( nWordState == SCE_NSIS_VARIABLE )
520 styler.ColourTo( i, SCE_NSIS_STRINGVAR);
521 else if( bUserVars )
522 styler.ColourTo( i, SCE_NSIS_STRINGVAR);
523 bVarInString = false;
525 // Covers "${TEST}..."
526 else if( bClassicVarInString && cNextChar == '}' )
528 styler.ColourTo( i+1, SCE_NSIS_STRINGVAR);
529 bClassicVarInString = false;
532 // Start of var in string
533 if( !bIngoreNextDollarSign && cCurrChar == '$' && cNextChar == '{' )
535 styler.ColourTo( i-1, state);
536 bClassicVarInString = true;
537 bVarInString = false;
539 else if( !bIngoreNextDollarSign && cCurrChar == '$' )
541 styler.ColourTo( i-1, state);
542 bVarInString = true;
543 bClassicVarInString = false;
548 // Colourise remaining document
549 styler.ColourTo(nLengthDoc-1,state);
552 static void FoldNsisDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler)
554 // No folding enabled, no reason to continue...
555 if( styler.GetPropertyInt("fold") == 0 )
556 return;
558 bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) == 1;
559 bool foldUtilityCmd = styler.GetPropertyInt("nsis.foldutilcmd", 1) == 1;
560 bool blockComment = false;
562 int lineCurrent = styler.GetLine(startPos);
563 unsigned int safeStartPos = styler.LineStart( lineCurrent );
565 bool bArg1 = true;
566 int nWordStart = -1;
568 int levelCurrent = SC_FOLDLEVELBASE;
569 if (lineCurrent > 0)
570 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
571 int levelNext = levelCurrent;
572 int style = styler.StyleAt(safeStartPos);
573 if( style == SCE_NSIS_COMMENTBOX )
575 if( styler.SafeGetCharAt(safeStartPos) == '/' && styler.SafeGetCharAt(safeStartPos+1) == '*' )
576 levelNext++;
577 blockComment = true;
580 for (unsigned int i = safeStartPos; i < startPos + length; i++)
582 char chCurr = styler.SafeGetCharAt(i);
583 style = styler.StyleAt(i);
584 if( blockComment && style != SCE_NSIS_COMMENTBOX )
586 levelNext--;
587 blockComment = false;
589 else if( !blockComment && style == SCE_NSIS_COMMENTBOX )
591 levelNext++;
592 blockComment = true;
595 if( bArg1 && !blockComment)
597 if( nWordStart == -1 && (isNsisLetter(chCurr) || chCurr == '!') )
599 nWordStart = i;
601 else if( isNsisLetter(chCurr) == false && nWordStart > -1 )
603 int newLevel = calculateFoldNsis( nWordStart, i-1, levelNext, styler, foldAtElse, foldUtilityCmd );
605 if( newLevel == levelNext )
607 if( foldAtElse && foldUtilityCmd )
609 if( NsisNextLineHasElse(i, startPos + length, styler) )
610 levelNext--;
613 else
614 levelNext = newLevel;
615 bArg1 = false;
619 if( chCurr == '\n' )
621 if( bArg1 && foldAtElse && foldUtilityCmd && !blockComment )
623 if( NsisNextLineHasElse(i, startPos + length, styler) )
624 levelNext--;
627 // If we are on a new line...
628 int levelUse = levelCurrent;
629 int lev = levelUse | levelNext << 16;
630 if (levelUse < levelNext )
631 lev |= SC_FOLDLEVELHEADERFLAG;
632 if (lev != styler.LevelAt(lineCurrent))
633 styler.SetLevel(lineCurrent, lev);
635 lineCurrent++;
636 levelCurrent = levelNext;
637 bArg1 = true; // New line, lets look at first argument again
638 nWordStart = -1;
642 int levelUse = levelCurrent;
643 int lev = levelUse | levelNext << 16;
644 if (levelUse < levelNext)
645 lev |= SC_FOLDLEVELHEADERFLAG;
646 if (lev != styler.LevelAt(lineCurrent))
647 styler.SetLevel(lineCurrent, lev);
650 static const char * const nsisWordLists[] = {
651 "Functions",
652 "Variables",
653 "Lables",
654 "UserDefined",
655 0, };
658 LexerModule lmNsis(SCLEX_NSIS, ColouriseNsisDoc, "nsis", FoldNsisDoc, nsisWordLists);