1 // Scintilla source code edit control
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.
17 #include "Scintilla.h"
21 #include "LexAccessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
28 using namespace Scintilla
;
32 // located in SciLexer.h
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(Sci_PositionU start
, Sci_PositionU end
, Accessor
&styler
)
74 Sci_Position nNextLine
= -1;
75 for( Sci_PositionU i
= start
; i
< end
; i
++ )
77 char cNext
= styler
.SafeGetCharAt( i
);
85 if( nNextLine
== -1 ) // We never found the next line...
88 for( Sci_PositionU firstChar
= nNextLine
; firstChar
< end
; firstChar
++ )
90 char cNext
= styler
.SafeGetCharAt( firstChar
);
97 if( styler
.Match(firstChar
, "!else") )
106 static int NsisCmp( const char *s1
, const char *s2
, bool bIgnoreCase
)
109 return CompareCaseInsensitive( s1
, s2
);
111 return strcmp( s1
, s2
);
114 static int calculateFoldNsis(Sci_PositionU start
, Sci_PositionU 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 )
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
)
133 if( style
!= SCE_NSIS_FUNCTIONDEF
&& style
!= SCE_NSIS_SECTIONDEF
&&
134 style
!= SCE_NSIS_SUBSECTIONDEF
&& style
!= SCE_NSIS_SECTIONGROUP
&&
135 style
!= SCE_NSIS_PAGEEX
)
139 int newFoldlevel
= foldlevel
;
140 bool bIgnoreCase
= false;
141 if( styler
.GetPropertyInt("nsis.ignorecase") == 1 )
144 char s
[20]; // The key word we are looking for has atmost 13 characters
146 for (Sci_PositionU i
= 0; i
< end
- start
+ 1 && i
< 19; i
++)
148 s
[i
] = static_cast<char>( styler
[ start
+ i
] );
154 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 )
156 else if( NsisCmp(s
, "!endif", bIgnoreCase
) == 0 || NsisCmp(s
, "!macroend", bIgnoreCase
) == 0 )
158 else if( bElse
&& NsisCmp(s
, "!else", bIgnoreCase
) == 0 )
163 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 )
165 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 )
172 static int classifyWordNsis(Sci_PositionU start
, Sci_PositionU end
, WordList
*keywordLists
[], Accessor
&styler
)
174 bool bIgnoreCase
= false;
175 if( styler
.GetPropertyInt("nsis.ignorecase") == 1 )
178 bool bUserVars
= false;
179 if( styler
.GetPropertyInt("nsis.uservars") == 1 )
186 WordList
&Functions
= *keywordLists
[0];
187 WordList
&Variables
= *keywordLists
[1];
188 WordList
&Lables
= *keywordLists
[2];
189 WordList
&UserDefined
= *keywordLists
[3];
191 for (Sci_PositionU i
= 0; i
< end
- start
+ 1 && i
< 99; i
++)
194 s
[i
] = static_cast<char>( tolower(styler
[ start
+ i
] ) );
196 s
[i
] = static_cast<char>( styler
[ start
+ i
] );
200 // Check for special words...
201 if( NsisCmp(s
, "!macro", bIgnoreCase
) == 0 || NsisCmp(s
, "!macroend", bIgnoreCase
) == 0 ) // Covers !macro and !macroend
202 return SCE_NSIS_MACRODEF
;
204 if( NsisCmp(s
, "!ifdef", bIgnoreCase
) == 0 || NsisCmp(s
, "!ifndef", bIgnoreCase
) == 0 || NsisCmp(s
, "!endif", bIgnoreCase
) == 0 ) // Covers !ifdef, !ifndef and !endif
205 return SCE_NSIS_IFDEFINEDEF
;
207 if( NsisCmp(s
, "!if", bIgnoreCase
) == 0 || NsisCmp(s
, "!else", bIgnoreCase
) == 0 ) // Covers !if and else
208 return SCE_NSIS_IFDEFINEDEF
;
210 if (NsisCmp(s
, "!ifmacrodef", bIgnoreCase
) == 0 || NsisCmp(s
, "!ifmacrondef", bIgnoreCase
) == 0 ) // Covers !ifmacrodef and !ifnmacrodef
211 return SCE_NSIS_IFDEFINEDEF
;
213 if( NsisCmp(s
, "SectionGroup", bIgnoreCase
) == 0 || NsisCmp(s
, "SectionGroupEnd", bIgnoreCase
) == 0 ) // Covers SectionGroup and SectionGroupEnd
214 return SCE_NSIS_SECTIONGROUP
;
216 if( NsisCmp(s
, "Section", bIgnoreCase
) == 0 || NsisCmp(s
, "SectionEnd", bIgnoreCase
) == 0 ) // Covers Section and SectionEnd
217 return SCE_NSIS_SECTIONDEF
;
219 if( NsisCmp(s
, "SubSection", bIgnoreCase
) == 0 || NsisCmp(s
, "SubSectionEnd", bIgnoreCase
) == 0 ) // Covers SubSection and SubSectionEnd
220 return SCE_NSIS_SUBSECTIONDEF
;
222 if( NsisCmp(s
, "PageEx", bIgnoreCase
) == 0 || NsisCmp(s
, "PageExEnd", bIgnoreCase
) == 0 ) // Covers PageEx and PageExEnd
223 return SCE_NSIS_PAGEEX
;
225 if( NsisCmp(s
, "Function", bIgnoreCase
) == 0 || NsisCmp(s
, "FunctionEnd", bIgnoreCase
) == 0 ) // Covers Function and FunctionEnd
226 return SCE_NSIS_FUNCTIONDEF
;
228 if ( Functions
.InList(s
) )
229 return SCE_NSIS_FUNCTION
;
231 if ( Variables
.InList(s
) )
232 return SCE_NSIS_VARIABLE
;
234 if ( Lables
.InList(s
) )
235 return SCE_NSIS_LABEL
;
237 if( UserDefined
.InList(s
) )
238 return SCE_NSIS_USERDEFINED
;
242 if( s
[1] == '{' && s
[strlen(s
)-1] == '}' )
243 return SCE_NSIS_VARIABLE
;
246 // See if the variable is a user defined variable
247 if( s
[0] == '$' && bUserVars
)
249 bool bHasSimpleNsisChars
= true;
250 for (Sci_PositionU j
= 1; j
< end
- start
+ 1 && j
< 99; j
++)
252 if( !isNsisChar( s
[j
] ) )
254 bHasSimpleNsisChars
= false;
259 if( bHasSimpleNsisChars
)
260 return SCE_NSIS_VARIABLE
;
263 // To check for numbers
264 if( isNsisNumber( s
[0] ) )
266 bool bHasSimpleNsisNumber
= true;
267 for (Sci_PositionU j
= 1; j
< end
- start
+ 1 && j
< 99; j
++)
269 if( !isNsisNumber( s
[j
] ) )
271 bHasSimpleNsisNumber
= false;
276 if( bHasSimpleNsisNumber
)
277 return SCE_NSIS_NUMBER
;
280 return SCE_NSIS_DEFAULT
;
283 static void ColouriseNsisDoc(Sci_PositionU startPos
, Sci_Position length
, int, WordList
*keywordLists
[], Accessor
&styler
)
285 int state
= SCE_NSIS_DEFAULT
;
287 state
= styler
.StyleAt(startPos
-1); // Use the style from the previous line, usually default, but could be commentbox
289 styler
.StartAt( startPos
);
290 styler
.GetLine( startPos
);
292 Sci_PositionU nLengthDoc
= startPos
+ length
;
293 styler
.StartSegment( startPos
);
296 bool bVarInString
= false;
297 bool bClassicVarInString
= false;
300 for( i
= startPos
; i
< nLengthDoc
; i
++ )
302 cCurrChar
= styler
.SafeGetCharAt( i
);
303 char cNextChar
= styler
.SafeGetCharAt(i
+1);
307 case SCE_NSIS_DEFAULT
:
308 if( cCurrChar
== ';' || cCurrChar
== '#' ) // we have a comment line
310 styler
.ColourTo(i
-1, state
);
311 state
= SCE_NSIS_COMMENT
;
314 if( cCurrChar
== '"' )
316 styler
.ColourTo(i
-1, state
);
317 state
= SCE_NSIS_STRINGDQ
;
318 bVarInString
= false;
319 bClassicVarInString
= false;
322 if( cCurrChar
== '\'' )
324 styler
.ColourTo(i
-1, state
);
325 state
= SCE_NSIS_STRINGRQ
;
326 bVarInString
= false;
327 bClassicVarInString
= false;
330 if( cCurrChar
== '`' )
332 styler
.ColourTo(i
-1, state
);
333 state
= SCE_NSIS_STRINGLQ
;
334 bVarInString
= false;
335 bClassicVarInString
= false;
339 // NSIS KeyWord,Function, Variable, UserDefined:
340 if( cCurrChar
== '$' || isNsisChar(cCurrChar
) || cCurrChar
== '!' )
342 styler
.ColourTo(i
-1,state
);
343 state
= SCE_NSIS_FUNCTION
;
345 // If it is a number, we must check and set style here first...
346 if( isNsisNumber(cCurrChar
) && (cNextChar
== '\t' || cNextChar
== ' ' || cNextChar
== '\r' || cNextChar
== '\n' ) )
347 styler
.ColourTo( i
, SCE_NSIS_NUMBER
);
352 if( cCurrChar
== '/' && cNextChar
== '*' )
354 styler
.ColourTo(i
-1,state
);
355 state
= SCE_NSIS_COMMENTBOX
;
360 case SCE_NSIS_COMMENT
:
361 if( cNextChar
== '\n' || cNextChar
== '\r' )
364 if( cCurrChar
== '\\' )
366 styler
.ColourTo(i
-2,state
);
367 styler
.ColourTo(i
,SCE_NSIS_DEFAULT
);
371 styler
.ColourTo(i
,state
);
372 state
= SCE_NSIS_DEFAULT
;
376 case SCE_NSIS_STRINGDQ
:
377 case SCE_NSIS_STRINGLQ
:
378 case SCE_NSIS_STRINGRQ
:
380 if( styler
.SafeGetCharAt(i
-1) == '\\' && styler
.SafeGetCharAt(i
-2) == '$' )
381 break; // Ignore the next character, even if it is a quote of some sort
383 if( cCurrChar
== '"' && state
== SCE_NSIS_STRINGDQ
)
385 styler
.ColourTo(i
,state
);
386 state
= SCE_NSIS_DEFAULT
;
390 if( cCurrChar
== '`' && state
== SCE_NSIS_STRINGLQ
)
392 styler
.ColourTo(i
,state
);
393 state
= SCE_NSIS_DEFAULT
;
397 if( cCurrChar
== '\'' && state
== SCE_NSIS_STRINGRQ
)
399 styler
.ColourTo(i
,state
);
400 state
= SCE_NSIS_DEFAULT
;
404 if( cNextChar
== '\r' || cNextChar
== '\n' )
406 Sci_Position nCurLine
= styler
.GetLine(i
+1);
407 Sci_Position nBack
= i
;
408 // We need to check if the previous line has a \ in it...
409 bool bNextLine
= false;
413 if( styler
.GetLine(nBack
) != nCurLine
)
416 char cTemp
= styler
.SafeGetCharAt(nBack
, 'a'); // Letter 'a' is safe here
423 if( cTemp
!= '\r' && cTemp
!= '\n' && cTemp
!= '\t' && cTemp
!= ' ' )
431 styler
.ColourTo(i
+1,state
);
433 if( bNextLine
== false )
435 styler
.ColourTo(i
,state
);
436 state
= SCE_NSIS_DEFAULT
;
441 case SCE_NSIS_FUNCTION
:
444 if( cCurrChar
== '$' )
445 state
= SCE_NSIS_DEFAULT
;
446 else if( cCurrChar
== '\\' && (cNextChar
== 'n' || cNextChar
== 'r' || cNextChar
== 't' ) )
447 state
= SCE_NSIS_DEFAULT
;
448 else if( (isNsisChar(cCurrChar
) && !isNsisChar( cNextChar
) && cNextChar
!= '}') || cCurrChar
== '}' )
450 state
= classifyWordNsis( styler
.GetStartSegment(), i
, keywordLists
, styler
);
451 styler
.ColourTo( i
, state
);
452 state
= SCE_NSIS_DEFAULT
;
454 else if( !isNsisChar( cCurrChar
) && cCurrChar
!= '{' && cCurrChar
!= '}' )
456 if( classifyWordNsis( styler
.GetStartSegment(), i
-1, keywordLists
, styler
) == SCE_NSIS_NUMBER
)
457 styler
.ColourTo( i
-1, SCE_NSIS_NUMBER
);
459 state
= SCE_NSIS_DEFAULT
;
461 if( cCurrChar
== '"' )
463 state
= SCE_NSIS_STRINGDQ
;
464 bVarInString
= false;
465 bClassicVarInString
= false;
467 else if( cCurrChar
== '`' )
469 state
= SCE_NSIS_STRINGLQ
;
470 bVarInString
= false;
471 bClassicVarInString
= false;
473 else if( cCurrChar
== '\'' )
475 state
= SCE_NSIS_STRINGRQ
;
476 bVarInString
= false;
477 bClassicVarInString
= false;
479 else if( cCurrChar
== '#' || cCurrChar
== ';' )
481 state
= SCE_NSIS_COMMENT
;
485 case SCE_NSIS_COMMENTBOX
:
487 if( styler
.SafeGetCharAt(i
-1) == '*' && cCurrChar
== '/' )
489 styler
.ColourTo(i
,state
);
490 state
= SCE_NSIS_DEFAULT
;
495 if( state
== SCE_NSIS_COMMENT
|| state
== SCE_NSIS_COMMENTBOX
)
497 styler
.ColourTo(i
,state
);
499 else if( state
== SCE_NSIS_STRINGDQ
|| state
== SCE_NSIS_STRINGLQ
|| state
== SCE_NSIS_STRINGRQ
)
501 bool bIngoreNextDollarSign
= false;
502 bool bUserVars
= false;
503 if( styler
.GetPropertyInt("nsis.uservars") == 1 )
506 if( bVarInString
&& cCurrChar
== '$' )
508 bVarInString
= false;
509 bIngoreNextDollarSign
= true;
511 else if( bVarInString
&& cCurrChar
== '\\' && (cNextChar
== 'n' || cNextChar
== 'r' || cNextChar
== 't' || cNextChar
== '"' || cNextChar
== '`' || cNextChar
== '\'' ) )
513 styler
.ColourTo( i
+1, SCE_NSIS_STRINGVAR
);
514 bVarInString
= false;
515 bIngoreNextDollarSign
= false;
518 // Covers "$INSTDIR and user vars like $MYVAR"
519 else if( bVarInString
&& !isNsisChar(cNextChar
) )
521 int nWordState
= classifyWordNsis( styler
.GetStartSegment(), i
, keywordLists
, styler
);
522 if( nWordState
== SCE_NSIS_VARIABLE
)
523 styler
.ColourTo( i
, SCE_NSIS_STRINGVAR
);
525 styler
.ColourTo( i
, SCE_NSIS_STRINGVAR
);
526 bVarInString
= false;
528 // Covers "${TEST}..."
529 else if( bClassicVarInString
&& cNextChar
== '}' )
531 styler
.ColourTo( i
+1, SCE_NSIS_STRINGVAR
);
532 bClassicVarInString
= false;
535 // Start of var in string
536 if( !bIngoreNextDollarSign
&& cCurrChar
== '$' && cNextChar
== '{' )
538 styler
.ColourTo( i
-1, state
);
539 bClassicVarInString
= true;
540 bVarInString
= false;
542 else if( !bIngoreNextDollarSign
&& cCurrChar
== '$' )
544 styler
.ColourTo( i
-1, state
);
546 bClassicVarInString
= false;
551 // Colourise remaining document
552 styler
.ColourTo(nLengthDoc
-1,state
);
555 static void FoldNsisDoc(Sci_PositionU startPos
, Sci_Position length
, int, WordList
*[], Accessor
&styler
)
557 // No folding enabled, no reason to continue...
558 if( styler
.GetPropertyInt("fold") == 0 )
561 bool foldAtElse
= styler
.GetPropertyInt("fold.at.else", 0) == 1;
562 bool foldUtilityCmd
= styler
.GetPropertyInt("nsis.foldutilcmd", 1) == 1;
563 bool blockComment
= false;
565 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
566 Sci_PositionU safeStartPos
= styler
.LineStart( lineCurrent
);
569 Sci_Position nWordStart
= -1;
571 int levelCurrent
= SC_FOLDLEVELBASE
;
573 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
574 int levelNext
= levelCurrent
;
575 int style
= styler
.StyleAt(safeStartPos
);
576 if( style
== SCE_NSIS_COMMENTBOX
)
578 if( styler
.SafeGetCharAt(safeStartPos
) == '/' && styler
.SafeGetCharAt(safeStartPos
+1) == '*' )
583 for (Sci_PositionU i
= safeStartPos
; i
< startPos
+ length
; i
++)
585 char chCurr
= styler
.SafeGetCharAt(i
);
586 style
= styler
.StyleAt(i
);
587 if( blockComment
&& style
!= SCE_NSIS_COMMENTBOX
)
590 blockComment
= false;
592 else if( !blockComment
&& style
== SCE_NSIS_COMMENTBOX
)
598 if( bArg1
&& !blockComment
)
600 if( nWordStart
== -1 && (isNsisLetter(chCurr
) || chCurr
== '!') )
604 else if( isNsisLetter(chCurr
) == false && nWordStart
> -1 )
606 int newLevel
= calculateFoldNsis( nWordStart
, i
-1, levelNext
, styler
, foldAtElse
, foldUtilityCmd
);
608 if( newLevel
== levelNext
)
610 if( foldAtElse
&& foldUtilityCmd
)
612 if( NsisNextLineHasElse(i
, startPos
+ length
, styler
) )
617 levelNext
= newLevel
;
624 if( bArg1
&& foldAtElse
&& foldUtilityCmd
&& !blockComment
)
626 if( NsisNextLineHasElse(i
, startPos
+ length
, styler
) )
630 // If we are on a new line...
631 int levelUse
= levelCurrent
;
632 int lev
= levelUse
| levelNext
<< 16;
633 if (levelUse
< levelNext
)
634 lev
|= SC_FOLDLEVELHEADERFLAG
;
635 if (lev
!= styler
.LevelAt(lineCurrent
))
636 styler
.SetLevel(lineCurrent
, lev
);
639 levelCurrent
= levelNext
;
640 bArg1
= true; // New line, lets look at first argument again
645 int levelUse
= levelCurrent
;
646 int lev
= levelUse
| levelNext
<< 16;
647 if (levelUse
< levelNext
)
648 lev
|= SC_FOLDLEVELHEADERFLAG
;
649 if (lev
!= styler
.LevelAt(lineCurrent
))
650 styler
.SetLevel(lineCurrent
, lev
);
653 static const char * const nsisWordLists
[] = {
661 LexerModule
lmNsis(SCLEX_NSIS
, ColouriseNsisDoc
, "nsis", FoldNsisDoc
, nsisWordLists
);