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(unsigned int start, unsigned int end, Accessor &styler)
75
for( unsigned int i = start; i < end; i++ )
77
char cNext = styler.SafeGetCharAt( i );
85
if( nNextLine == -1 ) // We never found the next line...
88
for( unsigned int 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(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 )
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
145
for (unsigned int i = 0; i < end - start + 1 && i < 19; i++)
147
s[i] = static_cast<char>( styler[ start + i ] );
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 )
155
else if( NsisCmp(s, "!endif", bIgnoreCase) == 0 || NsisCmp(s, "!macroend", bIgnoreCase ) == 0 )
157
else if( bElse && NsisCmp(s, "!else", bIgnoreCase) == 0 )
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 )
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 )
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 )
177
bool bUserVars = false;
178
if( styler.GetPropertyInt("nsis.uservars") == 1 )
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++)
191
s[i] = static_cast<char>( tolower(styler[ start + i ] ) );
193
s[i] = static_cast<char>( styler[ start + i ] );
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;
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;
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;
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;
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 );
293
bool bVarInString = false;
294
bool bClassicVarInString = false;
297
for( i = startPos; i < nLengthDoc; i++ )
299
cCurrChar = styler.SafeGetCharAt( i );
300
char cNextChar = styler.SafeGetCharAt(i+1);
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;
311
if( cCurrChar == '"' )
313
styler.ColourTo(i-1, state );
314
state = SCE_NSIS_STRINGDQ;
315
bVarInString = false;
316
bClassicVarInString = false;
319
if( cCurrChar == '\'' )
321
styler.ColourTo(i-1, state );
322
state = SCE_NSIS_STRINGRQ;
323
bVarInString = false;
324
bClassicVarInString = false;
327
if( cCurrChar == '`' )
329
styler.ColourTo(i-1, state );
330
state = SCE_NSIS_STRINGLQ;
331
bVarInString = false;
332
bClassicVarInString = false;
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);
349
if( cCurrChar == '/' && cNextChar == '*' )
351
styler.ColourTo(i-1,state);
352
state = SCE_NSIS_COMMENTBOX;
357
case SCE_NSIS_COMMENT:
358
if( cNextChar == '\n' || cNextChar == '\r' )
361
if( cCurrChar == '\\' )
363
styler.ColourTo(i-2,state);
364
styler.ColourTo(i,SCE_NSIS_DEFAULT);
368
styler.ColourTo(i,state);
369
state = SCE_NSIS_DEFAULT;
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;
387
if( cCurrChar == '`' && state == SCE_NSIS_STRINGLQ )
389
styler.ColourTo(i,state);
390
state = SCE_NSIS_DEFAULT;
394
if( cCurrChar == '\'' && state == SCE_NSIS_STRINGRQ )
396
styler.ColourTo(i,state);
397
state = SCE_NSIS_DEFAULT;
401
if( cNextChar == '\r' || cNextChar == '\n' )
403
int nCurLine = styler.GetLine(i+1);
405
// We need to check if the previous line has a \ in it...
406
bool bNextLine = false;
410
if( styler.GetLine(nBack) != nCurLine )
413
char cTemp = styler.SafeGetCharAt(nBack, 'a'); // Letter 'a' is safe here
420
if( cTemp != '\r' && cTemp != '\n' && cTemp != '\t' && cTemp != ' ' )
428
styler.ColourTo(i+1,state);
430
if( bNextLine == false )
432
styler.ColourTo(i,state);
433
state = SCE_NSIS_DEFAULT;
438
case SCE_NSIS_FUNCTION:
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;
482
case SCE_NSIS_COMMENTBOX:
484
if( styler.SafeGetCharAt(i-1) == '*' && cCurrChar == '/' )
486
styler.ColourTo(i,state);
487
state = SCE_NSIS_DEFAULT;
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 )
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);
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);
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 )
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 );
568
int levelCurrent = SC_FOLDLEVELBASE;
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) == '*' )
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 )
587
blockComment = false;
589
else if( !blockComment && style == SCE_NSIS_COMMENTBOX )
595
if( bArg1 && !blockComment)
597
if( nWordStart == -1 && (isNsisLetter(chCurr) || chCurr == '!') )
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) )
614
levelNext = newLevel;
621
if( bArg1 && foldAtElse && foldUtilityCmd && !blockComment )
623
if( NsisNextLineHasElse(i, startPos + length, styler) )
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);
636
levelCurrent = levelNext;
637
bArg1 = true; // New line, lets look at first argument again
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[] = {
658
LexerModule lmNsis(SCLEX_NSIS, ColouriseNsisDoc, "nsis", FoldNsisDoc, nsisWordLists);