PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/scintilla/LexPerl.cxx

http://haxedevelop.googlecode.com/
C++ | 1256 lines | 1152 code | 36 blank | 68 comment | 394 complexity | de5524c02db84f38a86cab2a2bcfa7c9 MD5 | raw file
Possible License(s): GPL-2.0
  1. // Scintilla source code edit control
  2. /** @file LexPerl.cxx
  3. ** Lexer for subset of Perl.
  4. **/
  5. // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
  6. // Lexical analysis fixes by Kein-Hong Man <mkh@pl.jaring.my>
  7. // The License.txt file describes the conditions under which this software may be distributed.
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include <stdio.h>
  12. #include <stdarg.h>
  13. #include "Platform.h"
  14. #include "PropSet.h"
  15. #include "Accessor.h"
  16. #include "KeyWords.h"
  17. #include "Scintilla.h"
  18. #include "SciLexer.h"
  19. #define PERLNUM_BINARY 1 // order is significant: 1-4 cannot have a dot
  20. #define PERLNUM_HEX 2
  21. #define PERLNUM_OCTAL 3
  22. #define PERLNUM_FLOAT 4 // actually exponent part
  23. #define PERLNUM_DECIMAL 5 // 1-5 are numbers; 6-7 are strings
  24. #define PERLNUM_VECTOR 6
  25. #define PERLNUM_V_VECTOR 7
  26. #define PERLNUM_BAD 8
  27. #define BACK_NONE 0 // lookback state for bareword disambiguation:
  28. #define BACK_OPERATOR 1 // whitespace/comments are insignificant
  29. #define BACK_KEYWORD 2 // operators/keywords are needed for disambiguation
  30. #define HERE_DELIM_MAX 256
  31. static inline bool isEOLChar(char ch) {
  32. return (ch == '\r') || (ch == '\n');
  33. }
  34. static bool isSingleCharOp(char ch) {
  35. char strCharSet[2];
  36. strCharSet[0] = ch;
  37. strCharSet[1] = '\0';
  38. return (NULL != strstr("rwxoRWXOezsfdlpSbctugkTBMAC", strCharSet));
  39. }
  40. static inline bool isPerlOperator(char ch) {
  41. if (ch == '^' || ch == '&' || ch == '\\' ||
  42. ch == '(' || ch == ')' || ch == '-' || ch == '+' ||
  43. ch == '=' || ch == '|' || ch == '{' || ch == '}' ||
  44. ch == '[' || ch == ']' || ch == ':' || ch == ';' ||
  45. ch == '>' || ch == ',' ||
  46. ch == '?' || ch == '!' || ch == '.' || ch == '~')
  47. return true;
  48. // these chars are already tested before this call
  49. // ch == '%' || ch == '*' || ch == '<' || ch == '/' ||
  50. return false;
  51. }
  52. static bool isPerlKeyword(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
  53. char s[100];
  54. unsigned int i, len = end - start;
  55. if (len > 30) { len = 30; }
  56. for (i = 0; i < len; i++, start++) s[i] = styler[start];
  57. s[i] = '\0';
  58. return keywords.InList(s);
  59. }
  60. // Note: as lexer uses chars, UTF-8 bytes are considered as <0 values
  61. // Note: iswordchar() was used in only one place in LexPerl, it is
  62. // unnecessary as '.' is processed as the concatenation operator, so
  63. // only isWordStart() is used in LexPerl
  64. static inline bool isWordStart(char ch) {
  65. return !isascii(ch) || isalnum(ch) || ch == '_';
  66. }
  67. static inline bool isEndVar(char ch) {
  68. return isascii(ch) && !isalnum(ch) && ch != '#' && ch != '$' &&
  69. ch != '_' && ch != '\'';
  70. }
  71. static inline bool isNonQuote(char ch) {
  72. return !isascii(ch) || isalnum(ch) || ch == '_';
  73. }
  74. static inline char actualNumStyle(int numberStyle) {
  75. if (numberStyle == PERLNUM_VECTOR || numberStyle == PERLNUM_V_VECTOR) {
  76. return SCE_PL_STRING;
  77. } else if (numberStyle == PERLNUM_BAD) {
  78. return SCE_PL_ERROR;
  79. }
  80. return SCE_PL_NUMBER;
  81. }
  82. static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) {
  83. if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) {
  84. return false;
  85. }
  86. while (*val) {
  87. if (*val != styler[pos++]) {
  88. return false;
  89. }
  90. val++;
  91. }
  92. return true;
  93. }
  94. static char opposite(char ch) {
  95. if (ch == '(')
  96. return ')';
  97. if (ch == '[')
  98. return ']';
  99. if (ch == '{')
  100. return '}';
  101. if (ch == '<')
  102. return '>';
  103. return ch;
  104. }
  105. static void ColourisePerlDoc(unsigned int startPos, int length, int initStyle,
  106. WordList *keywordlists[], Accessor &styler) {
  107. // Lexer for perl often has to backtrack to start of current style to determine
  108. // which characters are being used as quotes, how deeply nested is the
  109. // start position and what the termination string is for here documents
  110. WordList &keywords = *keywordlists[0];
  111. class HereDocCls {
  112. public:
  113. int State; // 0: '<<' encountered
  114. // 1: collect the delimiter
  115. // 2: here doc text (lines after the delimiter)
  116. char Quote; // the char after '<<'
  117. bool Quoted; // true if Quote in ('\'','"','`')
  118. int DelimiterLength; // strlen(Delimiter)
  119. char *Delimiter; // the Delimiter, 256: sizeof PL_tokenbuf
  120. HereDocCls() {
  121. State = 0;
  122. Quote = 0;
  123. Quoted = false;
  124. DelimiterLength = 0;
  125. Delimiter = new char[HERE_DELIM_MAX];
  126. Delimiter[0] = '\0';
  127. }
  128. ~HereDocCls() {
  129. delete []Delimiter;
  130. }
  131. };
  132. HereDocCls HereDoc; // TODO: FIFO for stacked here-docs
  133. class QuoteCls {
  134. public:
  135. int Rep;
  136. int Count;
  137. char Up;
  138. char Down;
  139. QuoteCls() {
  140. this->New(1);
  141. }
  142. void New(int r) {
  143. Rep = r;
  144. Count = 0;
  145. Up = '\0';
  146. Down = '\0';
  147. }
  148. void Open(char u) {
  149. Count++;
  150. Up = u;
  151. Down = opposite(Up);
  152. }
  153. };
  154. QuoteCls Quote;
  155. int state = initStyle;
  156. char numState = PERLNUM_DECIMAL;
  157. int dotCount = 0;
  158. unsigned int lengthDoc = startPos + length;
  159. //int sookedpos = 0; // these have no apparent use, see POD state
  160. //char sooked[100];
  161. //sooked[sookedpos] = '\0';
  162. // If in a long distance lexical state, seek to the beginning to find quote characters
  163. // Perl strings can be multi-line with embedded newlines, so backtrack.
  164. // Perl numbers have additional state during lexing, so backtrack too.
  165. if (state == SCE_PL_HERE_Q || state == SCE_PL_HERE_QQ || state == SCE_PL_HERE_QX) {
  166. while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_PL_HERE_DELIM)) {
  167. startPos--;
  168. }
  169. startPos = styler.LineStart(styler.GetLine(startPos));
  170. state = styler.StyleAt(startPos - 1);
  171. }
  172. if ( state == SCE_PL_STRING_Q
  173. || state == SCE_PL_STRING_QQ
  174. || state == SCE_PL_STRING_QX
  175. || state == SCE_PL_STRING_QR
  176. || state == SCE_PL_STRING_QW
  177. || state == SCE_PL_REGEX
  178. || state == SCE_PL_REGSUBST
  179. || state == SCE_PL_STRING
  180. || state == SCE_PL_BACKTICKS
  181. || state == SCE_PL_CHARACTER
  182. || state == SCE_PL_NUMBER
  183. || state == SCE_PL_IDENTIFIER
  184. || state == SCE_PL_ERROR
  185. ) {
  186. while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) {
  187. startPos--;
  188. }
  189. state = SCE_PL_DEFAULT;
  190. }
  191. // lookback at start of lexing to set proper state for backflag
  192. // after this, they are updated when elements are lexed
  193. int backflag = BACK_NONE;
  194. unsigned int backPos = startPos;
  195. if (backPos > 0) {
  196. backPos--;
  197. int sty = SCE_PL_DEFAULT;
  198. while ((backPos > 0) && (sty = styler.StyleAt(backPos),
  199. sty == SCE_PL_DEFAULT || sty == SCE_PL_COMMENTLINE))
  200. backPos--;
  201. if (sty == SCE_PL_OPERATOR)
  202. backflag = BACK_OPERATOR;
  203. else if (sty == SCE_PL_WORD)
  204. backflag = BACK_KEYWORD;
  205. }
  206. styler.StartAt(startPos);
  207. char chPrev = styler.SafeGetCharAt(startPos - 1);
  208. if (startPos == 0)
  209. chPrev = '\n';
  210. char chNext = styler[startPos];
  211. styler.StartSegment(startPos);
  212. for (unsigned int i = startPos; i < lengthDoc; i++) {
  213. char ch = chNext;
  214. // if the current character is not consumed due to the completion of an
  215. // earlier style, lexing can be restarted via a simple goto
  216. restartLexer:
  217. chNext = styler.SafeGetCharAt(i + 1);
  218. char chNext2 = styler.SafeGetCharAt(i + 2);
  219. if (styler.IsLeadByte(ch)) {
  220. chNext = styler.SafeGetCharAt(i + 2);
  221. chPrev = ' ';
  222. i += 1;
  223. continue;
  224. }
  225. if ((chPrev == '\r' && ch == '\n')) { // skip on DOS/Windows
  226. styler.ColourTo(i, state);
  227. chPrev = ch;
  228. continue;
  229. }
  230. if (HereDoc.State == 1 && isEOLChar(ch)) {
  231. // Begin of here-doc (the line after the here-doc delimiter):
  232. // Lexically, the here-doc starts from the next line after the >>, but the
  233. // first line of here-doc seem to follow the style of the last EOL sequence
  234. HereDoc.State = 2;
  235. if (HereDoc.Quoted) {
  236. if (state == SCE_PL_HERE_DELIM) {
  237. // Missing quote at end of string! We are stricter than perl.
  238. // Colour here-doc anyway while marking this bit as an error.
  239. state = SCE_PL_ERROR;
  240. }
  241. styler.ColourTo(i - 1, state);
  242. switch (HereDoc.Quote) {
  243. case '\'':
  244. state = SCE_PL_HERE_Q ;
  245. break;
  246. case '"':
  247. state = SCE_PL_HERE_QQ;
  248. break;
  249. case '`':
  250. state = SCE_PL_HERE_QX;
  251. break;
  252. }
  253. } else {
  254. styler.ColourTo(i - 1, state);
  255. switch (HereDoc.Quote) {
  256. case '\\':
  257. state = SCE_PL_HERE_Q ;
  258. break;
  259. default :
  260. state = SCE_PL_HERE_QQ;
  261. }
  262. }
  263. }
  264. if (state == SCE_PL_DEFAULT) {
  265. if ((isascii(ch) && isdigit(ch)) || (isascii(chNext) && isdigit(chNext) &&
  266. (ch == '.' || ch == 'v'))) {
  267. state = SCE_PL_NUMBER;
  268. backflag = BACK_NONE;
  269. numState = PERLNUM_DECIMAL;
  270. dotCount = 0;
  271. if (ch == '0') { // hex,bin,octal
  272. if (chNext == 'x') {
  273. numState = PERLNUM_HEX;
  274. } else if (chNext == 'b') {
  275. numState = PERLNUM_BINARY;
  276. } else if (isascii(chNext) && isdigit(chNext)) {
  277. numState = PERLNUM_OCTAL;
  278. }
  279. if (numState != PERLNUM_DECIMAL) {
  280. i++;
  281. ch = chNext;
  282. chNext = chNext2;
  283. }
  284. } else if (ch == 'v') { // vector
  285. numState = PERLNUM_V_VECTOR;
  286. }
  287. } else if (isWordStart(ch)) {
  288. // if immediately prefixed by '::', always a bareword
  289. state = SCE_PL_WORD;
  290. if (chPrev == ':' && styler.SafeGetCharAt(i - 2) == ':') {
  291. state = SCE_PL_IDENTIFIER;
  292. }
  293. unsigned int kw = i + 1;
  294. // first check for possible quote-like delimiter
  295. if (ch == 's' && !isNonQuote(chNext)) {
  296. state = SCE_PL_REGSUBST;
  297. Quote.New(2);
  298. } else if (ch == 'm' && !isNonQuote(chNext)) {
  299. state = SCE_PL_REGEX;
  300. Quote.New(1);
  301. } else if (ch == 'q' && !isNonQuote(chNext)) {
  302. state = SCE_PL_STRING_Q;
  303. Quote.New(1);
  304. } else if (ch == 'y' && !isNonQuote(chNext)) {
  305. state = SCE_PL_REGSUBST;
  306. Quote.New(2);
  307. } else if (ch == 't' && chNext == 'r' && !isNonQuote(chNext2)) {
  308. state = SCE_PL_REGSUBST;
  309. Quote.New(2);
  310. kw++;
  311. } else if (ch == 'q' && (chNext == 'q' || chNext == 'r' || chNext == 'w' || chNext == 'x') && !isNonQuote(chNext2)) {
  312. if (chNext == 'q') state = SCE_PL_STRING_QQ;
  313. else if (chNext == 'x') state = SCE_PL_STRING_QX;
  314. else if (chNext == 'r') state = SCE_PL_STRING_QR;
  315. else if (chNext == 'w') state = SCE_PL_STRING_QW;
  316. Quote.New(1);
  317. kw++;
  318. } else if (ch == 'x' && (chNext == '=' || // repetition
  319. !isWordStart(chNext) ||
  320. (isdigit(chPrev) && isdigit(chNext)))) {
  321. state = SCE_PL_OPERATOR;
  322. }
  323. // if potentially a keyword, scan forward and grab word, then check
  324. // if it's really one; if yes, disambiguation test is performed
  325. // otherwise it is always a bareword and we skip a lot of scanning
  326. // note: keywords assumed to be limited to [_a-zA-Z] only
  327. if (state == SCE_PL_WORD) {
  328. while (isWordStart(styler.SafeGetCharAt(kw))) kw++;
  329. if (!isPerlKeyword(styler.GetStartSegment(), kw, keywords, styler)) {
  330. state = SCE_PL_IDENTIFIER;
  331. }
  332. }
  333. // if already SCE_PL_IDENTIFIER, then no ambiguity, skip this
  334. // for quote-like delimiters/keywords, attempt to disambiguate
  335. // to select for bareword, change state -> SCE_PL_IDENTIFIER
  336. if (state != SCE_PL_IDENTIFIER && i > 0) {
  337. unsigned int j = i;
  338. bool moreback = false; // true if passed newline/comments
  339. bool brace = false; // true if opening brace found
  340. char ch2;
  341. // first look backwards past whitespace/comments for EOLs
  342. // if BACK_NONE, neither operator nor keyword, so skip test
  343. if (backflag != BACK_NONE) {
  344. while (--j > backPos) {
  345. if (isEOLChar(styler.SafeGetCharAt(j)))
  346. moreback = true;
  347. }
  348. ch2 = styler.SafeGetCharAt(j);
  349. if (ch2 == '{' && !moreback) {
  350. // {bareword: possible variable spec
  351. brace = true;
  352. } else if ((ch2 == '&' && styler.SafeGetCharAt(j - 1) != '&')
  353. // &bareword: subroutine call
  354. || (ch2 == '>' && styler.SafeGetCharAt(j - 1) == '-')
  355. // ->bareword: part of variable spec
  356. || (ch2 == 'b' && styler.Match(j - 2, "su"))) {
  357. // sub bareword: subroutine declaration
  358. // (implied BACK_KEYWORD, no keywords end in 'sub'!)
  359. state = SCE_PL_IDENTIFIER;
  360. }
  361. // if status still ambiguous, look forward after word past
  362. // tabs/spaces only; if ch2 isn't one of '[{(,' it can never
  363. // match anything, so skip the whole thing
  364. j = kw;
  365. if (state != SCE_PL_IDENTIFIER
  366. && (ch2 == '{' || ch2 == '(' || ch2 == '['|| ch2 == ',')
  367. && kw < lengthDoc) {
  368. while (ch2 = styler.SafeGetCharAt(j),
  369. (ch2 == ' ' || ch2 == '\t') && j < lengthDoc) {
  370. j++;
  371. }
  372. if ((ch2 == '}' && brace)
  373. // {bareword}: variable spec
  374. || (ch2 == '=' && styler.SafeGetCharAt(j + 1) == '>')) {
  375. // [{(, bareword=>: hash literal
  376. state = SCE_PL_IDENTIFIER;
  377. }
  378. }
  379. }
  380. }
  381. backflag = BACK_NONE;
  382. // an identifier or bareword
  383. if (state == SCE_PL_IDENTIFIER) {
  384. if ((!isWordStart(chNext) && chNext != '\'')
  385. || (chNext == '.' && chNext2 == '.')) {
  386. // We need that if length of word == 1!
  387. // This test is copied from the SCE_PL_WORD handler.
  388. styler.ColourTo(i, SCE_PL_IDENTIFIER);
  389. state = SCE_PL_DEFAULT;
  390. }
  391. // a keyword
  392. } else if (state == SCE_PL_WORD) {
  393. i = kw - 1;
  394. if (ch == '_' && chNext == '_' &&
  395. (isMatch(styler, lengthDoc, styler.GetStartSegment(), "__DATA__")
  396. || isMatch(styler, lengthDoc, styler.GetStartSegment(), "__END__"))) {
  397. styler.ColourTo(i, SCE_PL_DATASECTION);
  398. state = SCE_PL_DATASECTION;
  399. } else {
  400. styler.ColourTo(i, SCE_PL_WORD);
  401. state = SCE_PL_DEFAULT;
  402. backflag = BACK_KEYWORD;
  403. backPos = i;
  404. }
  405. ch = styler.SafeGetCharAt(i);
  406. chNext = styler.SafeGetCharAt(i + 1);
  407. // a repetition operator 'x'
  408. } else if (state == SCE_PL_OPERATOR) {
  409. styler.ColourTo(i, SCE_PL_OPERATOR);
  410. state = SCE_PL_DEFAULT;
  411. // quote-like delimiter, skip one char if double-char delimiter
  412. } else {
  413. i = kw - 1;
  414. chNext = styler.SafeGetCharAt(i + 1);
  415. }
  416. } else if (ch == '#') {
  417. state = SCE_PL_COMMENTLINE;
  418. } else if (ch == '\"') {
  419. state = SCE_PL_STRING;
  420. Quote.New(1);
  421. Quote.Open(ch);
  422. backflag = BACK_NONE;
  423. } else if (ch == '\'') {
  424. if (chPrev == '&') {
  425. // Archaic call
  426. styler.ColourTo(i, state);
  427. } else {
  428. state = SCE_PL_CHARACTER;
  429. Quote.New(1);
  430. Quote.Open(ch);
  431. }
  432. backflag = BACK_NONE;
  433. } else if (ch == '`') {
  434. state = SCE_PL_BACKTICKS;
  435. Quote.New(1);
  436. Quote.Open(ch);
  437. backflag = BACK_NONE;
  438. } else if (ch == '$') {
  439. if ((chNext == '{') || isspacechar(chNext)) {
  440. styler.ColourTo(i, SCE_PL_SCALAR);
  441. } else {
  442. state = SCE_PL_SCALAR;
  443. if ((chNext == '`' && chNext2 == '`')
  444. || (chNext == ':' && chNext2 == ':')) {
  445. i += 2;
  446. ch = styler.SafeGetCharAt(i);
  447. chNext = styler.SafeGetCharAt(i + 1);
  448. } else {
  449. i++;
  450. ch = chNext;
  451. chNext = chNext2;
  452. }
  453. }
  454. backflag = BACK_NONE;
  455. } else if (ch == '@') {
  456. if (!isascii(chNext) || isalpha(chNext) || chNext == '#' || chNext == '$'
  457. || chNext == '_' || chNext == '+' || chNext == '-') {
  458. state = SCE_PL_ARRAY;
  459. } else if (chNext == ':' && chNext2 == ':') {
  460. state = SCE_PL_ARRAY;
  461. i += 2;
  462. ch = styler.SafeGetCharAt(i);
  463. chNext = styler.SafeGetCharAt(i + 1);
  464. } else if (chNext != '{' && chNext != '[') {
  465. styler.ColourTo(i, SCE_PL_ARRAY);
  466. } else {
  467. styler.ColourTo(i, SCE_PL_ARRAY);
  468. }
  469. backflag = BACK_NONE;
  470. } else if (ch == '%') {
  471. if (!isascii(chNext) || isalpha(chNext) || chNext == '#' || chNext == '$'
  472. || chNext == '_' || chNext == '!' || chNext == '^') {
  473. state = SCE_PL_HASH;
  474. i++;
  475. ch = chNext;
  476. chNext = chNext2;
  477. } else if (chNext == ':' && chNext2 == ':') {
  478. state = SCE_PL_HASH;
  479. i += 2;
  480. ch = styler.SafeGetCharAt(i);
  481. chNext = styler.SafeGetCharAt(i + 1);
  482. } else if (chNext == '{') {
  483. styler.ColourTo(i, SCE_PL_HASH);
  484. } else {
  485. styler.ColourTo(i, SCE_PL_OPERATOR);
  486. }
  487. backflag = BACK_NONE;
  488. } else if (ch == '*') {
  489. char strch[2];
  490. strch[0] = chNext;
  491. strch[1] = '\0';
  492. if (chNext == ':' && chNext2 == ':') {
  493. state = SCE_PL_SYMBOLTABLE;
  494. i += 2;
  495. ch = styler.SafeGetCharAt(i);
  496. chNext = styler.SafeGetCharAt(i + 1);
  497. } else if (!isascii(chNext) || isalpha(chNext) || chNext == '_'
  498. || NULL != strstr("^/|,\\\";#%^:?<>)[]", strch)) {
  499. state = SCE_PL_SYMBOLTABLE;
  500. i++;
  501. ch = chNext;
  502. chNext = chNext2;
  503. } else if (chNext == '{') {
  504. styler.ColourTo(i, SCE_PL_SYMBOLTABLE);
  505. } else {
  506. if (chNext == '*') { // exponentiation
  507. i++;
  508. ch = chNext;
  509. chNext = chNext2;
  510. }
  511. styler.ColourTo(i, SCE_PL_OPERATOR);
  512. }
  513. backflag = BACK_NONE;
  514. } else if (ch == '/' || (ch == '<' && chNext == '<')) {
  515. // Explicit backward peeking to set a consistent preferRE for
  516. // any slash found, so no longer need to track preferRE state.
  517. // Find first previous significant lexed element and interpret.
  518. // Test for HERE doc start '<<' shares this code, helps to
  519. // determine if it should be an operator.
  520. bool preferRE = false;
  521. bool isHereDoc = (ch == '<');
  522. bool hereDocSpace = false; // these are for corner case:
  523. bool hereDocScalar = false; // SCALAR [whitespace] '<<'
  524. unsigned int bk = (i > 0)? i - 1: 0;
  525. char bkch;
  526. styler.Flush();
  527. if (styler.StyleAt(bk) == SCE_PL_DEFAULT)
  528. hereDocSpace = true;
  529. while ((bk > 0) && (styler.StyleAt(bk) == SCE_PL_DEFAULT ||
  530. styler.StyleAt(bk) == SCE_PL_COMMENTLINE)) {
  531. bk--;
  532. }
  533. if (bk == 0) {
  534. // position 0 won't really be checked; rarely happens
  535. // hard to fix due to an unsigned index i
  536. preferRE = true;
  537. } else {
  538. int bkstyle = styler.StyleAt(bk);
  539. bkch = styler.SafeGetCharAt(bk);
  540. switch(bkstyle) {
  541. case SCE_PL_OPERATOR:
  542. preferRE = true;
  543. if (bkch == ')' || bkch == ']') {
  544. preferRE = false;
  545. } else if (bkch == '}') {
  546. // backtrack further, count balanced brace pairs
  547. // if a brace pair found, see if it's a variable
  548. int braceCount = 1;
  549. while (--bk > 0) {
  550. bkstyle = styler.StyleAt(bk);
  551. if (bkstyle == SCE_PL_OPERATOR) {
  552. bkch = styler.SafeGetCharAt(bk);
  553. if (bkch == ';') { // early out
  554. break;
  555. } else if (bkch == '}') {
  556. braceCount++;
  557. } else if (bkch == '{') {
  558. if (--braceCount == 0)
  559. break;
  560. }
  561. }
  562. }
  563. if (bk == 0) {
  564. // at beginning, true
  565. } else if (braceCount == 0) {
  566. // balanced { found, bk>0, skip more whitespace
  567. if (styler.StyleAt(--bk) == SCE_PL_DEFAULT) {
  568. while (bk > 0) {
  569. bkstyle = styler.StyleAt(--bk);
  570. if (bkstyle != SCE_PL_DEFAULT)
  571. break;
  572. }
  573. }
  574. bkstyle = styler.StyleAt(bk);
  575. if (bkstyle == SCE_PL_SCALAR
  576. || bkstyle == SCE_PL_ARRAY
  577. || bkstyle == SCE_PL_HASH
  578. || bkstyle == SCE_PL_SYMBOLTABLE
  579. || bkstyle == SCE_PL_OPERATOR) {
  580. preferRE = false;
  581. }
  582. }
  583. }
  584. break;
  585. case SCE_PL_IDENTIFIER:
  586. preferRE = true;
  587. if (bkch == '>') { // inputsymbol
  588. preferRE = false;
  589. break;
  590. }
  591. // backtrack to find "->" or "::" before identifier
  592. while (bk > 0 && styler.StyleAt(bk) == SCE_PL_IDENTIFIER) {
  593. bk--;
  594. }
  595. while (bk > 0) {
  596. bkstyle = styler.StyleAt(bk);
  597. if (bkstyle == SCE_PL_DEFAULT ||
  598. bkstyle == SCE_PL_COMMENTLINE) {
  599. } else if (bkstyle == SCE_PL_OPERATOR) {
  600. // gcc 3.2.3 bloats if more compact form used
  601. bkch = styler.SafeGetCharAt(bk);
  602. if (bkch == '>') { // "->"
  603. if (styler.SafeGetCharAt(bk - 1) == '-') {
  604. preferRE = false;
  605. break;
  606. }
  607. } else if (bkch == ':') { // "::"
  608. if (styler.SafeGetCharAt(bk - 1) == ':') {
  609. preferRE = false;
  610. break;
  611. }
  612. }
  613. } else {// bare identifier, usually a function call but Perl
  614. // optimizes them as pseudo-constants, then the next
  615. // '/' will be a divide; favour divide over regex
  616. // if there is a whitespace after the '/'
  617. if (isspacechar(chNext)) {
  618. preferRE = false;
  619. }
  620. break;
  621. }
  622. bk--;
  623. }
  624. break;
  625. case SCE_PL_SCALAR: // for $var<< case
  626. hereDocScalar = true;
  627. break;
  628. // other styles uses the default, preferRE=false
  629. case SCE_PL_WORD:
  630. case SCE_PL_POD:
  631. case SCE_PL_POD_VERB:
  632. case SCE_PL_HERE_Q:
  633. case SCE_PL_HERE_QQ:
  634. case SCE_PL_HERE_QX:
  635. preferRE = true;
  636. break;
  637. }
  638. }
  639. if (isHereDoc) { // handle HERE doc
  640. // if SCALAR whitespace '<<', *always* a HERE doc
  641. if (preferRE || (hereDocSpace && hereDocScalar)) {
  642. state = SCE_PL_HERE_DELIM;
  643. HereDoc.State = 0;
  644. } else { // << operator
  645. i++;
  646. ch = chNext;
  647. chNext = chNext2;
  648. styler.ColourTo(i, SCE_PL_OPERATOR);
  649. }
  650. } else { // handle regexp
  651. if (preferRE) {
  652. state = SCE_PL_REGEX;
  653. Quote.New(1);
  654. Quote.Open(ch);
  655. } else { // / operator
  656. styler.ColourTo(i, SCE_PL_OPERATOR);
  657. }
  658. }
  659. backflag = BACK_NONE;
  660. } else if (ch == '<') {
  661. // looks forward for matching > on same line
  662. unsigned int fw = i + 1;
  663. while (fw < lengthDoc) {
  664. char fwch = styler.SafeGetCharAt(fw);
  665. if (fwch == ' ') {
  666. if (styler.SafeGetCharAt(fw-1) != '\\' ||
  667. styler.SafeGetCharAt(fw-2) != '\\')
  668. break;
  669. } else if (isEOLChar(fwch) || isspacechar(fwch)) {
  670. break;
  671. } else if (fwch == '>') {
  672. if ((fw - i) == 2 && // '<=>' case
  673. styler.SafeGetCharAt(fw-1) == '=') {
  674. styler.ColourTo(fw, SCE_PL_OPERATOR);
  675. } else {
  676. styler.ColourTo(fw, SCE_PL_IDENTIFIER);
  677. }
  678. i = fw;
  679. ch = fwch;
  680. chNext = styler.SafeGetCharAt(i+1);
  681. }
  682. fw++;
  683. }
  684. styler.ColourTo(i, SCE_PL_OPERATOR);
  685. backflag = BACK_NONE;
  686. } else if (ch == '=' // POD
  687. && isalpha(chNext)
  688. && (isEOLChar(chPrev))) {
  689. state = SCE_PL_POD;
  690. backflag = BACK_NONE;
  691. //sookedpos = 0;
  692. //sooked[sookedpos] = '\0';
  693. } else if (ch == '-' // file test operators
  694. && isSingleCharOp(chNext)
  695. && !isalnum((chNext2 = styler.SafeGetCharAt(i+2)))) {
  696. styler.ColourTo(i + 1, SCE_PL_WORD);
  697. state = SCE_PL_DEFAULT;
  698. i++;
  699. ch = chNext;
  700. chNext = chNext2;
  701. backflag = BACK_NONE;
  702. } else if (isPerlOperator(ch)) {
  703. if (ch == '.' && chNext == '.') { // .. and ...
  704. i++;
  705. if (chNext2 == '.') { i++; }
  706. state = SCE_PL_DEFAULT;
  707. ch = styler.SafeGetCharAt(i);
  708. chNext = styler.SafeGetCharAt(i + 1);
  709. }
  710. styler.ColourTo(i, SCE_PL_OPERATOR);
  711. backflag = BACK_OPERATOR;
  712. backPos = i;
  713. } else {
  714. // keep colouring defaults to make restart easier
  715. styler.ColourTo(i, SCE_PL_DEFAULT);
  716. }
  717. } else if (state == SCE_PL_NUMBER) {
  718. if (ch == '.') {
  719. if (chNext == '.') {
  720. // double dot is always an operator
  721. goto numAtEnd;
  722. } else if (numState <= PERLNUM_FLOAT) {
  723. // non-decimal number or float exponent, consume next dot
  724. styler.ColourTo(i - 1, SCE_PL_NUMBER);
  725. styler.ColourTo(i, SCE_PL_OPERATOR);
  726. state = SCE_PL_DEFAULT;
  727. } else { // decimal or vectors allows dots
  728. dotCount++;
  729. if (numState == PERLNUM_DECIMAL) {
  730. if (dotCount > 1) {
  731. if (isdigit(chNext)) { // really a vector
  732. numState = PERLNUM_VECTOR;
  733. } else // number then dot
  734. goto numAtEnd;
  735. }
  736. } else { // vectors
  737. if (!isdigit(chNext)) // vector then dot
  738. goto numAtEnd;
  739. }
  740. }
  741. } else if (ch == '_' && numState == PERLNUM_DECIMAL) {
  742. if (!isdigit(chNext)) {
  743. goto numAtEnd;
  744. }
  745. } else if (!isascii(ch) || isalnum(ch)) {
  746. if (numState == PERLNUM_VECTOR || numState == PERLNUM_V_VECTOR) {
  747. if (!isascii(ch) || isalpha(ch)) {
  748. if (dotCount == 0) { // change to word
  749. state = SCE_PL_IDENTIFIER;
  750. } else { // vector then word
  751. goto numAtEnd;
  752. }
  753. }
  754. } else if (numState == PERLNUM_DECIMAL) {
  755. if (ch == 'E' || ch == 'e') { // exponent
  756. numState = PERLNUM_FLOAT;
  757. if (chNext == '+' || chNext == '-') {
  758. i++;
  759. ch = chNext;
  760. chNext = chNext2;
  761. }
  762. } else if (!isascii(ch) || !isdigit(ch)) { // number then word
  763. goto numAtEnd;
  764. }
  765. } else if (numState == PERLNUM_FLOAT) {
  766. if (!isdigit(ch)) { // float then word
  767. goto numAtEnd;
  768. }
  769. } else if (numState == PERLNUM_OCTAL) {
  770. if (!isdigit(ch))
  771. goto numAtEnd;
  772. else if (ch > '7')
  773. numState = PERLNUM_BAD;
  774. } else if (numState == PERLNUM_BINARY) {
  775. if (!isdigit(ch))
  776. goto numAtEnd;
  777. else if (ch > '1')
  778. numState = PERLNUM_BAD;
  779. } else if (numState == PERLNUM_HEX) {
  780. int ch2 = toupper(ch);
  781. if (!isdigit(ch) && !(ch2 >= 'A' && ch2 <= 'F'))
  782. goto numAtEnd;
  783. } else {//(numState == PERLNUM_BAD) {
  784. if (!isdigit(ch))
  785. goto numAtEnd;
  786. }
  787. } else {
  788. // complete current number or vector
  789. numAtEnd:
  790. styler.ColourTo(i - 1, actualNumStyle(numState));
  791. state = SCE_PL_DEFAULT;
  792. goto restartLexer;
  793. }
  794. } else if (state == SCE_PL_IDENTIFIER) {
  795. if (!isWordStart(chNext) && chNext != '\'') {
  796. styler.ColourTo(i, SCE_PL_IDENTIFIER);
  797. state = SCE_PL_DEFAULT;
  798. ch = ' ';
  799. }
  800. } else {
  801. if (state == SCE_PL_COMMENTLINE) {
  802. if (isEOLChar(ch)) {
  803. styler.ColourTo(i - 1, state);
  804. state = SCE_PL_DEFAULT;
  805. goto restartLexer;
  806. } else if (isEOLChar(chNext)) {
  807. styler.ColourTo(i, state);
  808. state = SCE_PL_DEFAULT;
  809. }
  810. } else if (state == SCE_PL_HERE_DELIM) {
  811. //
  812. // From perldata.pod:
  813. // ------------------
  814. // A line-oriented form of quoting is based on the shell ``here-doc''
  815. // syntax.
  816. // Following a << you specify a string to terminate the quoted material,
  817. // and all lines following the current line down to the terminating
  818. // string are the value of the item.
  819. // The terminating string may be either an identifier (a word),
  820. // or some quoted text.
  821. // If quoted, the type of quotes you use determines the treatment of
  822. // the text, just as in regular quoting.
  823. // An unquoted identifier works like double quotes.
  824. // There must be no space between the << and the identifier.
  825. // (If you put a space it will be treated as a null identifier,
  826. // which is valid, and matches the first empty line.)
  827. // (This is deprecated, -w warns of this syntax)
  828. // The terminating string must appear by itself (unquoted and with no
  829. // surrounding whitespace) on the terminating line.
  830. //
  831. // From Bash info:
  832. // ---------------
  833. // Specifier format is: <<[-]WORD
  834. // Optional '-' is for removal of leading tabs from here-doc.
  835. // Whitespace acceptable after <<[-] operator.
  836. //
  837. if (HereDoc.State == 0) { // '<<' encountered
  838. bool gotspace = false;
  839. unsigned int oldi = i;
  840. if (chNext == ' ' || chNext == '\t') {
  841. // skip whitespace; legal for quoted delimiters
  842. gotspace = true;
  843. do {
  844. i++;
  845. chNext = styler.SafeGetCharAt(i + 1);
  846. } while ((i + 1 < lengthDoc) && (chNext == ' ' || chNext == '\t'));
  847. chNext2 = styler.SafeGetCharAt(i + 2);
  848. }
  849. HereDoc.State = 1;
  850. HereDoc.Quote = chNext;
  851. HereDoc.Quoted = false;
  852. HereDoc.DelimiterLength = 0;
  853. HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
  854. if (chNext == '\'' || chNext == '"' || chNext == '`') {
  855. // a quoted here-doc delimiter
  856. i++;
  857. ch = chNext;
  858. chNext = chNext2;
  859. HereDoc.Quoted = true;
  860. } else if (isspacechar(chNext) || isdigit(chNext) || chNext == '\\'
  861. || chNext == '=' || chNext == '$' || chNext == '@'
  862. || ((isalpha(chNext) || chNext == '_') && gotspace)) {
  863. // left shift << or <<= operator cases
  864. // restore position if operator
  865. i = oldi;
  866. styler.ColourTo(i, SCE_PL_OPERATOR);
  867. state = SCE_PL_DEFAULT;
  868. HereDoc.State = 0;
  869. goto restartLexer;
  870. } else {
  871. // an unquoted here-doc delimiter, no special handling
  872. // (cannot be prefixed by spaces/tabs), or
  873. // symbols terminates; deprecated zero-length delimiter
  874. }
  875. } else if (HereDoc.State == 1) { // collect the delimiter
  876. backflag = BACK_NONE;
  877. if (HereDoc.Quoted) { // a quoted here-doc delimiter
  878. if (ch == HereDoc.Quote) { // closing quote => end of delimiter
  879. styler.ColourTo(i, state);
  880. state = SCE_PL_DEFAULT;
  881. } else {
  882. if (ch == '\\' && chNext == HereDoc.Quote) { // escaped quote
  883. i++;
  884. ch = chNext;
  885. chNext = chNext2;
  886. }
  887. HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
  888. HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
  889. }
  890. } else { // an unquoted here-doc delimiter
  891. if (isalnum(ch) || ch == '_') {
  892. HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
  893. HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
  894. } else {
  895. styler.ColourTo(i - 1, state);
  896. state = SCE_PL_DEFAULT;
  897. goto restartLexer;
  898. }
  899. }
  900. if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) {
  901. styler.ColourTo(i - 1, state);
  902. state = SCE_PL_ERROR;
  903. goto restartLexer;
  904. }
  905. }
  906. } else if (HereDoc.State == 2) {
  907. // state == SCE_PL_HERE_Q || state == SCE_PL_HERE_QQ || state == SCE_PL_HERE_QX
  908. if (isEOLChar(chPrev) && isMatch(styler, lengthDoc, i, HereDoc.Delimiter)) {
  909. i += HereDoc.DelimiterLength;
  910. chPrev = styler.SafeGetCharAt(i - 1);
  911. ch = styler.SafeGetCharAt(i);
  912. if (isEOLChar(ch)) {
  913. styler.ColourTo(i - 1, state);
  914. state = SCE_PL_DEFAULT;
  915. backflag = BACK_NONE;
  916. HereDoc.State = 0;
  917. goto restartLexer;
  918. }
  919. chNext = styler.SafeGetCharAt(i + 1);
  920. }
  921. } else if (state == SCE_PL_POD
  922. || state == SCE_PL_POD_VERB) {
  923. if (isEOLChar(chPrev)) {
  924. if (ch == ' ' || ch == '\t') {
  925. styler.ColourTo(i - 1, state);
  926. state = SCE_PL_POD_VERB;
  927. } else {
  928. styler.ColourTo(i - 1, state);
  929. state = SCE_PL_POD;
  930. if (ch == '=') {
  931. if (isMatch(styler, lengthDoc, i, "=cut")) {
  932. styler.ColourTo(i - 1 + 4, state);
  933. i += 4;
  934. state = SCE_PL_DEFAULT;
  935. ch = styler.SafeGetCharAt(i);
  936. //chNext = styler.SafeGetCharAt(i + 1);
  937. goto restartLexer;
  938. }
  939. }
  940. }
  941. }
  942. } else if (state == SCE_PL_SCALAR // variable names
  943. || state == SCE_PL_ARRAY
  944. || state == SCE_PL_HASH
  945. || state == SCE_PL_SYMBOLTABLE) {
  946. if (ch == ':' && chNext == ':') { // skip ::
  947. i++;
  948. ch = chNext;
  949. chNext = chNext2;
  950. }
  951. else if (isEndVar(ch)) {
  952. if (i == (styler.GetStartSegment() + 1)) {
  953. // Special variable: $(, $_ etc.
  954. styler.ColourTo(i, state);
  955. state = SCE_PL_DEFAULT;
  956. } else {
  957. styler.ColourTo(i - 1, state);
  958. state = SCE_PL_DEFAULT;
  959. goto restartLexer;
  960. }
  961. }
  962. } else if (state == SCE_PL_REGEX
  963. || state == SCE_PL_STRING_QR
  964. ) {
  965. if (!Quote.Up && !isspacechar(ch)) {
  966. Quote.Open(ch);
  967. } else if (ch == '\\' && Quote.Up != '\\') {
  968. // SG: Is it save to skip *every* escaped char?
  969. i++;
  970. ch = chNext;
  971. chNext = styler.SafeGetCharAt(i + 1);
  972. } else {
  973. if (ch == Quote.Down /*&& chPrev != '\\'*/) {
  974. Quote.Count--;
  975. if (Quote.Count == 0) {
  976. Quote.Rep--;
  977. if (Quote.Up == Quote.Down) {
  978. Quote.Count++;
  979. }
  980. }
  981. if (!isalpha(chNext)) {
  982. if (Quote.Rep <= 0) {
  983. styler.ColourTo(i, state);
  984. state = SCE_PL_DEFAULT;
  985. ch = ' ';
  986. }
  987. }
  988. } else if (ch == Quote.Up /*&& chPrev != '\\'*/) {
  989. Quote.Count++;
  990. } else if (!isascii(chNext) || !isalpha(chNext)) {
  991. if (Quote.Rep <= 0) {
  992. styler.ColourTo(i, state);
  993. state = SCE_PL_DEFAULT;
  994. ch = ' ';
  995. }
  996. }
  997. }
  998. } else if (state == SCE_PL_REGSUBST) {
  999. if (!Quote.Up && !isspacechar(ch)) {
  1000. Quote.Open(ch);
  1001. } else if (ch == '\\' && Quote.Up != '\\') {
  1002. // SG: Is it save to skip *every* escaped char?
  1003. i++;
  1004. ch = chNext;
  1005. chNext = styler.SafeGetCharAt(i + 1);
  1006. } else {
  1007. if (Quote.Count == 0 && Quote.Rep == 1) {
  1008. /* We matched something like s(...) or tr{...}
  1009. * and are looking for the next matcher characters,
  1010. * which could be either bracketed ({...}) or non-bracketed
  1011. * (/.../).
  1012. *
  1013. * Number-signs are problematic. If they occur after
  1014. * the close of the first part, treat them like
  1015. * a Quote.Up char, even if they actually start comments.
  1016. *
  1017. * If we find an alnum, we end the regsubst, and punt.
  1018. *
  1019. * Eric Promislow ericp@activestate.com Aug 9,2000
  1020. */
  1021. if (isspacechar(ch)) {
  1022. // Keep going
  1023. }
  1024. else if (!isascii(ch) || isalnum(ch)) {
  1025. styler.ColourTo(i, state);
  1026. state = SCE_PL_DEFAULT;
  1027. ch = ' ';
  1028. } else {
  1029. Quote.Open(ch);
  1030. }
  1031. } else if (ch == Quote.Down /*&& chPrev != '\\'*/) {
  1032. Quote.Count--;
  1033. if (Quote.Count == 0) {
  1034. Quote.Rep--;
  1035. }
  1036. if (!isascii(chNext) || !isalpha(chNext)) {
  1037. if (Quote.Rep <= 0) {
  1038. styler.ColourTo(i, state);
  1039. state = SCE_PL_DEFAULT;
  1040. ch = ' ';
  1041. }
  1042. }
  1043. if (Quote.Up == Quote.Down) {
  1044. Quote.Count++;
  1045. }
  1046. } else if (ch == Quote.Up /*&& chPrev != '\\'*/) {
  1047. Quote.Count++;
  1048. } else if (!isascii(chNext) || !isalpha(chNext)) {
  1049. if (Quote.Rep <= 0) {
  1050. styler.ColourTo(i, state);
  1051. state = SCE_PL_DEFAULT;
  1052. ch = ' ';
  1053. }
  1054. }
  1055. }
  1056. } else if (state == SCE_PL_STRING_Q
  1057. || state == SCE_PL_STRING_QQ
  1058. || state == SCE_PL_STRING_QX
  1059. || state == SCE_PL_STRING_QW
  1060. || state == SCE_PL_STRING
  1061. || state == SCE_PL_CHARACTER
  1062. || state == SCE_PL_BACKTICKS
  1063. ) {
  1064. if (!Quote.Down && !isspacechar(ch)) {
  1065. Quote.Open(ch);
  1066. } else if (ch == '\\' && Quote.Up != '\\') {
  1067. i++;
  1068. ch = chNext;
  1069. chNext = styler.SafeGetCharAt(i + 1);
  1070. } else if (ch == Quote.Down) {
  1071. Quote.Count--;
  1072. if (Quote.Count == 0) {
  1073. Quote.Rep--;
  1074. if (Quote.Rep <= 0) {
  1075. styler.ColourTo(i, state);
  1076. state = SCE_PL_DEFAULT;
  1077. ch = ' ';
  1078. }
  1079. if (Quote.Up == Quote.Down) {
  1080. Quote.Count++;
  1081. }
  1082. }
  1083. } else if (ch == Quote.Up) {
  1084. Quote.Count++;
  1085. }
  1086. }
  1087. }
  1088. if (state == SCE_PL_ERROR) {
  1089. break;
  1090. }
  1091. chPrev = ch;
  1092. }
  1093. styler.ColourTo(lengthDoc - 1, state);
  1094. }
  1095. static bool IsCommentLine(int line, Accessor &styler) {
  1096. int pos = styler.LineStart(line);
  1097. int eol_pos = styler.LineStart(line + 1) - 1;
  1098. for (int i = pos; i < eol_pos; i++) {
  1099. char ch = styler[i];
  1100. int style = styler.StyleAt(i);
  1101. if (ch == '#' && style == SCE_PL_COMMENTLINE)
  1102. return true;
  1103. else if (ch != ' ' && ch != '\t')
  1104. return false;
  1105. }
  1106. return false;
  1107. }
  1108. static void FoldPerlDoc(unsigned int startPos, int length, int, WordList *[],
  1109. Accessor &styler) {
  1110. bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
  1111. bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
  1112. // Custom folding of POD and packages
  1113. bool foldPOD = styler.GetPropertyInt("fold.perl.pod", 1) != 0;
  1114. bool foldPackage = styler.GetPropertyInt("fold.perl.package", 1) != 0;
  1115. unsigned int endPos = startPos + length;
  1116. int visibleChars = 0;
  1117. int lineCurrent = styler.GetLine(startPos);
  1118. int levelPrev = SC_FOLDLEVELBASE;
  1119. if (lineCurrent > 0)
  1120. levelPrev = styler.LevelAt(lineCurrent - 1) >> 16;
  1121. int levelCurrent = levelPrev;
  1122. char chNext = styler[startPos];
  1123. char chPrev = styler.SafeGetCharAt(startPos - 1);
  1124. int styleNext = styler.StyleAt(startPos);
  1125. // Used at end of line to determine if the line was a package definition
  1126. bool isPackageLine = false;
  1127. bool isPodHeading = false;
  1128. for (unsigned int i = startPos; i < endPos; i++) {
  1129. char ch = chNext;
  1130. chNext = styler.SafeGetCharAt(i + 1);
  1131. int style = styleNext;
  1132. styleNext = styler.StyleAt(i + 1);
  1133. bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
  1134. bool atLineStart = isEOLChar(chPrev) || i == 0;
  1135. // Comment folding
  1136. if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
  1137. {
  1138. if (!IsCommentLine(lineCurrent - 1, styler)
  1139. && IsCommentLine(lineCurrent + 1, styler))
  1140. levelCurrent++;
  1141. else if (IsCommentLine(lineCurrent - 1, styler)
  1142. && !IsCommentLine(lineCurrent+1, styler))
  1143. levelCurrent--;
  1144. }
  1145. if (style == SCE_C_OPERATOR) {
  1146. if (ch == '{') {
  1147. levelCurrent++;
  1148. } else if (ch == '}') {
  1149. levelCurrent--;
  1150. }
  1151. }
  1152. // Custom POD folding
  1153. if (foldPOD && atLineStart) {
  1154. int stylePrevCh = (i) ? styler.StyleAt(i - 1):SCE_PL_DEFAULT;
  1155. if (style == SCE_PL_POD) {
  1156. if (stylePrevCh != SCE_PL_POD && stylePrevCh != SCE_PL_POD_VERB)
  1157. levelCurrent++;
  1158. else if (styler.Match(i, "=cut"))
  1159. levelCurrent--;
  1160. else if (styler.Match(i, "=head"))
  1161. isPodHeading = true;
  1162. } else if (style == SCE_PL_DATASECTION) {
  1163. if (ch == '=' && isalpha(chNext) && levelCurrent == SC_FOLDLEVELBASE)
  1164. levelCurrent++;
  1165. else if (styler.Match(i, "=cut") && levelCurrent > SC_FOLDLEVELBASE)
  1166. levelCurrent--;
  1167. else if (styler.Match(i, "=head"))
  1168. isPodHeading = true;
  1169. // if package used or unclosed brace, level > SC_FOLDLEVELBASE!
  1170. // reset needed as level test is vs. SC_FOLDLEVELBASE
  1171. else if (styler.Match(i, "__END__"))
  1172. levelCurrent = SC_FOLDLEVELBASE;
  1173. }
  1174. }
  1175. // Custom package folding
  1176. if (foldPackage && atLineStart) {
  1177. if (style == SCE_PL_WORD && styler.Match(i, "package")) {
  1178. isPackageLine = true;
  1179. }
  1180. }
  1181. if (atEOL) {
  1182. int lev = levelPrev;
  1183. if (isPodHeading) {
  1184. lev = levelPrev - 1;
  1185. lev |= SC_FOLDLEVELHEADERFLAG;
  1186. isPodHeading = false;
  1187. }
  1188. // Check if line was a package declaration
  1189. // because packages need "special" treatment
  1190. if (isPackageLine) {
  1191. lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
  1192. levelCurrent = SC_FOLDLEVELBASE + 1;
  1193. isPackageLine = false;
  1194. }
  1195. lev |= levelCurrent << 16;
  1196. if (visibleChars == 0 && foldCompact)
  1197. lev |= SC_FOLDLEVELWHITEFLAG;
  1198. if ((levelCurrent > levelPrev) && (visibleChars > 0))
  1199. lev |= SC_FOLDLEVELHEADERFLAG;
  1200. if (lev != styler.LevelAt(lineCurrent)) {
  1201. styler.SetLevel(lineCurrent, lev);
  1202. }
  1203. lineCurrent++;
  1204. levelPrev = levelCurrent;
  1205. visibleChars = 0;
  1206. }
  1207. if (!isspacechar(ch))
  1208. visibleChars++;
  1209. chPrev = ch;
  1210. }
  1211. // Fill in the real level of the next line, keeping the current flags as they will be filled in later
  1212. int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
  1213. styler.SetLevel(lineCurrent, levelPrev | flagsNext);
  1214. }
  1215. static const char * const perlWordListDesc[] = {
  1216. "Keywords",
  1217. 0
  1218. };
  1219. LexerModule lmPerl(SCLEX_PERL, ColourisePerlDoc, "perl", FoldPerlDoc, perlWordListDesc);