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.
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include <ctype.h>
15 
16 #include "ILexer.h"
17 #include "Scintilla.h"
18 #include "SciLexer.h"
19 
20 #include "WordList.h"
21 #include "LexAccessor.h"
22 #include "Accessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
26 
27 using namespace Scintilla;
28 
29 /*
30 // located in SciLexer.h
31 #define SCLEX_NSIS 43
32 
33 #define SCE_NSIS_DEFAULT 0
34 #define SCE_NSIS_COMMENT 1
35 #define SCE_NSIS_STRINGDQ 2
36 #define SCE_NSIS_STRINGLQ 3
37 #define SCE_NSIS_STRINGRQ 4
38 #define SCE_NSIS_FUNCTION 5
39 #define SCE_NSIS_VARIABLE 6
40 #define SCE_NSIS_LABEL 7
41 #define SCE_NSIS_USERDEFINED 8
42 #define SCE_NSIS_SECTIONDEF 9
43 #define SCE_NSIS_SUBSECTIONDEF 10
44 #define SCE_NSIS_IFDEFINEDEF 11
45 #define SCE_NSIS_MACRODEF 12
46 #define SCE_NSIS_STRINGVAR 13
47 #define SCE_NSIS_NUMBER 14
48 // ADDED for Scintilla v1.63
49 #define SCE_NSIS_SECTIONGROUP 15
50 #define SCE_NSIS_PAGEEX 16
51 #define SCE_NSIS_FUNCTIONDEF 17
52 #define SCE_NSIS_COMMENTBOX 18
53 */
54 
isNsisNumber(char ch)55 static bool isNsisNumber(char ch)
56 {
57   return (ch >= '0' && ch <= '9');
58 }
59 
isNsisChar(char ch)60 static bool isNsisChar(char ch)
61 {
62   return (ch == '.' ) || (ch == '_' ) || isNsisNumber(ch) || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
63 }
64 
isNsisLetter(char ch)65 static bool isNsisLetter(char ch)
66 {
67   return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
68 }
69 
NsisNextLineHasElse(Sci_PositionU start,Sci_PositionU end,Accessor & styler)70 static bool NsisNextLineHasElse(Sci_PositionU start, Sci_PositionU end, Accessor &styler)
71 {
72   Sci_Position nNextLine = -1;
73   for( Sci_PositionU i = start; i < end; i++ )
74   {
75     char cNext = styler.SafeGetCharAt( i );
76     if( cNext == '\n' )
77     {
78       nNextLine = i+1;
79       break;
80     }
81   }
82 
83   if( nNextLine == -1 ) // We never found the next line...
84     return false;
85 
86   for( Sci_PositionU firstChar = nNextLine; firstChar < end; firstChar++ )
87   {
88     char cNext = styler.SafeGetCharAt( firstChar );
89     if( cNext == ' ' )
90       continue;
91     if( cNext == '\t' )
92       continue;
93     if( cNext == '!' )
94     {
95       if( styler.Match(firstChar, "!else") )
96         return true;
97     }
98     break;
99   }
100 
101   return false;
102 }
103 
NsisCmp(const char * s1,const char * s2,bool bIgnoreCase)104 static int NsisCmp( const char *s1, const char *s2, bool bIgnoreCase )
105 {
106   if( bIgnoreCase )
107      return CompareCaseInsensitive( s1, s2);
108 
109   return strcmp( s1, s2 );
110 }
111 
calculateFoldNsis(Sci_PositionU start,Sci_PositionU end,int foldlevel,Accessor & styler,bool bElse,bool foldUtilityCmd)112 static int calculateFoldNsis(Sci_PositionU start, Sci_PositionU end, int foldlevel, Accessor &styler, bool bElse, bool foldUtilityCmd )
113 {
114   int style = styler.StyleAt(end);
115 
116   // If the word is too long, it is not what we are looking for
117   if( end - start > 20 )
118     return foldlevel;
119 
120   if( foldUtilityCmd )
121   {
122     // Check the style at this point, if it is not valid, then return zero
123     if( style != SCE_NSIS_FUNCTIONDEF && style != SCE_NSIS_SECTIONDEF &&
124         style != SCE_NSIS_SUBSECTIONDEF && style != SCE_NSIS_IFDEFINEDEF &&
125         style != SCE_NSIS_MACRODEF && style != SCE_NSIS_SECTIONGROUP &&
126         style != SCE_NSIS_PAGEEX )
127           return foldlevel;
128   }
129   else
130   {
131     if( style != SCE_NSIS_FUNCTIONDEF && style != SCE_NSIS_SECTIONDEF &&
132         style != SCE_NSIS_SUBSECTIONDEF && style != SCE_NSIS_SECTIONGROUP &&
133         style != SCE_NSIS_PAGEEX )
134           return foldlevel;
135   }
136 
137   int newFoldlevel = foldlevel;
138   bool bIgnoreCase = false;
139   if( styler.GetPropertyInt("nsis.ignorecase") == 1 )
140     bIgnoreCase = true;
141 
142   char s[20]; // The key word we are looking for has atmost 13 characters
143   s[0] = '\0';
144   for (Sci_PositionU i = 0; i < end - start + 1 && i < 19; i++)
145 	{
146 		s[i] = static_cast<char>( styler[ start + i ] );
147 		s[i + 1] = '\0';
148 	}
149 
150   if( s[0] == '!' )
151   {
152     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 )
153       newFoldlevel++;
154     else if( NsisCmp(s, "!endif", bIgnoreCase) == 0 || NsisCmp(s, "!macroend", bIgnoreCase ) == 0 )
155       newFoldlevel--;
156     else if( bElse && NsisCmp(s, "!else", bIgnoreCase) == 0 )
157       newFoldlevel++;
158   }
159   else
160   {
161     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 )
162       newFoldlevel++;
163     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 )
164       newFoldlevel--;
165   }
166 
167   return newFoldlevel;
168 }
169 
classifyWordNsis(Sci_PositionU start,Sci_PositionU end,WordList * keywordLists[],Accessor & styler)170 static int classifyWordNsis(Sci_PositionU start, Sci_PositionU end, WordList *keywordLists[], Accessor &styler )
171 {
172   bool bIgnoreCase = false;
173   if( styler.GetPropertyInt("nsis.ignorecase") == 1 )
174     bIgnoreCase = true;
175 
176   bool bUserVars = false;
177   if( styler.GetPropertyInt("nsis.uservars") == 1 )
178     bUserVars = true;
179 
180 	char s[100];
181 	s[0] = '\0';
182 	s[1] = '\0';
183 
184 	WordList &Functions = *keywordLists[0];
185 	WordList &Variables = *keywordLists[1];
186 	WordList &Lables = *keywordLists[2];
187 	WordList &UserDefined = *keywordLists[3];
188 
189 	for (Sci_PositionU i = 0; i < end - start + 1 && i < 99; i++)
190 	{
191     if( bIgnoreCase )
192       s[i] = static_cast<char>( tolower(styler[ start + i ] ) );
193     else
194 		  s[i] = static_cast<char>( styler[ start + i ] );
195 		s[i + 1] = '\0';
196 	}
197 
198 	// Check for special words...
199 	if( NsisCmp(s, "!macro", bIgnoreCase ) == 0 || NsisCmp(s, "!macroend", bIgnoreCase) == 0 ) // Covers !macro and !macroend
200 		return SCE_NSIS_MACRODEF;
201 
202 	if( NsisCmp(s, "!ifdef", bIgnoreCase ) == 0 ||  NsisCmp(s, "!ifndef", bIgnoreCase) == 0 ||  NsisCmp(s, "!endif", bIgnoreCase) == 0 ) // Covers !ifdef, !ifndef and !endif
203 		return SCE_NSIS_IFDEFINEDEF;
204 
205 	if( NsisCmp(s, "!if", bIgnoreCase ) == 0 || NsisCmp(s, "!else", bIgnoreCase )  == 0 ) // Covers !if and else
206 		return SCE_NSIS_IFDEFINEDEF;
207 
208 	if (NsisCmp(s, "!ifmacrodef", bIgnoreCase ) == 0 || NsisCmp(s, "!ifmacrondef", bIgnoreCase )  == 0 ) // Covers !ifmacrodef and !ifnmacrodef
209 		return SCE_NSIS_IFDEFINEDEF;
210 
211   if( NsisCmp(s, "SectionGroup", bIgnoreCase) == 0 || NsisCmp(s, "SectionGroupEnd", bIgnoreCase) == 0 ) // Covers SectionGroup and SectionGroupEnd
212     return SCE_NSIS_SECTIONGROUP;
213 
214 	if( NsisCmp(s, "Section", bIgnoreCase ) == 0 || NsisCmp(s, "SectionEnd", bIgnoreCase) == 0 ) // Covers Section and SectionEnd
215 		return SCE_NSIS_SECTIONDEF;
216 
217 	if( NsisCmp(s, "SubSection", bIgnoreCase) == 0 || NsisCmp(s, "SubSectionEnd", bIgnoreCase) == 0 ) // Covers SubSection and SubSectionEnd
218 		return SCE_NSIS_SUBSECTIONDEF;
219 
220   if( NsisCmp(s, "PageEx", bIgnoreCase) == 0 || NsisCmp(s, "PageExEnd", bIgnoreCase) == 0 ) // Covers PageEx and PageExEnd
221     return SCE_NSIS_PAGEEX;
222 
223 	if( NsisCmp(s, "Function", bIgnoreCase) == 0 || NsisCmp(s, "FunctionEnd", bIgnoreCase) == 0 ) // Covers Function and FunctionEnd
224 		return SCE_NSIS_FUNCTIONDEF;
225 
226 	if ( Functions.InList(s) )
227 		return SCE_NSIS_FUNCTION;
228 
229 	if ( Variables.InList(s) )
230 		return SCE_NSIS_VARIABLE;
231 
232 	if ( Lables.InList(s) )
233 		return SCE_NSIS_LABEL;
234 
235 	if( UserDefined.InList(s) )
236 		return SCE_NSIS_USERDEFINED;
237 
238 	if( strlen(s) > 3 )
239 	{
240 		if( s[1] == '{' && s[strlen(s)-1] == '}' )
241 			return SCE_NSIS_VARIABLE;
242 	}
243 
244   // See if the variable is a user defined variable
245   if( s[0] == '$' && bUserVars )
246   {
247     bool bHasSimpleNsisChars = true;
248     for (Sci_PositionU j = 1; j < end - start + 1 && j < 99; j++)
249 	  {
250       if( !isNsisChar( s[j] ) )
251       {
252         bHasSimpleNsisChars = false;
253         break;
254       }
255 	  }
256 
257     if( bHasSimpleNsisChars )
258       return SCE_NSIS_VARIABLE;
259   }
260 
261   // To check for numbers
262   if( isNsisNumber( s[0] ) )
263   {
264     bool bHasSimpleNsisNumber = true;
265     for (Sci_PositionU j = 1; j < end - start + 1 && j < 99; j++)
266 	  {
267       if( !isNsisNumber( s[j] ) )
268       {
269         bHasSimpleNsisNumber = false;
270         break;
271       }
272 	  }
273 
274     if( bHasSimpleNsisNumber )
275       return SCE_NSIS_NUMBER;
276   }
277 
278 	return SCE_NSIS_DEFAULT;
279 }
280 
ColouriseNsisDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * keywordLists[],Accessor & styler)281 static void ColouriseNsisDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *keywordLists[], Accessor &styler)
282 {
283 	int state = SCE_NSIS_DEFAULT;
284   if( startPos > 0 )
285     state = styler.StyleAt(startPos-1); // Use the style from the previous line, usually default, but could be commentbox
286 
287 	styler.StartAt( startPos );
288 	styler.GetLine( startPos );
289 
290 	Sci_PositionU nLengthDoc = startPos + length;
291 	styler.StartSegment( startPos );
292 
293 	char cCurrChar;
294 	bool bVarInString = false;
295   bool bClassicVarInString = false;
296 
297 	Sci_PositionU i;
298 	for( i = startPos; i < nLengthDoc; i++ )
299 	{
300 		cCurrChar = styler.SafeGetCharAt( i );
301 		char cNextChar = styler.SafeGetCharAt(i+1);
302 
303 		switch(state)
304 		{
305 			case SCE_NSIS_DEFAULT:
306 				if( cCurrChar == ';' || cCurrChar == '#' ) // we have a comment line
307 				{
308 					styler.ColourTo(i-1, state );
309 					state = SCE_NSIS_COMMENT;
310 					break;
311 				}
312 				if( cCurrChar == '"' )
313 				{
314 					styler.ColourTo(i-1, state );
315 					state = SCE_NSIS_STRINGDQ;
316 					bVarInString = false;
317           bClassicVarInString = false;
318 					break;
319 				}
320 				if( cCurrChar == '\'' )
321 				{
322 					styler.ColourTo(i-1, state );
323 					state = SCE_NSIS_STRINGRQ;
324 					bVarInString = false;
325           bClassicVarInString = false;
326 					break;
327 				}
328 				if( cCurrChar == '`' )
329 				{
330 					styler.ColourTo(i-1, state );
331 					state = SCE_NSIS_STRINGLQ;
332 					bVarInString = false;
333           bClassicVarInString = false;
334 					break;
335 				}
336 
337 				// NSIS KeyWord,Function, Variable, UserDefined:
338 				if( cCurrChar == '$' || isNsisChar(cCurrChar) || cCurrChar == '!' )
339 				{
340 					styler.ColourTo(i-1,state);
341 				  state = SCE_NSIS_FUNCTION;
342 
343           // If it is a number, we must check and set style here first...
344           if( isNsisNumber(cCurrChar) && (cNextChar == '\t' || cNextChar == ' ' || cNextChar == '\r' || cNextChar == '\n' ) )
345               styler.ColourTo( i, SCE_NSIS_NUMBER);
346 
347 					break;
348 				}
349 
350         if( cCurrChar == '/' && cNextChar == '*' )
351         {
352           styler.ColourTo(i-1,state);
353           state = SCE_NSIS_COMMENTBOX;
354           break;
355         }
356 
357 				break;
358 			case SCE_NSIS_COMMENT:
359 				if( cNextChar == '\n' || cNextChar == '\r' )
360         {
361           // Special case:
362           if( cCurrChar == '\\' )
363           {
364             styler.ColourTo(i-2,state);
365             styler.ColourTo(i,SCE_NSIS_DEFAULT);
366           }
367           else
368           {
369 				    styler.ColourTo(i,state);
370             state = SCE_NSIS_DEFAULT;
371           }
372         }
373 				break;
374 			case SCE_NSIS_STRINGDQ:
375       case SCE_NSIS_STRINGLQ:
376       case SCE_NSIS_STRINGRQ:
377 
378         if( styler.SafeGetCharAt(i-1) == '\\' && styler.SafeGetCharAt(i-2) == '$' )
379           break; // Ignore the next character, even if it is a quote of some sort
380 
381         if( cCurrChar == '"' && state == SCE_NSIS_STRINGDQ )
382 				{
383 					styler.ColourTo(i,state);
384 				  state = SCE_NSIS_DEFAULT;
385           break;
386 				}
387 
388         if( cCurrChar == '`' && state == SCE_NSIS_STRINGLQ )
389         {
390 					styler.ColourTo(i,state);
391 				  state = SCE_NSIS_DEFAULT;
392           break;
393 				}
394 
395         if( cCurrChar == '\'' && state == SCE_NSIS_STRINGRQ )
396 				{
397 					styler.ColourTo(i,state);
398 				  state = SCE_NSIS_DEFAULT;
399           break;
400 				}
401 
402         if( cNextChar == '\r' || cNextChar == '\n' )
403         {
404           Sci_Position nCurLine = styler.GetLine(i+1);
405           Sci_Position nBack = i;
406           // We need to check if the previous line has a \ in it...
407           bool bNextLine = false;
408 
409           while( nBack > 0 )
410           {
411             if( styler.GetLine(nBack) != nCurLine )
412               break;
413 
414             char cTemp = styler.SafeGetCharAt(nBack, 'a'); // Letter 'a' is safe here
415 
416             if( cTemp == '\\' )
417             {
418               bNextLine = true;
419               break;
420             }
421             if( cTemp != '\r' && cTemp != '\n' && cTemp != '\t' && cTemp != ' ' )
422               break;
423 
424             nBack--;
425           }
426 
427           if( bNextLine )
428           {
429             styler.ColourTo(i+1,state);
430           }
431           if( bNextLine == false )
432           {
433             styler.ColourTo(i,state);
434 				    state = SCE_NSIS_DEFAULT;
435           }
436         }
437 				break;
438 
439 			case SCE_NSIS_FUNCTION:
440 
441 				// NSIS KeyWord:
442         if( cCurrChar == '$' )
443           state = SCE_NSIS_DEFAULT;
444         else if( cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' ) )
445           state = SCE_NSIS_DEFAULT;
446 				else if( (isNsisChar(cCurrChar) && !isNsisChar( cNextChar) && cNextChar != '}') || cCurrChar == '}' )
447 				{
448 					state = classifyWordNsis( styler.GetStartSegment(), i, keywordLists, styler );
449 					styler.ColourTo( i, state);
450 					state = SCE_NSIS_DEFAULT;
451 				}
452 				else if( !isNsisChar( cCurrChar ) && cCurrChar != '{' && cCurrChar != '}' )
453 				{
454           if( classifyWordNsis( styler.GetStartSegment(), i-1, keywordLists, styler) == SCE_NSIS_NUMBER )
455              styler.ColourTo( i-1, SCE_NSIS_NUMBER );
456 
457 					state = SCE_NSIS_DEFAULT;
458 
459 					if( cCurrChar == '"' )
460 					{
461 						state = SCE_NSIS_STRINGDQ;
462 						bVarInString = false;
463             bClassicVarInString = false;
464 					}
465 					else if( cCurrChar == '`' )
466 					{
467 						state = SCE_NSIS_STRINGLQ;
468 						bVarInString = false;
469             bClassicVarInString = false;
470 					}
471 					else if( cCurrChar == '\'' )
472 					{
473 						state = SCE_NSIS_STRINGRQ;
474 						bVarInString = false;
475             bClassicVarInString = false;
476 					}
477 					else if( cCurrChar == '#' || cCurrChar == ';' )
478           {
479 						state = SCE_NSIS_COMMENT;
480           }
481 				}
482 				break;
483       case SCE_NSIS_COMMENTBOX:
484 
485         if( styler.SafeGetCharAt(i-1) == '*' && cCurrChar == '/' )
486         {
487           styler.ColourTo(i,state);
488           state = SCE_NSIS_DEFAULT;
489         }
490         break;
491 		}
492 
493 		if( state == SCE_NSIS_COMMENT || state == SCE_NSIS_COMMENTBOX )
494 		{
495 			styler.ColourTo(i,state);
496 		}
497 		else if( state == SCE_NSIS_STRINGDQ || state == SCE_NSIS_STRINGLQ || state == SCE_NSIS_STRINGRQ )
498 		{
499       bool bIngoreNextDollarSign = false;
500       bool bUserVars = false;
501       if( styler.GetPropertyInt("nsis.uservars") == 1 )
502         bUserVars = true;
503 
504       if( bVarInString && cCurrChar == '$' )
505       {
506         bVarInString = false;
507         bIngoreNextDollarSign = true;
508       }
509       else if( bVarInString && cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' || cNextChar == '"' || cNextChar == '`' || cNextChar == '\'' ) )
510       {
511         styler.ColourTo( i+1, SCE_NSIS_STRINGVAR);
512         bVarInString = false;
513         bIngoreNextDollarSign = false;
514       }
515 
516       // Covers "$INSTDIR and user vars like $MYVAR"
517       else if( bVarInString && !isNsisChar(cNextChar) )
518       {
519         int nWordState = classifyWordNsis( styler.GetStartSegment(), i, keywordLists, styler);
520 				if( nWordState == SCE_NSIS_VARIABLE )
521 					styler.ColourTo( i, SCE_NSIS_STRINGVAR);
522         else if( bUserVars )
523           styler.ColourTo( i, SCE_NSIS_STRINGVAR);
524         bVarInString = false;
525       }
526       // Covers "${TEST}..."
527       else if( bClassicVarInString && cNextChar == '}' )
528       {
529         styler.ColourTo( i+1, SCE_NSIS_STRINGVAR);
530 				bClassicVarInString = false;
531       }
532 
533       // Start of var in string
534 			if( !bIngoreNextDollarSign && cCurrChar == '$' && cNextChar == '{' )
535 			{
536 				styler.ColourTo( i-1, state);
537 				bClassicVarInString = true;
538         bVarInString = false;
539 			}
540       else if( !bIngoreNextDollarSign && cCurrChar == '$' )
541       {
542         styler.ColourTo( i-1, state);
543         bVarInString = true;
544         bClassicVarInString = false;
545       }
546 		}
547 	}
548 
549   // Colourise remaining document
550 	styler.ColourTo(nLengthDoc-1,state);
551 }
552 
FoldNsisDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)553 static void FoldNsisDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler)
554 {
555 	// No folding enabled, no reason to continue...
556 	if( styler.GetPropertyInt("fold") == 0 )
557 		return;
558 
559   bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) == 1;
560   bool foldUtilityCmd = styler.GetPropertyInt("nsis.foldutilcmd", 1) == 1;
561   bool blockComment = false;
562 
563   Sci_Position lineCurrent = styler.GetLine(startPos);
564   Sci_PositionU safeStartPos = styler.LineStart( lineCurrent );
565 
566   bool bArg1 = true;
567   Sci_Position nWordStart = -1;
568 
569   int levelCurrent = SC_FOLDLEVELBASE;
570 	if (lineCurrent > 0)
571 		levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
572 	int levelNext = levelCurrent;
573   int style = styler.StyleAt(safeStartPos);
574   if( style == SCE_NSIS_COMMENTBOX )
575   {
576     if( styler.SafeGetCharAt(safeStartPos) == '/' && styler.SafeGetCharAt(safeStartPos+1) == '*' )
577       levelNext++;
578     blockComment = true;
579   }
580 
581   for (Sci_PositionU i = safeStartPos; i < startPos + length; i++)
582 	{
583     char chCurr = styler.SafeGetCharAt(i);
584     style = styler.StyleAt(i);
585     if( blockComment && style != SCE_NSIS_COMMENTBOX )
586     {
587       levelNext--;
588       blockComment = false;
589     }
590     else if( !blockComment && style == SCE_NSIS_COMMENTBOX )
591     {
592       levelNext++;
593       blockComment = true;
594     }
595 
596     if( bArg1 && !blockComment)
597     {
598       if( nWordStart == -1 && (isNsisLetter(chCurr) || chCurr == '!') )
599       {
600         nWordStart = i;
601       }
602       else if( isNsisLetter(chCurr) == false && nWordStart > -1 )
603       {
604         int newLevel = calculateFoldNsis( nWordStart, i-1, levelNext, styler, foldAtElse, foldUtilityCmd );
605 
606         if( newLevel == levelNext )
607         {
608           if( foldAtElse && foldUtilityCmd )
609           {
610             if( NsisNextLineHasElse(i, startPos + length, styler) )
611               levelNext--;
612           }
613         }
614         else
615           levelNext = newLevel;
616         bArg1 = false;
617       }
618     }
619 
620     if( chCurr == '\n' )
621     {
622       if( bArg1 && foldAtElse && foldUtilityCmd && !blockComment )
623       {
624         if( NsisNextLineHasElse(i, startPos + length, styler) )
625           levelNext--;
626       }
627 
628       // If we are on a new line...
629       int levelUse = levelCurrent;
630 			int lev = levelUse | levelNext << 16;
631       if (levelUse < levelNext )
632 				lev |= SC_FOLDLEVELHEADERFLAG;
633 			if (lev != styler.LevelAt(lineCurrent))
634 				styler.SetLevel(lineCurrent, lev);
635 
636 			lineCurrent++;
637 			levelCurrent = levelNext;
638       bArg1 = true; // New line, lets look at first argument again
639       nWordStart = -1;
640     }
641   }
642 
643 	int levelUse = levelCurrent;
644 	int lev = levelUse | levelNext << 16;
645 	if (levelUse < levelNext)
646 		lev |= SC_FOLDLEVELHEADERFLAG;
647 	if (lev != styler.LevelAt(lineCurrent))
648 		styler.SetLevel(lineCurrent, lev);
649 }
650 
651 static const char * const nsisWordLists[] = {
652 	"Functions",
653 	"Variables",
654 	"Lables",
655 	"UserDefined",
656 	0, };
657 
658 
659 LexerModule lmNsis(SCLEX_NSIS, ColouriseNsisDoc, "nsis", FoldNsisDoc, nsisWordLists);
660 
661