1 skipped line |
2 | | 2 | | /** @file LexPerl.cxx |
|
3 | | 3 | | ** Lexer for subset of Perl. |
|
|
5 | | // Lexical analysis fixes by Kein-Hong Man <mkh@pl.jaring.my> 2003-2004 | | 5 | | // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org> |
6 | | // Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org> | | 6 | | // Lexical analysis fixes by Kein-Hong Man <mkh@pl.jaring.my> |
7 | | 7 | | // The License.txt file describes the conditions under which this software may be distributed. |
|
|
|
10 skipped lines |
20 | | 20 | | #include "Scintilla.h" |
|
21 | | 21 | | #include "SciLexer.h" |
|
|
23 | | #define PERLNUM_DECIMAL 1 | | 23 | | #define PERLNUM_BINARY 1 // order is significant: 1-4 cannot have a dot |
24 | | #define PERLNUM_NON_DEC 2 | | 24 | | #define PERLNUM_HEX 2 |
25 | | #define PERLNUM_FLOAT 3 | | 25 | | #define PERLNUM_OCTAL 3 |
26 | | #define PERLNUM_VECTOR 4 | | 26 | | #define PERLNUM_FLOAT 4 // actually exponent part |
| | 27 | | #define PERLNUM_DECIMAL 5 // 1-5 are numbers; 6-7 are strings |
| | 28 | | #define PERLNUM_VECTOR 6 |
27 | | #define PERLNUM_V_VECTOR 5 | | 29 | | #define PERLNUM_V_VECTOR 7 |
| | 30 | | #define PERLNUM_BAD 8 |
| | 31 | | |
| | 32 | | #define BACK_NONE 0 // lookback state for bareword disambiguation: |
| | 33 | | #define BACK_OPERATOR 1 // whitespace/comments are insignificant |
| | 34 | | #define BACK_KEYWORD 2 // operators/keywords are needed for disambiguation |
|
29 | | 36 | | #define HERE_DELIM_MAX 256 |
|
|
13 skipped lines |
44 | | 51 | | ch == '(' || ch == ')' || ch == '-' || ch == '+' || |
|
45 | | 52 | | ch == '=' || ch == '|' || ch == '{' || ch == '}' || |
|
46 | | 53 | | ch == '[' || ch == ']' || ch == ':' || ch == ';' || |
|
47 | | ch == '>' || ch == ',' || | | 54 | | ch == '>' || ch == ',' || |
48 | | 55 | | ch == '?' || ch == '!' || ch == '.' || ch == '~') |
|
|
50 | | 57 | | // these chars are already tested before this call |
|
1 skipped line |
|
|
|
55 | | static int classifyWordPerl(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) { | | 62 | | static bool isPerlKeyword(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) { |
|
57 | | for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { | | 64 | | unsigned int i, len = end - start; |
| | 65 | | if (len > 30) { len = 30; } |
58 | | s[i] = styler[start + i]; | | 66 | | for (i = 0; i < len; i++, start++) s[i] = styler[start]; |
59 | | s[i + 1] = '\0'; | | 67 | | s[i] = '\0'; |
60 | | } | | |
61 | | char chAttr = SCE_PL_IDENTIFIER; | | |
62 | | if (keywords.InList(s)) | | 68 | | return keywords.InList(s); |
63 | | chAttr = SCE_PL_WORD; | | |
64 | | styler.ColourTo(end, chAttr); | | |
65 | | return chAttr; | | |
|
|
68 | | 71 | | static inline bool isEndVar(char ch) { |
|
7 skipped lines |
|
|
78 | | 81 | | static inline char actualNumStyle(int numberStyle) { |
|
79 | | switch (numberStyle) { | | |
80 | | case PERLNUM_VECTOR: | | |
81 | | case PERLNUM_V_VECTOR: | | 82 | | if (numberStyle == PERLNUM_VECTOR || numberStyle == PERLNUM_V_VECTOR) { |
82 | | return SCE_PL_STRING; | | 83 | | return SCE_PL_STRING; |
83 | | case PERLNUM_DECIMAL: | | |
84 | | case PERLNUM_NON_DEC: | | 84 | | } else if (numberStyle == PERLNUM_BAD) { |
85 | | case PERLNUM_FLOAT: | | 85 | | return SCE_PL_ERROR; |
86 | | default: | | 86 | | } |
87 | | return SCE_PL_NUMBER; | | 87 | | return SCE_PL_NUMBER; |
88 | | } | | |
|
|
91 | | 90 | | static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) { |
|
41 skipped lines |
133 | | 132 | | char *Delimiter;// the Delimiter, 256: sizeof PL_tokenbuf |
|
|
|
| | 135 | | Quote = 0; |
| | 136 | | Quoted = false; |
136 | | 137 | | DelimiterLength = 0; |
|
137 | | 138 | | Delimiter = new char[HERE_DELIM_MAX]; |
|
138 | | 139 | | Delimiter[0] = '\0'; |
|
57 skipped lines |
196 | | 197 | | || state == SCE_PL_CHARACTER |
|
197 | | 198 | | || state == SCE_PL_NUMBER |
|
198 | | 199 | | || state == SCE_PL_IDENTIFIER |
|
| | 200 | | || state == SCE_PL_ERROR |
|
200 | | 202 | | while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) { |
|
|
1 skipped line |
203 | | 205 | | state = SCE_PL_DEFAULT; |
|
|
|
| | 208 | | // lookback at start of lexing to set proper state for backflag |
| | 209 | | // after this, they are updated when elements are lexed |
| | 210 | | int backflag = BACK_NONE; |
| | 211 | | unsigned int backPos = startPos; |
| | 212 | | if (backPos > 0) { |
6 skipped lines |
| | 219 | | backflag = BACK_OPERATOR; |
| | 220 | | else if (sty == SCE_PL_WORD) |
| | 221 | | backflag = BACK_KEYWORD; |
| | 222 | | } |
| | 223 | | |
206 | | 224 | | styler.StartAt(startPos); |
|
207 | | 225 | | char chPrev = styler.SafeGetCharAt(startPos - 1); |
|
208 | | 226 | | if (startPos == 0) |
|
60 skipped lines |
269 | | 287 | | if (isdigit(ch) || (isdigit(chNext) && |
|
270 | | 288 | | (ch == '.' || ch == 'v'))) { |
|
271 | | 289 | | state = SCE_PL_NUMBER; |
|
| | 290 | | backflag = BACK_NONE; |
272 | | 291 | | numState = PERLNUM_DECIMAL; |
|
|
274 | | 293 | | if (ch == '0') {// hex,bin,octal |
|
| | 294 | | if (chNext == 'x') { |
| | 295 | | numState = PERLNUM_HEX; |
| | 296 | | } else if (chNext == 'b') { |
| | 297 | | numState = PERLNUM_BINARY; |
275 | | if (chNext == 'x' || chNext == 'b' || isdigit(chNext)) { | | 298 | | } else if (isdigit(chNext)) { |
276 | | numState = PERLNUM_NON_DEC; | | 299 | | numState = PERLNUM_OCTAL; |
| | 300 | | } |
| | 301 | | if (numState != PERLNUM_DECIMAL) { |
| | 302 | | i++; |
| | 303 | | ch = chNext; |
| | 304 | | chNext = chNext2; |
277 | | } | | 305 | | } |
278 | | 306 | | } else if (ch == 'v') { // vector |
|
279 | | 307 | | numState = PERLNUM_V_VECTOR; |
|
|
281 | | 309 | | } else if (iswordstart(ch)) { |
|
| | 310 | | // if immediately prefixed by '::', always a bareword |
| | 311 | | state = SCE_PL_WORD; |
282 | | if (chPrev == '>' && styler.SafeGetCharAt(i - 2) == '-') { | | 312 | | if (chPrev == ':' && styler.SafeGetCharAt(i - 2) == ':') { |
283 | | state = SCE_PL_IDENTIFIER; // part of "->" expr | | 313 | | state = SCE_PL_IDENTIFIER; |
284 | | if ((!iswordchar(chNext) && chNext != '\'') | | 314 | | } |
285 | | || (chNext == '.' && chNext2 == '.')) { | | 315 | | unsigned int kw = i + 1; |
286 | | // We need that if length of word == 1! | | 316 | | // first check for possible quote-like delimiter |
287 | | styler.ColourTo(i, SCE_PL_IDENTIFIER); | | |
288 | | state = SCE_PL_DEFAULT; | | |
289 | | } | | |
290 | | } else if (ch == 's' && !isNonQuote(chNext)) { | | 317 | | if (ch == 's' && !isNonQuote(chNext)) { |
291 | | 318 | | state = SCE_PL_REGSUBST; |
|
|
293 | | 320 | | } else if (ch == 'm' && !isNonQuote(chNext)) { |
|
8 skipped lines |
302 | | 329 | | } else if (ch == 't' && chNext == 'r' && !isNonQuote(chNext2)) { |
|
303 | | 330 | | state = SCE_PL_REGSUBST; |
|
|
305 | | i++; | | 332 | | kw++; |
306 | | chNext = chNext2; | | |
307 | | 333 | | } else if (ch == 'q' && (chNext == 'q' || chNext == 'r' || chNext == 'w' || chNext == 'x') && !isNonQuote(chNext2)) { |
|
308 | | 334 | | if (chNext == 'q') state = SCE_PL_STRING_QQ; |
|
309 | | 335 | | else if (chNext == 'x') state = SCE_PL_STRING_QX; |
|
310 | | 336 | | else if (chNext == 'r') state = SCE_PL_STRING_QR; |
|
311 | | 337 | | else if (chNext == 'w') state = SCE_PL_STRING_QW; |
|
312 | | i++; | | |
313 | | chNext = chNext2; | | |
|
| | 339 | | kw++; |
315 | | 340 | | } else if (ch == 'x' && (chNext == '=' || // repetition |
|
316 | | (chNext != '_' && !isalnum(chNext)) || | | 341 | | (chNext != '_' && !isalnum(chNext)) || |
317 | | (isdigit(chPrev) && isdigit(chNext)))) { | | 342 | | (isdigit(chPrev) && isdigit(chNext)))) { |
318 | | styler.ColourTo(i, SCE_PL_OPERATOR); | | 343 | | state = SCE_PL_OPERATOR; |
319 | | } else { | | 344 | | } |
320 | | state = SCE_PL_WORD; | | 345 | | // if potentially a keyword, scan forward and grab word, then check |
321 | | if ((!iswordchar(chNext) && chNext != '\'') | | 346 | | // if it's really one; if yes, disambiguation test is performed |
322 | | || (chNext == '.' && chNext2 == '.')) { | | 347 | | // otherwise it is always a bareword and we skip a lot of scanning |
323 | | // We need that if length of word == 1! | | 348 | | // note: keywords assumed to be limited to [_a-zA-Z] only |
324 | | // This test is copied from the SCE_PL_WORD handler. | | 349 | | if (state == SCE_PL_WORD) { |
| | 350 | | while (iswordstart(styler.SafeGetCharAt(kw))) kw++; |
325 | | classifyWordPerl(styler.GetStartSegment(), i, keywords, styler); | | 351 | | if (!isPerlKeyword(styler.GetStartSegment(), kw, keywords, styler)) { |
326 | | state = SCE_PL_DEFAULT; | | 352 | | state = SCE_PL_IDENTIFIER; |
327 | | } | | 353 | | } |
| | 354 | | } |
| | 355 | | // if already SCE_PL_IDENTIFIER, then no ambiguity, skip this |
| | 356 | | // for quote-like delimiters/keywords, attempt to disambiguate |
| | 357 | | // to select for bareword, change state -> SCE_PL_IDENTIFIER |
| | 358 | | if (state != SCE_PL_IDENTIFIER && i > 0) { |
| | 359 | | unsigned int j = i; |
| | 360 | | bool moreback = false; // true if passed newline/comments |
| | 361 | | bool brace = false; // true if opening brace found |
| | 362 | | char ch2; |
| | 363 | | // first look backwards past whitespace/comments for EOLs |
63 skipped lines |
| | 427 | | ch = styler.SafeGetCharAt(i); |
| | 428 | | chNext = styler.SafeGetCharAt(i + 1); |
| | 429 | | // a repetition operator 'x' |
| | 430 | | } else if (state == SCE_PL_OPERATOR) { |
| | 431 | | styler.ColourTo(i, SCE_PL_OPERATOR); |
| | 432 | | state = SCE_PL_DEFAULT; |
| | 433 | | // quote-like delimiter, skip one char if double-char delimiter |
| | 434 | | } else { |
| | 435 | | i = kw - 1; |
| | 436 | | chNext = styler.SafeGetCharAt(i + 1); |
328 | | } | | 437 | | } |
329 | | 438 | | } else if (ch == '#') { |
|
330 | | 439 | | state = SCE_PL_COMMENTLINE; |
|
331 | | 440 | | } else if (ch == '\"') { |
|
332 | | 441 | | state = SCE_PL_STRING; |
|
|
|
| | 444 | | backflag = BACK_NONE; |
335 | | 445 | | } else if (ch == '\'') { |
|
336 | | 446 | | if (chPrev == '&') { |
|
|
3 skipped lines |
|
|
|
| | 454 | | backflag = BACK_NONE; |
344 | | 455 | | } else if (ch == '`') { |
|
345 | | 456 | | state = SCE_PL_BACKTICKS; |
|
|
|
| | 459 | | backflag = BACK_NONE; |
348 | | 460 | | } else if (ch == '$') { |
|
349 | | 461 | | if ((chNext == '{') || isspacechar(chNext)) { |
|
350 | | 462 | | styler.ColourTo(i, SCE_PL_SCALAR); |
|
9 skipped lines |
360 | | 472 | | chNext = chNext2; |
|
|
|
| | 475 | | backflag = BACK_NONE; |
363 | | 476 | | } else if (ch == '@') { |
|
364 | | 477 | | if (isalpha(chNext) || chNext == '#' || chNext == '$' |
|
365 | | || chNext == '_' || chNext == '+') { | | 478 | | || chNext == '_' || chNext == '+' || chNext == '-') { |
366 | | 479 | | state = SCE_PL_ARRAY; |
|
367 | | 480 | | } else if (chNext != '{' && chNext != '[') { |
|
368 | | 481 | | styler.ColourTo(i, SCE_PL_ARRAY); |
|
369 | | i++; | | |
370 | | ch = ' '; | | |
|
372 | | 483 | | styler.ColourTo(i, SCE_PL_ARRAY); |
|
|
| | 485 | | backflag = BACK_NONE; |
374 | | 486 | | } else if (ch == '%') { |
|
375 | | if (isalpha(chNext) || chNext == '#' || chNext == '$' || chNext == '_') { | | 487 | | if (isalpha(chNext) || chNext == '#' || chNext == '$' |
| | 488 | | || chNext == '_' || chNext == '!' || chNext == '^') { |
376 | | 489 | | state = SCE_PL_HASH; |
|
| | 490 | | i++; |
| | 491 | | ch = chNext; |
| | 492 | | chNext = chNext2; |
377 | | 493 | | } else if (chNext == '{') { |
|
378 | | 494 | | styler.ColourTo(i, SCE_PL_HASH); |
|
|
380 | | 496 | | styler.ColourTo(i, SCE_PL_OPERATOR); |
|
|
| | 498 | | backflag = BACK_NONE; |
382 | | 499 | | } else if (ch == '*') { |
|
| | 500 | | char strch[2]; |
| | 501 | | strch[0] = chNext; |
| | 502 | | strch[1] = '\0'; |
383 | | if (isalpha(chNext) || chNext == '_' || chNext == '{') { | | 503 | | if (isalpha(chNext) || chNext == '_' || |
| | 504 | | NULL != strstr("^/|,\\\";#%^:?<>)[]", strch)) { |
384 | | 505 | | state = SCE_PL_SYMBOLTABLE; |
|
| | 506 | | i++; |
| | 507 | | ch = chNext; |
| | 508 | | chNext = chNext2; |
| | 509 | | } else if (chNext == '{') { |
| | 510 | | styler.ColourTo(i, SCE_PL_SYMBOLTABLE); |
|
386 | | 512 | | if (chNext == '*') {// exponentiation |
|
|
2 skipped lines |
|
391 | | 517 | | styler.ColourTo(i, SCE_PL_OPERATOR); |
|
|
| | 519 | | backflag = BACK_NONE; |
393 | | } else if (ch == '/') { | | 520 | | } else if (ch == '/' || (ch == '<' && chNext == '<')) { |
394 | | 521 | | // Explicit backward peeking to set a consistent preferRE for |
|
395 | | 522 | | // any slash found, so no longer need to track preferRE state. |
|
396 | | 523 | | // Find first previous significant lexed element and interpret. |
|
| | 524 | | // Test for HERE doc start '<<' shares this code, helps to |
| | 525 | | // determine if it should be an operator. |
397 | | 526 | | bool preferRE = false; |
|
| | 527 | | bool isHereDoc = (ch == '<'); |
| | 528 | | bool hereDocSpace = false; // these are for corner case: |
| | 529 | | bool hereDocScalar = false; // SCALAR [whitespace] '<<' |
398 | | 530 | | unsigned int bk = (i > 0)? i - 1: 0; |
|
|
|
| | 533 | | if (styler.StyleAt(bk) == SCE_PL_DEFAULT) |
| | 534 | | hereDocSpace = true; |
401 | | 535 | | while ((bk > 0) && (styler.StyleAt(bk) == SCE_PL_DEFAULT || |
|
402 | | 536 | | styler.StyleAt(bk) == SCE_PL_COMMENTLINE)) { |
|
|
90 skipped lines |
|
|
|
| | 631 | | case SCE_PL_SCALAR: // for $var<< case |
| | 632 | | hereDocScalar = true; |
| | 633 | | break; |
497 | | 634 | | // other styles uses the default, preferRE=false |
|
498 | | 635 | | case SCE_PL_WORD: |
|
|
| | 637 | | case SCE_PL_POD_VERB: |
500 | | 638 | | case SCE_PL_HERE_Q: |
|
501 | | 639 | | case SCE_PL_HERE_QQ: |
|
502 | | 640 | | case SCE_PL_HERE_QX: |
|
1 skipped line |
|
|
|
| | 645 | | if (isHereDoc) { // handle HERE doc |
| | 646 | | // if SCALAR whitespace '<<', *always* a HERE doc |
507 | | if (preferRE) { | | 647 | | if (preferRE || (hereDocSpace && hereDocScalar)) { |
508 | | state = SCE_PL_REGEX; | | 648 | | state = SCE_PL_HERE_DELIM; |
509 | | Quote.New(1); | | 649 | | HereDoc.State = 0; |
510 | | Quote.Open(ch); | | |
511 | | } else { | | 650 | | } else { // << operator |
| | 651 | | i++; |
| | 652 | | ch = chNext; |
| | 653 | | chNext = chNext2; |
512 | | styler.ColourTo(i, SCE_PL_OPERATOR); | | 654 | | styler.ColourTo(i, SCE_PL_OPERATOR); |
513 | | } | | 655 | | } |
514 | | } else if (ch == '<' && chNext == '<') { | | 656 | | } else { // handle regexp |
| | 657 | | if (preferRE) { |
515 | | state = SCE_PL_HERE_DELIM; | | 658 | | state = SCE_PL_REGEX; |
| | 659 | | Quote.New(1); |
| | 660 | | Quote.Open(ch); |
| | 661 | | } else { // / operator |
| | 662 | | styler.ColourTo(i, SCE_PL_OPERATOR); |
| | 663 | | } |
| | 664 | | } |
516 | | HereDoc.State = 0; | | 665 | | backflag = BACK_NONE; |
517 | | 666 | | } else if (ch == '<') { |
|
518 | | 667 | | // looks forward for matching > on same line |
|
519 | | 668 | | unsigned int fw = i + 1; |
|
520 | | 669 | | while (fw < lengthDoc) { |
|
521 | | 670 | | char fwch = styler.SafeGetCharAt(fw); |
|
| | 671 | | if (fwch == ' ') { |
| | 672 | | if (styler.SafeGetCharAt(fw-1) != '\\' || |
| | 673 | | styler.SafeGetCharAt(fw-2) != '\\') |
| | 674 | | break; |
522 | | if (isEOLChar(fwch) || isspacechar(fwch)) | | 675 | | } else if (isEOLChar(fwch) || isspacechar(fwch)) { |
|
524 | | else if (fwch == '>') { | | 677 | | } else if (fwch == '>') { |
525 | | 678 | | if ((fw - i) == 2 &&// '<=>' case |
|
526 | | 679 | | styler.SafeGetCharAt(fw-1) == '=') { |
|
527 | | 680 | | styler.ColourTo(fw, SCE_PL_OPERATOR); |
|
7 skipped lines |
|
|
537 | | 690 | | styler.ColourTo(i, SCE_PL_OPERATOR); |
|
| | 691 | | backflag = BACK_NONE; |
538 | | 692 | | } else if (ch == '='// POD |
|
539 | | 693 | | && isalpha(chNext) |
|
540 | | 694 | | && (isEOLChar(chPrev))) { |
|
541 | | 695 | | state = SCE_PL_POD; |
|
| | 696 | | backflag = BACK_NONE; |
|
543 | | 698 | | //sooked[sookedpos] = '\0'; |
|
544 | | 699 | | } else if (ch == '-'// file test operators |
|
4 skipped lines |
|
|
551 | | 706 | | chNext = chNext2; |
|
| | 707 | | backflag = BACK_NONE; |
552 | | 708 | | } else if (isPerlOperator(ch)) { |
|
553 | | 709 | | if (ch == '.' && chNext == '.') { // .. and ... |
|
|
3 skipped lines |
558 | | 714 | | chNext = styler.SafeGetCharAt(i + 1); |
|
|
560 | | 716 | | styler.ColourTo(i, SCE_PL_OPERATOR); |
|
| | 717 | | backflag = BACK_OPERATOR; |
| | 718 | | backPos = i; |
|
562 | | 720 | | // keep colouring defaults to make restart easier |
|
563 | | 721 | | styler.ColourTo(i, SCE_PL_DEFAULT); |
|
3 skipped lines |
567 | | 725 | | if (chNext == '.') { |
|
568 | | 726 | | // double dot is always an operator |
|
|
570 | | } else if (numState == PERLNUM_NON_DEC || numState == PERLNUM_FLOAT) { | | 728 | | } else if (numState <= PERLNUM_FLOAT) { |
571 | | 729 | | // non-decimal number or float exponent, consume next dot |
|
572 | | 730 | | styler.ColourTo(i - 1, SCE_PL_NUMBER); |
|
573 | | 731 | | styler.ColourTo(i, SCE_PL_OPERATOR); |
|
20 skipped lines |
594 | | 752 | | if (numState == PERLNUM_VECTOR || numState == PERLNUM_V_VECTOR) { |
|
595 | | 753 | | if (isalpha(ch)) { |
|
596 | | 754 | | if (dotCount == 0) { // change to word |
|
597 | | state = SCE_PL_WORD; | | 755 | | state = SCE_PL_IDENTIFIER; |
598 | | 756 | | } else { // vector then word |
|
|
|
13 skipped lines |
614 | | 772 | | if (!isdigit(ch)) { // float then word |
|
|
|
| | 775 | | } else if (numState == PERLNUM_OCTAL) { |
| | 776 | | if (!isdigit(ch)) |
| | 777 | | goto numAtEnd; |
| | 778 | | else if (ch > '7') |
| | 779 | | numState = PERLNUM_BAD; |
617 | | } else {// (numState == PERLNUM_NON_DEC) | | 780 | | } else if (numState == PERLNUM_BINARY) { |
618 | | // allow alphanum for bin,hex,oct for now | | 781 | | if (!isdigit(ch)) |
619 | | } | | 782 | | goto numAtEnd; |
| | 783 | | else if (ch > '1') |
| | 784 | | numState = PERLNUM_BAD; |
| | 785 | | } else if (numState == PERLNUM_HEX) { |
| | 786 | | int ch2 = toupper(ch); |
| | 787 | | if (!isdigit(ch) && !(ch2 >= 'A' && ch2 <= 'F')) |
| | 788 | | goto numAtEnd; |
| | 789 | | } else {//(numState == PERLNUM_BAD) { |
| | 790 | | if (!isdigit(ch)) |
| | 791 | | goto numAtEnd; |
| | 792 | | } |
|
621 | | 794 | | // complete current number or vector |
|
|
1 skipped line |
624 | | 797 | | state = SCE_PL_DEFAULT; |
|
625 | | 798 | | goto restartLexer; |
|
|
627 | | } else if (state == SCE_PL_WORD) { | | |
628 | | if ((!iswordchar(chNext) && chNext != '\'') | | |
629 | | || chNext == '.') { | | |
630 | | // ".." is always an operator if preceded by a SCE_PL_WORD. | | |
631 | | // "." never used in Perl variable names | | |
6 skipped lines |
638 | | classifyWordPerl(styler.GetStartSegment(), i, keywords, styler); | | |
639 | | state = SCE_PL_DEFAULT; | | |
640 | | ch = ' '; | | |
641 | | } | | |
642 | | } | | |
643 | | 800 | | } else if (state == SCE_PL_IDENTIFIER) { |
|
644 | | if ((!iswordchar(chNext) && chNext != '\'') | | 801 | | if (!iswordstart(chNext) && chNext != '\'') { |
645 | | || chNext == '.') { | | |
646 | | 802 | | styler.ColourTo(i, SCE_PL_IDENTIFIER); |
|
647 | | 803 | | state = SCE_PL_DEFAULT; |
|
|
36 skipped lines |
685 | | 841 | | // Whitespace acceptable after <<[-] operator. |
|
|
687 | | 843 | | if (HereDoc.State == 0) { // '<<' encountered |
|
| | 844 | | bool gotspace = false; |
| | 845 | | unsigned int oldi = i; |
| | 846 | | if (chNext == ' ' || chNext == '\t') { |
| | 847 | | // skip whitespace; legal for quoted delimiters |
| | 848 | | gotspace = true; |
| | 849 | | do { |
| | 850 | | i++; |
| | 851 | | chNext = styler.SafeGetCharAt(i + 1); |
| | 852 | | } while ((i + 1 < lengthDoc) && (chNext == ' ' || chNext == '\t')); |
| | 853 | | chNext2 = styler.SafeGetCharAt(i + 2); |
| | 854 | | } |
688 | | 855 | | HereDoc.State = 1; |
|
689 | | 856 | | HereDoc.Quote = chNext; |
|
690 | | 857 | | HereDoc.Quoted = false; |
|
691 | | 858 | | HereDoc.DelimiterLength = 0; |
|
692 | | 859 | | HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0'; |
|
693 | | if (chNext == '\'' || chNext == '"' || chNext == '`') { // a quoted here-doc delimiter | | 860 | | if (chNext == '\'' || chNext == '"' || chNext == '`') { |
| | 861 | | // a quoted here-doc delimiter |
|
|
696 | | 864 | | chNext = chNext2; |
|
697 | | 865 | | HereDoc.Quoted = true; |
|
698 | | } else if (isalpha(chNext) || chNext == '_') { | | |
699 | | // an unquoted here-doc delimiter, no special handling | | |
700 | | 866 | | } else if (isspacechar(chNext) || isdigit(chNext) || chNext == '\\' |
|
701 | | || chNext == '=' || chNext == '$' || chNext == '@') { | | 867 | | || chNext == '=' || chNext == '$' || chNext == '@' |
| | 868 | | || ((isalpha(chNext) || chNext == '_') && gotspace)) { |
702 | | 869 | | // left shift << or <<= operator cases |
|
| | 870 | | // restore position if operator |
| | 871 | | i = oldi; |
703 | | 872 | | styler.ColourTo(i, SCE_PL_OPERATOR); |
|
704 | | 873 | | state = SCE_PL_DEFAULT; |
|
705 | | 874 | | HereDoc.State = 0; |
|
| | 875 | | goto restartLexer; |
|
| | 877 | | // an unquoted here-doc delimiter, no special handling |
| | 878 | | // (cannot be prefixed by spaces/tabs), or |
707 | | 879 | | // symbols terminates; deprecated zero-length delimiter |
|
|
|
710 | | 882 | | } else if (HereDoc.State == 1) { // collect the delimiter |
|
| | 883 | | backflag = BACK_NONE; |
711 | | 884 | | if (HereDoc.Quoted) { // a quoted here-doc delimiter |
|
712 | | 885 | | if (ch == HereDoc.Quote) { // closing quote => end of delimiter |
|
713 | | 886 | | styler.ColourTo(i, state); |
|
32 skipped lines |
746 | | 919 | | if (isEOLChar(ch)) { |
|
747 | | 920 | | styler.ColourTo(i - 1, state); |
|
748 | | 921 | | state = SCE_PL_DEFAULT; |
|
| | 922 | | backflag = BACK_NONE; |
749 | | 923 | | HereDoc.State = 0; |
|
750 | | 924 | | goto restartLexer; |
|
|
752 | | 926 | | chNext = styler.SafeGetCharAt(i + 1); |
|
|
754 | | } else if (state == SCE_PL_POD) { | | 928 | | } else if (state == SCE_PL_POD |
| | 929 | | || state == SCE_PL_POD_VERB) { |
| | 930 | | if (isEOLChar(chPrev)) { |
755 | | if (ch == '=' && isEOLChar(chPrev)) { | | 931 | | if (ch == ' ' || ch == '\t') { |
| | 932 | | styler.ColourTo(i - 1, state); |
| | 933 | | state = SCE_PL_POD_VERB; |
| | 934 | | } else { |
| | 935 | | styler.ColourTo(i - 1, state); |
| | 936 | | state = SCE_PL_POD; |
| | 937 | | if (ch == '=') { |
756 | | if (isMatch(styler, lengthDoc, i, "=cut")) { | | 938 | | if (isMatch(styler, lengthDoc, i, "=cut")) { |
757 | | styler.ColourTo(i - 1 + 4, state); | | 939 | | styler.ColourTo(i - 1 + 4, state); |
758 | | i += 4; | | 940 | | i += 4; |
759 | | state = SCE_PL_DEFAULT; | | 941 | | state = SCE_PL_DEFAULT; |
760 | | ch = styler.SafeGetCharAt(i); | | 942 | | ch = styler.SafeGetCharAt(i); |
761 | | //chNext = styler.SafeGetCharAt(i + 1); | | 943 | | //chNext = styler.SafeGetCharAt(i + 1); |
762 | | goto restartLexer; | | 944 | | goto restartLexer; |
| | 945 | | } |
| | 946 | | } |
|
|
765 | | 949 | | } else if (state == SCE_PL_SCALAR // variable names |
|
6 skipped lines |
772 | | 956 | | chNext = chNext2; |
|
|
774 | | 958 | | else if (isEndVar(ch)) { |
|
775 | | if ((state == SCE_PL_SCALAR || state == SCE_PL_ARRAY) | | |
776 | | && i == (styler.GetStartSegment() + 1)) { | | 959 | | if (i == (styler.GetStartSegment() + 1)) { |
777 | | 960 | | // Special variable: $(, $_ etc. |
|
778 | | 961 | | styler.ColourTo(i, state); |
|
779 | | 962 | | state = SCE_PL_DEFAULT; |
|
137 skipped lines |
917 | | 1100 | | styler.ColourTo(lengthDoc - 1, state); |
|
|
|
| | 1103 | | static bool IsCommentLine(int line, Accessor &styler) { |
| | 1104 | | int pos = styler.LineStart(line); |
| | 1105 | | int eol_pos = styler.LineStart(line + 1) - 1; |
| | 1106 | | for (int i = pos; i < eol_pos; i++) { |
| | 1107 | | char ch = styler[i]; |
4 skipped lines |
| | 1112 | | return false; |
| | 1113 | | } |
| | 1114 | | return false; |
| | 1115 | | } |
| | 1116 | | |
920 | | 1117 | | static void FoldPerlDoc(unsigned int startPos, int length, int, WordList *[], |
|
921 | | 1118 | | Accessor &styler) { |
|
922 | | 1119 | | bool foldComment = styler.GetPropertyInt("fold.comment") != 0; |
|
923 | | 1120 | | bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; |
|
| | 1121 | | // Custom folding of POD and packages |
| | 1122 | | bool foldPOD = styler.GetPropertyInt("fold.perl.pod", 1) != 0; |
| | 1123 | | bool foldPackage = styler.GetPropertyInt("fold.perl.package", 1) != 0; |
924 | | 1124 | | unsigned int endPos = startPos + length; |
|
925 | | 1125 | | int visibleChars = 0; |
|
926 | | 1126 | | int lineCurrent = styler.GetLine(startPos); |
|
| | 1127 | | int levelPrev = SC_FOLDLEVELBASE; |
| | 1128 | | if (lineCurrent > 0) |
927 | | int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; | | 1129 | | levelPrev = styler.LevelAt(lineCurrent - 1) >> 16; |
928 | | 1130 | | int levelCurrent = levelPrev; |
|
929 | | 1131 | | char chNext = styler[startPos]; |
|
| | 1132 | | char chPrev = styler.SafeGetCharAt(startPos - 1); |
930 | | 1133 | | int styleNext = styler.StyleAt(startPos); |
|
| | 1134 | | // Used at end of line to determine if the line was a package definition |
| | 1135 | | bool isPackageLine = false; |
| | 1136 | | bool isPodHeading = false; |
931 | | 1137 | | for (unsigned int i = startPos; i < endPos; i++) { |
|
932 | | 1138 | | char ch = chNext; |
|
933 | | 1139 | | chNext = styler.SafeGetCharAt(i + 1); |
|
934 | | 1140 | | int style = styleNext; |
|
935 | | 1141 | | styleNext = styler.StyleAt(i + 1); |
|
936 | | 1142 | | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
|
937 | | if (foldComment && (style == SCE_PL_COMMENTLINE)) { | | 1143 | | bool atLineStart = isEOLChar(chPrev) || i == 0; |
| | 1144 | | // Comment folding |
938 | | if ((ch == '/') && (chNext == '/')) { | | 1145 | | if (foldComment && atEOL && IsCommentLine(lineCurrent, styler)) |
939 | | char chNext2 = styler.SafeGetCharAt(i + 2); | | 1146 | | { |
940 | | if (chNext2 == '{') { | | 1147 | | if (!IsCommentLine(lineCurrent - 1, styler) |
| | 1148 | | && IsCommentLine(lineCurrent + 1, styler)) |
941 | | levelCurrent++; | | 1149 | | levelCurrent++; |
942 | | } else if (chNext2 == '}') { | | 1150 | | else if (IsCommentLine(lineCurrent - 1, styler) |
| | 1151 | | && !IsCommentLine(lineCurrent+1, styler)) |
943 | | levelCurrent--; | | 1152 | | levelCurrent--; |
944 | | } | | |
945 | | } | | |
946 | | } | | 1153 | | } |
947 | | 1154 | | if (style == SCE_C_OPERATOR) { |
|
948 | | 1155 | | if (ch == '{') { |
|
|
1 skipped line |
|
|
|
| | 1161 | | // Custom POD folding |
| | 1162 | | if (foldPOD && atLineStart) { |
| | 1163 | | int stylePrevCh = (i) ? styler.StyleAt(i - 1):SCE_PL_DEFAULT; |
| | 1164 | | if (style == SCE_PL_POD) { |
| | 1165 | | if (stylePrevCh != SCE_PL_POD && stylePrevCh != SCE_PL_POD_VERB) |
20 skipped lines |
| | 1186 | | if (style == SCE_PL_WORD && styler.Match(i, "package")) { |
| | 1187 | | isPackageLine = true; |
| | 1188 | | } |
| | 1189 | | } |
| | 1190 | | |
|
955 | | 1192 | | int lev = levelPrev; |
|
| | 1193 | | if (isPodHeading) { |
| | 1194 | | lev = levelPrev - 1; |
| | 1195 | | lev |= SC_FOLDLEVELHEADERFLAG; |
| | 1196 | | isPodHeading = false; |
| | 1197 | | } |
3 skipped lines |
| | 1201 | | lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG; |
| | 1202 | | levelCurrent = SC_FOLDLEVELBASE + 1; |
| | 1203 | | isPackageLine = false; |
| | 1204 | | } |
| | 1205 | | lev |= levelCurrent << 16; |
956 | | 1206 | | if (visibleChars == 0 && foldCompact) |
|
957 | | 1207 | | lev |= SC_FOLDLEVELWHITEFLAG; |
|
958 | | 1208 | | if ((levelCurrent > levelPrev) && (visibleChars > 0)) |
|
7 skipped lines |
|
967 | | 1217 | | if (!isspacechar(ch)) |
|
|
| | 1219 | | chPrev = ch; |
|
970 | | 1221 | | // Fill in the real level of the next line, keeping the current flags as they will be filled in later |
|
971 | | 1222 | | int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; |
|
11 skipped lines |