43
55
return BASH_BASE_ERROR;
46
static inline bool isEOLChar(char ch) {
47
return (ch == '\r') || (ch == '\n');
50
static bool isSingleCharOp(char ch) {
54
return (NULL != strstr("rwxoRWXOezsfdlpSbctugkTBMACahGLNn", strCharSet));
57
static inline bool isBashOperator(char ch) {
58
if (ch == '^' || ch == '&' || ch == '\\' || ch == '%' ||
59
ch == '(' || ch == ')' || ch == '-' || ch == '+' ||
60
ch == '=' || ch == '|' || ch == '{' || ch == '}' ||
61
ch == '[' || ch == ']' || ch == ':' || ch == ';' ||
62
ch == '>' || ch == ',' || ch == '/' || ch == '<' ||
63
ch == '?' || ch == '!' || ch == '.' || ch == '~' ||
69
static int classifyWordBash(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
71
for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) {
72
s[i] = styler[start + i];
75
char chAttr = SCE_SH_IDENTIFIER;
76
if (keywords.InList(s))
78
styler.ColourTo(end, chAttr);
82
static inline int getBashNumberBase(unsigned int start, unsigned int end, Accessor &styler) {
58
static inline int getBashNumberBase(char *s) {
84
for (unsigned int i = 0; i < end - start + 1 && i < 10; i++) {
85
base = base * 10 + (styler[start + i] - '0');
62
base = base * 10 + (*s++ - '0');
87
if (base > 64 || (end - start) > 1) {
65
if (base > 64 || i > 2) {
88
66
return BASH_BASE_ERROR;
93
static inline bool isEndVar(char ch) {
94
return !isalnum(ch) && ch != '$' && ch != '_';
97
static inline bool isNonQuote(char ch) {
98
return isalnum(ch) || ch == '_';
101
static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) {
102
if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) {
106
if (*val != styler[pos++]) {
114
static char opposite(char ch) {
71
static int opposite(int ch) {
72
if (ch == '(') return ')';
73
if (ch == '[') return ']';
74
if (ch == '{') return '}';
75
if (ch == '<') return '>';
126
79
static void ColouriseBashDoc(unsigned int startPos, int length, int initStyle,
127
WordList *keywordlists[], Accessor &styler) {
129
// Lexer for bash often has to backtrack to start of current style to determine
130
// which characters are being used as quotes, how deeply nested is the
131
// start position and what the termination string is for here documents
80
WordList *keywordlists[], Accessor &styler) {
133
82
WordList &keywords = *keywordlists[0];
84
CharacterSet setWordStart(CharacterSet::setAlpha, "_");
85
// note that [+-] are often parts of identifiers in shell scripts
86
CharacterSet setWord(CharacterSet::setAlphaNum, "._+-");
87
CharacterSet setBashOperator(CharacterSet::setNone, "^&\\%()-+=|{}[]:;>,*/<?!.~@");
88
CharacterSet setSingleCharOp(CharacterSet::setNone, "rwxoRWXOezsfdlpSbctugkTBMACahGLNn");
89
CharacterSet setParam(CharacterSet::setAlphaNum, "$_");
90
CharacterSet setHereDoc(CharacterSet::setAlpha, "_\\-+!");
91
CharacterSet setHereDoc2(CharacterSet::setAlphaNum, "_-+!");
92
CharacterSet setLeftShift(CharacterSet::setDigits, "=$");
94
class HereDocCls { // Class to manage HERE document elements
137
96
int State; // 0: '<<' encountered
138
97
// 1: collect the delimiter
139
98
// 2: here doc text (lines after the delimiter)
140
char Quote; // the char after '<<'
99
int Quote; // the char after '<<'
141
100
bool Quoted; // true if Quote in ('\'','"','`')
142
101
bool Indent; // indented delimiter (for <<-)
143
102
int DelimiterLength; // strlen(Delimiter)
144
103
char *Delimiter; // the Delimiter, 256: sizeof PL_tokenbuf
150
109
DelimiterLength = 0;
151
110
Delimiter = new char[HERE_DELIM_MAX];
152
111
Delimiter[0] = '\0';
113
void Append(int ch) {
114
Delimiter[DelimiterLength++] = static_cast<char>(ch);
115
Delimiter[DelimiterLength] = '\0';
155
118
delete []Delimiter;
158
121
HereDocCls HereDoc;
123
class QuoteCls { // Class to manage quote pairs (simplified vs LexPerl)
178
135
Down = opposite(Up);
183
int state = initStyle;
185
unsigned int lengthDoc = startPos + length;
146
unsigned int endPos = startPos + length;
187
// If in a long distance lexical state, seek to the beginning to find quote characters
148
// Backtrack to beginning of style if required...
149
// If in a long distance lexical state, backtrack to find quote characters
150
if (initStyle == SCE_SH_HERE_Q) {
151
while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_SH_HERE_DELIM)) {
154
startPos = styler.LineStart(styler.GetLine(startPos));
155
initStyle = styler.StyleAt(startPos - 1);
188
157
// Bash strings can be multi-line with embedded newlines, so backtrack.
189
158
// Bash numbers have additional state during lexing, so backtrack too.
190
if (state == SCE_SH_HERE_Q) {
191
while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_SH_HERE_DELIM)) {
194
startPos = styler.LineStart(styler.GetLine(startPos));
195
state = styler.StyleAt(startPos - 1);
197
if (state == SCE_SH_STRING
198
|| state == SCE_SH_BACKTICKS
199
|| state == SCE_SH_CHARACTER
200
|| state == SCE_SH_NUMBER
201
|| state == SCE_SH_IDENTIFIER
202
|| state == SCE_SH_COMMENTLINE
204
while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) {
207
state = SCE_SH_DEFAULT;
210
styler.StartAt(startPos);
211
char chPrev = styler.SafeGetCharAt(startPos - 1);
214
char chNext = styler[startPos];
215
styler.StartSegment(startPos);
217
for (unsigned int i = startPos; i < lengthDoc; i++) {
219
// if the current character is not consumed due to the completion of an
220
// earlier style, lexing can be restarted via a simple goto
222
chNext = styler.SafeGetCharAt(i + 1);
223
char chNext2 = styler.SafeGetCharAt(i + 2);
225
if (styler.IsLeadByte(ch)) {
226
chNext = styler.SafeGetCharAt(i + 2);
232
if ((chPrev == '\r' && ch == '\n')) { // skip on DOS/Windows
233
styler.ColourTo(i, state);
238
if (HereDoc.State == 1 && isEOLChar(ch)) {
239
// Begin of here-doc (the line after the here-doc delimiter):
240
// Lexically, the here-doc starts from the next line after the >>, but the
241
// first line of here-doc seem to follow the style of the last EOL sequence
243
if (HereDoc.Quoted) {
244
if (state == SCE_SH_HERE_DELIM) {
245
// Missing quote at end of string! We are stricter than bash.
246
// Colour here-doc anyway while marking this bit as an error.
247
state = SCE_SH_ERROR;
249
styler.ColourTo(i - 1, state);
250
// HereDoc.Quote always == '\''
251
state = SCE_SH_HERE_Q;
253
styler.ColourTo(i - 1, state);
255
state = SCE_SH_HERE_Q;
259
if (state == SCE_SH_DEFAULT) {
260
if (ch == '\\') { // escaped character
264
styler.ColourTo(i, SCE_SH_IDENTIFIER);
265
} else if (isdigit(ch)) {
266
state = SCE_SH_NUMBER;
267
numBase = BASH_BASE_DECIMAL;
268
if (ch == '0') { // hex,octal
269
if (chNext == 'x' || chNext == 'X') {
270
numBase = BASH_BASE_HEX;
274
} else if (isdigit(chNext)) {
275
numBase = BASH_BASE_OCTAL;
159
if (initStyle == SCE_SH_STRING
160
|| initStyle == SCE_SH_BACKTICKS
161
|| initStyle == SCE_SH_CHARACTER
162
|| initStyle == SCE_SH_NUMBER
163
|| initStyle == SCE_SH_IDENTIFIER
164
|| initStyle == SCE_SH_COMMENTLINE) {
165
while ((startPos > 1) && (styler.StyleAt(startPos - 1) == initStyle)) {
168
initStyle = SCE_SH_DEFAULT;
171
StyleContext sc(startPos, endPos - startPos, initStyle, styler);
173
for (; sc.More(); sc.Forward()) {
175
// Determine if the current state should terminate.
177
case SCE_SH_OPERATOR:
178
sc.SetState(SCE_SH_DEFAULT);
181
// "." never used in Bash variable names but used in file names
182
if (!setWord.Contains(sc.ch)) {
184
sc.GetCurrent(s, sizeof(s));
185
if (s[0] != '-' && // for file operators
186
!keywords.InList(s)) {
187
sc.ChangeState(SCE_SH_IDENTIFIER);
278
} else if (iswordstart(ch)) {
280
if (!iswordchar(chNext) && chNext != '+' && chNext != '-') {
281
// We need that if length of word == 1!
282
// This test is copied from the SCE_SH_WORD handler.
283
classifyWordBash(styler.GetStartSegment(), i, keywords, styler);
284
state = SCE_SH_DEFAULT;
286
} else if (ch == '#') {
287
state = SCE_SH_COMMENTLINE;
288
} else if (ch == '\"') {
289
state = SCE_SH_STRING;
292
} else if (ch == '\'') {
293
state = SCE_SH_CHARACTER;
296
} else if (ch == '`') {
297
state = SCE_SH_BACKTICKS;
300
} else if (ch == '$') {
302
state = SCE_SH_PARAM;
304
} else if (chNext == '\'') {
305
state = SCE_SH_CHARACTER;
307
} else if (chNext == '"') {
308
state = SCE_SH_STRING;
310
} else if (chNext == '(' && chNext2 == '(') {
311
styler.ColourTo(i, SCE_SH_OPERATOR);
312
state = SCE_SH_DEFAULT;
314
} else if (chNext == '(' || chNext == '`') {
315
state = SCE_SH_BACKTICKS;
321
state = SCE_SH_SCALAR;
327
} else if (ch == '*') {
328
if (chNext == '*') { // exponentiation
333
styler.ColourTo(i, SCE_SH_OPERATOR);
334
} else if (ch == '<' && chNext == '<') {
335
state = SCE_SH_HERE_DELIM;
337
HereDoc.Indent = false;
338
} else if (ch == '-' // file test operators
339
&& isSingleCharOp(chNext)
340
&& !isalnum((chNext2 = styler.SafeGetCharAt(i+2)))) {
341
styler.ColourTo(i + 1, SCE_SH_WORD);
342
state = SCE_SH_DEFAULT;
346
} else if (isBashOperator(ch)) {
347
styler.ColourTo(i, SCE_SH_OPERATOR);
349
// keep colouring defaults to make restart easier
350
styler.ColourTo(i, SCE_SH_DEFAULT);
352
} else if (state == SCE_SH_NUMBER) {
353
int digit = translateBashDigit(ch);
354
if (numBase == BASH_BASE_DECIMAL) {
356
numBase = getBashNumberBase(styler.GetStartSegment(), i - 1, styler);
357
if (numBase == BASH_BASE_ERROR) // take the rest as comment
359
} else if (!isdigit(ch))
361
} else if (numBase == BASH_BASE_HEX) {
362
if ((digit < 16) || (digit >= 36 && digit <= 41)) {
363
// hex digit 0-9a-fA-F
366
} else if (numBase == BASH_BASE_OCTAL ||
367
numBase == BASH_BASE_OCTAL_ERROR) {
189
sc.SetState(SCE_SH_DEFAULT);
192
case SCE_SH_IDENTIFIER:
193
if (sc.chPrev == '\\') { // for escaped chars
194
sc.ForwardSetState(SCE_SH_DEFAULT);
195
} else if (!setWord.Contains(sc.ch)) {
196
sc.SetState(SCE_SH_DEFAULT);
200
digit = translateBashDigit(sc.ch);
201
if (numBase == BASH_BASE_DECIMAL) {
204
sc.GetCurrent(s, sizeof(s));
205
numBase = getBashNumberBase(s);
206
if (numBase != BASH_BASE_ERROR)
208
} else if (IsADigit(sc.ch))
210
} else if (numBase == BASH_BASE_HEX) {
211
if (IsADigit(sc.ch, 16))
213
#ifdef PEDANTIC_OCTAL
214
} else if (numBase == BASH_BASE_OCTAL ||
215
numBase == BASH_BASE_OCTAL_ERROR) {
369
218
if (digit <= 9) {
370
219
numBase = BASH_BASE_OCTAL_ERROR;
374
} else if (numBase == BASH_BASE_ERROR) {
377
} else { // DD#DDDD number style handling
378
if (digit != BASH_BASE_ERROR) {
380
// case-insensitive if base<=36
381
if (digit >= 36) digit -= 26;
383
if (digit >= numBase) {
223
} else if (numBase == BASH_BASE_ERROR) {
226
} else { // DD#DDDD number style handling
227
if (digit != BASH_BASE_ERROR) {
229
// case-insensitive if base<=36
230
if (digit >= 36) digit -= 26;
384
234
if (digit <= 9) {
385
235
numBase = BASH_BASE_ERROR;
391
if (numBase == BASH_BASE_ERROR ||
392
numBase == BASH_BASE_OCTAL_ERROR)
393
state = SCE_SH_ERROR;
394
styler.ColourTo(i - 1, state);
395
state = SCE_SH_DEFAULT;
399
} else if (state == SCE_SH_WORD) {
400
if (!iswordchar(chNext) && chNext != '+' && chNext != '-') {
401
// "." never used in Bash variable names
402
// but used in file names
403
classifyWordBash(styler.GetStartSegment(), i, keywords, styler);
404
state = SCE_SH_DEFAULT;
407
} else if (state == SCE_SH_IDENTIFIER) {
408
if (!iswordchar(chNext) && chNext != '+' && chNext != '-') {
409
styler.ColourTo(i, SCE_SH_IDENTIFIER);
410
state = SCE_SH_DEFAULT;
414
if (state == SCE_SH_COMMENTLINE) {
415
if (ch == '\\' && isEOLChar(chNext)) {
240
// fallthrough when number is at an end or error
241
if (numBase == BASH_BASE_ERROR
242
#ifdef PEDANTIC_OCTAL
243
|| numBase == BASH_BASE_OCTAL_ERROR
246
sc.ChangeState(SCE_SH_ERROR);
248
sc.SetState(SCE_SH_DEFAULT);
250
case SCE_SH_COMMENTLINE:
251
if (sc.ch == '\\' && (sc.chNext == '\r' || sc.chNext == '\n')) {
416
252
// comment continuation
417
if (chNext == '\r' && chNext2 == '\n') {
419
ch = styler.SafeGetCharAt(i);
420
chNext = styler.SafeGetCharAt(i + 1);
254
if (sc.ch == '\r' && sc.chNext == '\n') {
426
} else if (isEOLChar(ch)) {
427
styler.ColourTo(i - 1, state);
428
state = SCE_SH_DEFAULT;
430
} else if (isEOLChar(chNext)) {
431
styler.ColourTo(i, state);
432
state = SCE_SH_DEFAULT;
257
} else if (sc.atLineEnd) {
258
sc.ForwardSetState(SCE_SH_DEFAULT);
434
} else if (state == SCE_SH_HERE_DELIM) {
261
case SCE_SH_HERE_DELIM:
436
262
// From Bash info:
437
263
// ---------------
438
264
// Specifier format is: <<[-]WORD
440
266
// Whitespace acceptable after <<[-] operator
442
268
if (HereDoc.State == 0) { // '<<' encountered
444
HereDoc.Quote = chNext;
269
HereDoc.Quote = sc.chNext;
445
270
HereDoc.Quoted = false;
446
271
HereDoc.DelimiterLength = 0;
447
272
HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
448
if (chNext == '\'' || chNext == '\"') { // a quoted here-doc delimiter (' or ")
273
if (sc.chNext == '\'' || sc.chNext == '\"') { // a quoted here-doc delimiter (' or ")
452
275
HereDoc.Quoted = true;
453
} else if (!HereDoc.Indent && chNext == '-') { // <<- indent case
277
} else if (!HereDoc.Indent && sc.chNext == '-') { // <<- indent case
454
278
HereDoc.Indent = true;
456
} else if (isalpha(chNext) || chNext == '_' || chNext == '\\'
457
|| chNext == '-' || chNext == '+' || chNext == '!') {
279
} else if (setHereDoc.Contains(sc.chNext)) {
458
280
// an unquoted here-doc delimiter, no special handling
459
// TODO check what exactly bash considers part of the delim
460
} else if (chNext == '<') { // HERE string <<<
464
styler.ColourTo(i, SCE_SH_HERE_DELIM);
465
state = SCE_SH_DEFAULT;
467
} else if (isspacechar(chNext)) {
281
// TODO check what exactly bash considers part of the delim
283
} else if (sc.chNext == '<') { // HERE string <<<
285
sc.ForwardSetState(SCE_SH_DEFAULT);
286
} else if (IsASpace(sc.chNext)) {
468
287
// eat whitespace
470
} else if (isdigit(chNext) || chNext == '=' || chNext == '$') {
288
} else if (setLeftShift.Contains(sc.chNext)) {
471
289
// left shift << or <<= operator cases
472
styler.ColourTo(i, SCE_SH_OPERATOR);
473
state = SCE_SH_DEFAULT;
290
sc.ChangeState(SCE_SH_OPERATOR);
291
sc.ForwardSetState(SCE_SH_DEFAULT);
476
293
// symbols terminates; deprecated zero-length delimiter
478
296
} else if (HereDoc.State == 1) { // collect the delimiter
479
297
if (HereDoc.Quoted) { // a quoted here-doc delimiter
480
if (ch == HereDoc.Quote) { // closing quote => end of delimiter
481
styler.ColourTo(i, state);
482
state = SCE_SH_DEFAULT;
298
if (sc.ch == HereDoc.Quote) { // closing quote => end of delimiter
299
sc.ForwardSetState(SCE_SH_DEFAULT);
484
if (ch == '\\' && chNext == HereDoc.Quote) { // escaped quote
301
if (sc.ch == '\\' && sc.chNext == HereDoc.Quote) { // escaped quote
489
HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
490
HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
304
HereDoc.Append(sc.ch);
492
306
} else { // an unquoted here-doc delimiter
493
if (isalnum(ch) || ch == '_' || ch == '-' || ch == '+' || ch == '!') {
494
HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
495
HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
496
} else if (ch == '\\') {
307
if (setHereDoc2.Contains(sc.ch)) {
308
HereDoc.Append(sc.ch);
309
} else if (sc.ch == '\\') {
497
310
// skip escape prefix
499
styler.ColourTo(i - 1, state);
500
state = SCE_SH_DEFAULT;
504
if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) {
505
styler.ColourTo(i - 1, state);
506
state = SCE_SH_ERROR;
510
} else if (HereDoc.State == 2) {
511
// state == SCE_SH_HERE_Q
512
if (isMatch(styler, lengthDoc, i, HereDoc.Delimiter)) {
513
if (!HereDoc.Indent && isEOLChar(chPrev)) {
515
// standard HERE delimiter
516
i += HereDoc.DelimiterLength;
517
chPrev = styler.SafeGetCharAt(i - 1);
518
ch = styler.SafeGetCharAt(i);
520
styler.ColourTo(i - 1, state);
521
state = SCE_SH_DEFAULT;
525
chNext = styler.SafeGetCharAt(i + 1);
526
} else if (HereDoc.Indent) {
527
// indented HERE delimiter
528
unsigned int bk = (i > 0)? i - 1: 0;
530
ch = styler.SafeGetCharAt(bk--);
533
} else if (!isspacechar(ch)) {
534
break; // got leading non-whitespace
539
} else if (state == SCE_SH_SCALAR) { // variable names
541
if ((state == SCE_SH_SCALAR)
542
&& i == (styler.GetStartSegment() + 1)) {
312
sc.SetState(SCE_SH_DEFAULT);
315
if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) { // force blowup
316
sc.SetState(SCE_SH_ERROR);
322
// HereDoc.State == 2
323
if (sc.atLineStart) {
324
sc.SetState(SCE_SH_HERE_Q);
326
while (IsASpace(sc.ch) && !sc.atLineEnd) { // whitespace prefix
331
sc.SetState(SCE_SH_HERE_Q);
332
while (!sc.atLineEnd) {
335
char s[HERE_DELIM_MAX];
336
sc.GetCurrent(s, sizeof(s));
337
if (sc.LengthCurrent() == 0)
339
if (s[strlen(s) - 1] == '\r')
340
s[strlen(s) - 1] = '\0';
341
if (strcmp(HereDoc.Delimiter, s) == 0) {
342
if ((prefixws > 0 && HereDoc.Indent) || // indentation rule
343
(prefixws == 0 && !HereDoc.Indent)) {
344
sc.SetState(SCE_SH_DEFAULT);
350
case SCE_SH_SCALAR: // variable names
351
if (!setParam.Contains(sc.ch)) {
352
if (sc.LengthCurrent() == 1) {
543
353
// Special variable: $(, $_ etc.
544
styler.ColourTo(i, state);
545
state = SCE_SH_DEFAULT;
354
sc.ForwardSetState(SCE_SH_DEFAULT);
547
styler.ColourTo(i - 1, state);
548
state = SCE_SH_DEFAULT;
356
sc.SetState(SCE_SH_DEFAULT);
552
} else if (state == SCE_SH_STRING
553
|| state == SCE_SH_CHARACTER
554
|| state == SCE_SH_BACKTICKS
555
|| state == SCE_SH_PARAM
557
if (!Quote.Down && !isspacechar(ch)) {
559
} else if (ch == '\\' && Quote.Up != '\\') {
562
chNext = styler.SafeGetCharAt(i + 1);
563
} else if (ch == Quote.Down) {
360
case SCE_SH_STRING: // delimited styles
361
case SCE_SH_CHARACTER:
362
case SCE_SH_BACKTICKS:
364
if (sc.ch == '\\' && Quote.Up != '\\') {
366
} else if (sc.ch == Quote.Down) {
565
368
if (Quote.Count == 0) {
567
if (Quote.Rep <= 0) {
568
styler.ColourTo(i, state);
569
state = SCE_SH_DEFAULT;
572
if (Quote.Up == Quote.Down) {
369
sc.ForwardSetState(SCE_SH_DEFAULT);
576
} else if (ch == Quote.Up) {
371
} else if (sc.ch == Quote.Up) {
581
if (state == SCE_SH_ERROR) {
377
// Must check end of HereDoc state 1 before default state is handled
378
if (HereDoc.State == 1 && sc.atLineEnd) {
379
// Begin of here-doc (the line after the here-doc delimiter):
380
// Lexically, the here-doc starts from the next line after the >>, but the
381
// first line of here-doc seem to follow the style of the last EOL sequence
383
if (HereDoc.Quoted) {
384
if (sc.state == SCE_SH_HERE_DELIM) {
385
// Missing quote at end of string! We are stricter than bash.
386
// Colour here-doc anyway while marking this bit as an error.
387
sc.ChangeState(SCE_SH_ERROR);
389
// HereDoc.Quote always == '\''
391
sc.SetState(SCE_SH_HERE_Q);
394
// Determine if a new state should be entered.
395
if (sc.state == SCE_SH_DEFAULT) {
396
if (sc.ch == '\\') { // escaped character
397
sc.SetState(SCE_SH_IDENTIFIER);
398
} else if (IsADigit(sc.ch)) {
399
sc.SetState(SCE_SH_NUMBER);
400
numBase = BASH_BASE_DECIMAL;
401
if (sc.ch == '0') { // hex,octal
402
if (sc.chNext == 'x' || sc.chNext == 'X') {
403
numBase = BASH_BASE_HEX;
405
} else if (IsADigit(sc.chNext)) {
406
#ifdef PEDANTIC_OCTAL
407
numBase = BASH_BASE_OCTAL;
409
numBase = BASH_BASE_HEX;
413
} else if (setWordStart.Contains(sc.ch)) {
414
sc.SetState(SCE_SH_WORD);
415
} else if (sc.ch == '#') {
416
sc.SetState(SCE_SH_COMMENTLINE);
417
} else if (sc.ch == '\"') {
418
sc.SetState(SCE_SH_STRING);
420
} else if (sc.ch == '\'') {
421
sc.SetState(SCE_SH_CHARACTER);
423
} else if (sc.ch == '`') {
424
sc.SetState(SCE_SH_BACKTICKS);
426
} else if (sc.ch == '$') {
427
sc.SetState(SCE_SH_SCALAR);
430
sc.ChangeState(SCE_SH_PARAM);
431
} else if (sc.ch == '\'') {
432
sc.ChangeState(SCE_SH_CHARACTER);
433
} else if (sc.ch == '"') {
434
sc.ChangeState(SCE_SH_STRING);
435
} else if (sc.ch == '(' || sc.ch == '`') {
436
sc.ChangeState(SCE_SH_BACKTICKS);
437
if (sc.chNext == '(') { // $(( is lexed as operator
438
sc.ChangeState(SCE_SH_OPERATOR);
441
continue; // scalar has no delimiter pair
443
// fallthrough, open delim for $[{'"(`]
445
} else if (sc.Match('<', '<')) {
446
sc.SetState(SCE_SH_HERE_DELIM);
448
HereDoc.Indent = false;
449
} else if (sc.ch == '-' && // one-char file test operators
450
setSingleCharOp.Contains(sc.chNext) &&
451
!setWord.Contains(sc.GetRelative(2)) &&
452
IsASpace(sc.chPrev)) {
453
sc.SetState(SCE_SH_WORD);
455
} else if (setBashOperator.Contains(sc.ch)) {
456
sc.SetState(SCE_SH_OPERATOR);
586
styler.ColourTo(lengthDoc - 1, state);
589
463
static bool IsCommentLine(int line, Accessor &styler) {