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