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.
16
#include "CharClassify.h"
20
#include "Scintilla.h"
24
using namespace Scintilla;
28
// located in SciLexer.h
31
#define SCE_NSIS_DEFAULT 0
32
#define SCE_NSIS_COMMENT 1
33
#define SCE_NSIS_STRINGDQ 2
34
#define SCE_NSIS_STRINGLQ 3
35
#define SCE_NSIS_STRINGRQ 4
36
#define SCE_NSIS_FUNCTION 5
37
#define SCE_NSIS_VARIABLE 6
38
#define SCE_NSIS_LABEL 7
39
#define SCE_NSIS_USERDEFINED 8
40
#define SCE_NSIS_SECTIONDEF 9
41
#define SCE_NSIS_SUBSECTIONDEF 10
42
#define SCE_NSIS_IFDEFINEDEF 11
43
#define SCE_NSIS_MACRODEF 12
44
#define SCE_NSIS_STRINGVAR 13
45
#define SCE_NSIS_NUMBER 14
46
// ADDED for Scintilla v1.63
47
#define SCE_NSIS_SECTIONGROUP 15
48
#define SCE_NSIS_PAGEEX 16
49
#define SCE_NSIS_FUNCTIONDEF 17
50
#define SCE_NSIS_COMMENTBOX 18
53
static bool isNsisNumber(char ch)
55
return (ch >= '0' && ch <= '9');
58
static bool isNsisChar(char ch)
60
return (ch == '.' ) || (ch == '_' ) || isNsisNumber(ch) || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
63
static bool isNsisLetter(char ch)
65
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
68
static bool NsisNextLineHasElse(unsigned int start, unsigned int end, Accessor &styler)
71
for( unsigned int i = start; i < end; i++ )
73
char cNext = styler.SafeGetCharAt( i );
81
if( nNextLine == -1 ) // We never found the next line...
84
for( unsigned int firstChar = nNextLine; firstChar < end; firstChar++ )
86
char cNext = styler.SafeGetCharAt( firstChar );
93
if( styler.Match(firstChar, "!else") )
102
static int NsisCmp( const char *s1, const char *s2, bool bIgnoreCase )
105
return CompareCaseInsensitive( s1, s2);
107
return strcmp( s1, s2 );
110
static int calculateFoldNsis(unsigned int start, unsigned int end, int foldlevel, Accessor &styler, bool bElse, bool foldUtilityCmd )
112
int style = styler.StyleAt(end);
114
// If the word is too long, it is not what we are looking for
115
if( end - start > 20 )
120
// Check the style at this point, if it is not valid, then return zero
121
if( style != SCE_NSIS_FUNCTIONDEF && style != SCE_NSIS_SECTIONDEF &&
122
style != SCE_NSIS_SUBSECTIONDEF && style != SCE_NSIS_IFDEFINEDEF &&
123
style != SCE_NSIS_MACRODEF && style != SCE_NSIS_SECTIONGROUP &&
124
style != SCE_NSIS_PAGEEX )
129
if( style != SCE_NSIS_FUNCTIONDEF && style != SCE_NSIS_SECTIONDEF &&
130
style != SCE_NSIS_SUBSECTIONDEF && style != SCE_NSIS_SECTIONGROUP &&
131
style != SCE_NSIS_PAGEEX )
135
int newFoldlevel = foldlevel;
136
bool bIgnoreCase = false;
137
if( styler.GetPropertyInt("nsis.ignorecase") == 1 )
140
char s[20]; // The key word we are looking for has atmost 13 characters
141
for (unsigned int i = 0; i < end - start + 1 && i < 19; i++)
143
s[i] = static_cast<char>( styler[ start + i ] );
149
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 )
151
else if( NsisCmp(s, "!endif", bIgnoreCase) == 0 || NsisCmp(s, "!macroend", bIgnoreCase ) == 0 )
153
else if( bElse && NsisCmp(s, "!else", bIgnoreCase) == 0 )
158
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 )
160
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 )
167
static int classifyWordNsis(unsigned int start, unsigned int end, WordList *keywordLists[], Accessor &styler )
169
bool bIgnoreCase = false;
170
if( styler.GetPropertyInt("nsis.ignorecase") == 1 )
173
bool bUserVars = false;
174
if( styler.GetPropertyInt("nsis.uservars") == 1 )
179
WordList &Functions = *keywordLists[0];
180
WordList &Variables = *keywordLists[1];
181
WordList &Lables = *keywordLists[2];
182
WordList &UserDefined = *keywordLists[3];
184
for (unsigned int i = 0; i < end - start + 1 && i < 99; i++)
187
s[i] = static_cast<char>( tolower(styler[ start + i ] ) );
189
s[i] = static_cast<char>( styler[ start + i ] );
193
// Check for special words...
194
if( NsisCmp(s, "!macro", bIgnoreCase ) == 0 || NsisCmp(s, "!macroend", bIgnoreCase) == 0 ) // Covers !macro and !macroend
195
return SCE_NSIS_MACRODEF;
197
if( NsisCmp(s, "!ifdef", bIgnoreCase ) == 0 || NsisCmp(s, "!ifndef", bIgnoreCase) == 0 || NsisCmp(s, "!endif", bIgnoreCase) == 0 ) // Covers !ifdef, !ifndef and !endif
198
return SCE_NSIS_IFDEFINEDEF;
200
if( NsisCmp(s, "!if", bIgnoreCase ) == 0 || NsisCmp(s, "!else", bIgnoreCase ) == 0 ) // Covers !if and else
201
return SCE_NSIS_IFDEFINEDEF;
203
if (NsisCmp(s, "!ifmacrodef", bIgnoreCase ) == 0 || NsisCmp(s, "!ifmacrondef", bIgnoreCase ) == 0 ) // Covers !ifmacrodef and !ifnmacrodef
204
return SCE_NSIS_IFDEFINEDEF;
206
if( NsisCmp(s, "SectionGroup", bIgnoreCase) == 0 || NsisCmp(s, "SectionGroupEnd", bIgnoreCase) == 0 ) // Covers SectionGroup and SectionGroupEnd
207
return SCE_NSIS_SECTIONGROUP;
209
if( NsisCmp(s, "Section", bIgnoreCase ) == 0 || NsisCmp(s, "SectionEnd", bIgnoreCase) == 0 ) // Covers Section and SectionEnd
210
return SCE_NSIS_SECTIONDEF;
212
if( NsisCmp(s, "SubSection", bIgnoreCase) == 0 || NsisCmp(s, "SubSectionEnd", bIgnoreCase) == 0 ) // Covers SubSection and SubSectionEnd
213
return SCE_NSIS_SUBSECTIONDEF;
215
if( NsisCmp(s, "PageEx", bIgnoreCase) == 0 || NsisCmp(s, "PageExEnd", bIgnoreCase) == 0 ) // Covers PageEx and PageExEnd
216
return SCE_NSIS_PAGEEX;
218
if( NsisCmp(s, "Function", bIgnoreCase) == 0 || NsisCmp(s, "FunctionEnd", bIgnoreCase) == 0 ) // Covers Function and FunctionEnd
219
return SCE_NSIS_FUNCTIONDEF;
221
if ( Functions.InList(s) )
222
return SCE_NSIS_FUNCTION;
224
if ( Variables.InList(s) )
225
return SCE_NSIS_VARIABLE;
227
if ( Lables.InList(s) )
228
return SCE_NSIS_LABEL;
230
if( UserDefined.InList(s) )
231
return SCE_NSIS_USERDEFINED;
235
if( s[1] == '{' && s[strlen(s)-1] == '}' )
236
return SCE_NSIS_VARIABLE;
239
// See if the variable is a user defined variable
240
if( s[0] == '$' && bUserVars )
242
bool bHasSimpleNsisChars = true;
243
for (unsigned int j = 1; j < end - start + 1 && j < 99; j++)
245
if( !isNsisChar( s[j] ) )
247
bHasSimpleNsisChars = false;
252
if( bHasSimpleNsisChars )
253
return SCE_NSIS_VARIABLE;
256
// To check for numbers
257
if( isNsisNumber( s[0] ) )
259
bool bHasSimpleNsisNumber = true;
260
for (unsigned int j = 1; j < end - start + 1 && j < 99; j++)
262
if( !isNsisNumber( s[j] ) )
264
bHasSimpleNsisNumber = false;
269
if( bHasSimpleNsisNumber )
270
return SCE_NSIS_NUMBER;
273
return SCE_NSIS_DEFAULT;
276
static void ColouriseNsisDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler)
278
int state = SCE_NSIS_DEFAULT;
280
state = styler.StyleAt(startPos-1); // Use the style from the previous line, usually default, but could be commentbox
282
styler.StartAt( startPos );
283
styler.GetLine( startPos );
285
unsigned int nLengthDoc = startPos + length;
286
styler.StartSegment( startPos );
289
bool bVarInString = false;
290
bool bClassicVarInString = false;
293
for( i = startPos; i < nLengthDoc; i++ )
295
cCurrChar = styler.SafeGetCharAt( i );
296
char cNextChar = styler.SafeGetCharAt(i+1);
300
case SCE_NSIS_DEFAULT:
301
if( cCurrChar == ';' || cCurrChar == '#' ) // we have a comment line
303
styler.ColourTo(i-1, state );
304
state = SCE_NSIS_COMMENT;
307
if( cCurrChar == '"' )
309
styler.ColourTo(i-1, state );
310
state = SCE_NSIS_STRINGDQ;
311
bVarInString = false;
312
bClassicVarInString = false;
315
if( cCurrChar == '\'' )
317
styler.ColourTo(i-1, state );
318
state = SCE_NSIS_STRINGRQ;
319
bVarInString = false;
320
bClassicVarInString = false;
323
if( cCurrChar == '`' )
325
styler.ColourTo(i-1, state );
326
state = SCE_NSIS_STRINGLQ;
327
bVarInString = false;
328
bClassicVarInString = false;
332
// NSIS KeyWord,Function, Variable, UserDefined:
333
if( cCurrChar == '$' || isNsisChar(cCurrChar) || cCurrChar == '!' )
335
styler.ColourTo(i-1,state);
336
state = SCE_NSIS_FUNCTION;
338
// If it is a number, we must check and set style here first...
339
if( isNsisNumber(cCurrChar) && (cNextChar == '\t' || cNextChar == ' ' || cNextChar == '\r' || cNextChar == '\n' ) )
340
styler.ColourTo( i, SCE_NSIS_NUMBER);
345
if( cCurrChar == '/' && cNextChar == '*' )
347
styler.ColourTo(i-1,state);
348
state = SCE_NSIS_COMMENTBOX;
353
case SCE_NSIS_COMMENT:
354
if( cNextChar == '\n' || cNextChar == '\r' )
357
if( cCurrChar == '\\' )
359
styler.ColourTo(i-2,state);
360
styler.ColourTo(i,SCE_NSIS_DEFAULT);
364
styler.ColourTo(i,state);
365
state = SCE_NSIS_DEFAULT;
369
case SCE_NSIS_STRINGDQ:
370
case SCE_NSIS_STRINGLQ:
371
case SCE_NSIS_STRINGRQ:
373
if( styler.SafeGetCharAt(i-1) == '\\' && styler.SafeGetCharAt(i-2) == '$' )
374
break; // Ignore the next character, even if it is a quote of some sort
376
if( cCurrChar == '"' && state == SCE_NSIS_STRINGDQ )
378
styler.ColourTo(i,state);
379
state = SCE_NSIS_DEFAULT;
383
if( cCurrChar == '`' && state == SCE_NSIS_STRINGLQ )
385
styler.ColourTo(i,state);
386
state = SCE_NSIS_DEFAULT;
390
if( cCurrChar == '\'' && state == SCE_NSIS_STRINGRQ )
392
styler.ColourTo(i,state);
393
state = SCE_NSIS_DEFAULT;
397
if( cNextChar == '\r' || cNextChar == '\n' )
399
int nCurLine = styler.GetLine(i+1);
401
// We need to check if the previous line has a \ in it...
402
bool bNextLine = false;
406
if( styler.GetLine(nBack) != nCurLine )
409
char cTemp = styler.SafeGetCharAt(nBack, 'a'); // Letter 'a' is safe here
416
if( cTemp != '\r' && cTemp != '\n' && cTemp != '\t' && cTemp != ' ' )
424
styler.ColourTo(i+1,state);
426
if( bNextLine == false )
428
styler.ColourTo(i,state);
429
state = SCE_NSIS_DEFAULT;
434
case SCE_NSIS_FUNCTION:
437
if( cCurrChar == '$' )
438
state = SCE_NSIS_DEFAULT;
439
else if( cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' ) )
440
state = SCE_NSIS_DEFAULT;
441
else if( (isNsisChar(cCurrChar) && !isNsisChar( cNextChar) && cNextChar != '}') || cCurrChar == '}' )
443
state = classifyWordNsis( styler.GetStartSegment(), i, keywordLists, styler );
444
styler.ColourTo( i, state);
445
state = SCE_NSIS_DEFAULT;
447
else if( !isNsisChar( cCurrChar ) && cCurrChar != '{' && cCurrChar != '}' )
449
if( classifyWordNsis( styler.GetStartSegment(), i-1, keywordLists, styler) == SCE_NSIS_NUMBER )
450
styler.ColourTo( i-1, SCE_NSIS_NUMBER );
452
state = SCE_NSIS_DEFAULT;
454
if( cCurrChar == '"' )
456
state = SCE_NSIS_STRINGDQ;
457
bVarInString = false;
458
bClassicVarInString = false;
460
else if( cCurrChar == '`' )
462
state = SCE_NSIS_STRINGLQ;
463
bVarInString = false;
464
bClassicVarInString = false;
466
else if( cCurrChar == '\'' )
468
state = SCE_NSIS_STRINGRQ;
469
bVarInString = false;
470
bClassicVarInString = false;
472
else if( cCurrChar == '#' || cCurrChar == ';' )
474
state = SCE_NSIS_COMMENT;
478
case SCE_NSIS_COMMENTBOX:
480
if( styler.SafeGetCharAt(i-1) == '*' && cCurrChar == '/' )
482
styler.ColourTo(i,state);
483
state = SCE_NSIS_DEFAULT;
488
if( state == SCE_NSIS_COMMENT || state == SCE_NSIS_COMMENTBOX )
490
styler.ColourTo(i,state);
492
else if( state == SCE_NSIS_STRINGDQ || state == SCE_NSIS_STRINGLQ || state == SCE_NSIS_STRINGRQ )
494
bool bIngoreNextDollarSign = false;
495
bool bUserVars = false;
496
if( styler.GetPropertyInt("nsis.uservars") == 1 )
499
if( bVarInString && cCurrChar == '$' )
501
bVarInString = false;
502
bIngoreNextDollarSign = true;
504
else if( bVarInString && cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' || cNextChar == '"' || cNextChar == '`' || cNextChar == '\'' ) )
506
styler.ColourTo( i+1, SCE_NSIS_STRINGVAR);
507
bVarInString = false;
508
bIngoreNextDollarSign = false;
511
// Covers "$INSTDIR and user vars like $MYVAR"
512
else if( bVarInString && !isNsisChar(cNextChar) )
514
int nWordState = classifyWordNsis( styler.GetStartSegment(), i, keywordLists, styler);
515
if( nWordState == SCE_NSIS_VARIABLE )
516
styler.ColourTo( i, SCE_NSIS_STRINGVAR);
518
styler.ColourTo( i, SCE_NSIS_STRINGVAR);
519
bVarInString = false;
521
// Covers "${TEST}..."
522
else if( bClassicVarInString && cNextChar == '}' )
524
styler.ColourTo( i+1, SCE_NSIS_STRINGVAR);
525
bClassicVarInString = false;
528
// Start of var in string
529
if( !bIngoreNextDollarSign && cCurrChar == '$' && cNextChar == '{' )
531
styler.ColourTo( i-1, state);
532
bClassicVarInString = true;
533
bVarInString = false;
535
else if( !bIngoreNextDollarSign && cCurrChar == '$' )
537
styler.ColourTo( i-1, state);
539
bClassicVarInString = false;
544
// Colourise remaining document
545
styler.ColourTo(nLengthDoc-1,state);
548
static void FoldNsisDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler)
550
// No folding enabled, no reason to continue...
551
if( styler.GetPropertyInt("fold") == 0 )
554
bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) == 1;
555
bool foldUtilityCmd = styler.GetPropertyInt("nsis.foldutilcmd", 1) == 1;
556
bool blockComment = false;
558
int lineCurrent = styler.GetLine(startPos);
559
unsigned int safeStartPos = styler.LineStart( lineCurrent );
564
int levelCurrent = SC_FOLDLEVELBASE;
566
levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
567
int levelNext = levelCurrent;
568
int style = styler.StyleAt(safeStartPos);
569
if( style == SCE_NSIS_COMMENTBOX )
571
if( styler.SafeGetCharAt(safeStartPos) == '/' && styler.SafeGetCharAt(safeStartPos+1) == '*' )
576
for (unsigned int i = safeStartPos; i < startPos + length; i++)
578
char chCurr = styler.SafeGetCharAt(i);
579
style = styler.StyleAt(i);
580
if( blockComment && style != SCE_NSIS_COMMENTBOX )
583
blockComment = false;
585
else if( !blockComment && style == SCE_NSIS_COMMENTBOX )
591
if( bArg1 && !blockComment)
593
if( nWordStart == -1 && (isNsisLetter(chCurr) || chCurr == '!') )
597
else if( isNsisLetter(chCurr) == false && nWordStart > -1 )
599
int newLevel = calculateFoldNsis( nWordStart, i-1, levelNext, styler, foldAtElse, foldUtilityCmd );
601
if( newLevel == levelNext )
603
if( foldAtElse && foldUtilityCmd )
605
if( NsisNextLineHasElse(i, startPos + length, styler) )
610
levelNext = newLevel;
617
if( bArg1 && foldAtElse && foldUtilityCmd && !blockComment )
619
if( NsisNextLineHasElse(i, startPos + length, styler) )
623
// If we are on a new line...
624
int levelUse = levelCurrent;
625
int lev = levelUse | levelNext << 16;
626
if (levelUse < levelNext )
627
lev |= SC_FOLDLEVELHEADERFLAG;
628
if (lev != styler.LevelAt(lineCurrent))
629
styler.SetLevel(lineCurrent, lev);
632
levelCurrent = levelNext;
633
bArg1 = true; // New line, lets look at first argument again
638
int levelUse = levelCurrent;
639
int lev = levelUse | levelNext << 16;
640
if (levelUse < levelNext)
641
lev |= SC_FOLDLEVELHEADERFLAG;
642
if (lev != styler.LevelAt(lineCurrent))
643
styler.SetLevel(lineCurrent, lev);
646
static const char * const nsisWordLists[] = {
654
LexerModule lmNsis(SCLEX_NSIS, ColouriseNsisDoc, "nsis", FoldNsisDoc, nsisWordLists);