1// Scintilla source code edit control 2/** @file LexTADS3.cxx 3 ** Lexer for TADS3. 4 **/ 5/* Copyright 2005 by Michael Cartmell 6 * Parts copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org> 7 * In particular FoldTADS3Doc is derived from FoldCppDoc 8 * The License.txt file describes the conditions under which this software may 9 * be distributed. 10 */ 11 12/* 13 * TADS3 is a language designed by Michael J. Roberts for the writing of text 14 * based games. TADS comes from Text Adventure Development System. It has good 15 * support for the processing and outputting of formatted text and much of a 16 * TADS program listing consists of strings. 17 * 18 * TADS has two types of strings, those enclosed in single quotes (') and those 19 * enclosed in double quotes ("). These strings have different symantics and 20 * can be given different highlighting if desired. 21 * 22 * There can be embedded within both types of strings html tags 23 * ( <tag key=value> ), library directives ( <.directive> ), and message 24 * parameters ( {The doctor's/his} ). 25 * 26 * Double quoted strings can also contain interpolated expressions 27 * ( << rug.moved ? ' and a hole in the floor. ' : nil >> ). These expressions 28 * may themselves contain single or double quoted strings, although the double 29 * quoted strings may not contain interpolated expressions. 30 * 31 * These embedded constructs influence the output and formatting and are an 32 * important part of a program and require highlighting. 33 * 34 * LINKS 35 * http://www.tads.org/ 36 */ 37 38#include <stdlib.h> 39#include <string.h> 40#include <ctype.h> 41#include <stdio.h> 42#include <stdarg.h> 43 44#include "Platform.h" 45 46#include "PropSet.h" 47#include "Accessor.h" 48#include "StyleContext.h" 49#include "KeyWords.h" 50#include "Scintilla.h" 51#include "SciLexer.h" 52 53static const int T3_SINGLE_QUOTE = 1; 54static const int T3_INT_EXPRESSION = 2; 55 56static inline bool IsEOL(const int ch, const int chNext) { 57 return (ch == '\r' && chNext != '\n') || (ch == '\n'); 58} 59 60static inline bool IsASpaceOrTab(const int ch) { 61 return ch == ' ' || ch == '\t'; 62} 63 64static inline bool IsATADS3Operator(const int ch) { 65 return ch == '=' || ch == '{' || ch == '}' || ch == '(' || ch == ')' 66 || ch == '[' || ch == ']' || ch == ',' || ch == ':' || ch == ';' 67 || ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%' 68 || ch == '?' || ch == '!' || ch == '<' || ch == '>' || ch == '|' 69 || ch == '@' || ch == '&' || ch == '~'; 70} 71 72static inline bool IsAWordChar(const int ch) { 73 return isalnum(ch) || ch == '_' || ch == '.'; 74} 75 76static inline bool IsAWordStart(const int ch) { 77 return isalpha(ch) || ch == '_'; 78} 79 80static inline bool IsAHexDigit(const int ch) { 81 int lch = tolower(ch); 82 return isdigit(lch) || lch == 'a' || lch == 'b' || lch == 'c' 83 || lch == 'd' || lch == 'e' || lch == 'f'; 84} 85 86static inline bool IsAnHTMLChar(int ch) { 87 return isalnum(ch) || ch == '-' || ch == '_' || ch == '.'; 88} 89 90static inline bool IsADirectiveChar(int ch) { 91 return isalnum(ch) || isspace(ch) || ch == '-' || ch == '/'; 92} 93 94static inline bool IsANumberStart(StyleContext &sc) { 95 return isdigit(sc.ch) 96 || (!isdigit(sc.chPrev) && sc.ch == '.' && isdigit(sc.chNext)); 97} 98 99inline static void ColouriseTADS3Operator(StyleContext &sc) { 100 int initState = sc.state; 101 sc.SetState(SCE_T3_OPERATOR); 102 sc.ForwardSetState(initState); 103} 104 105static void ColouriseTADSHTMLString(StyleContext &sc, int &lineState) { 106 int endState = sc.state; 107 int chQuote = sc.ch; 108 if (endState == SCE_T3_HTML_STRING) { 109 if (lineState&T3_SINGLE_QUOTE) { 110 endState = SCE_T3_S_STRING; 111 chQuote = '"'; 112 } else if (lineState&T3_INT_EXPRESSION) { 113 endState = SCE_T3_X_STRING; 114 chQuote = '\''; 115 } else { 116 endState = SCE_T3_D_STRING; 117 chQuote = '\''; 118 } 119 } else { 120 sc.SetState(SCE_T3_HTML_STRING); 121 sc.Forward(); 122 } 123 int chString = chQuote == '"'? '\'': '"'; 124 125 while (sc.More()) { 126 if (IsEOL(sc.ch, sc.chNext)) { 127 return; 128 } 129 if (sc.ch == chQuote) { 130 sc.ForwardSetState(endState); 131 return; 132 } 133 if (sc.ch == chString) { 134 sc.SetState(endState); 135 return; 136 } 137 if (sc.Match('\\', static_cast<char>(chQuote)) 138 || sc.Match('\\', static_cast<char>(chString))) { 139 sc.Forward(2); 140 } else { 141 sc.Forward(); 142 } 143 } 144} 145 146static void ColouriseTADS3HTMLTagStart(StyleContext &sc) { 147 sc.SetState(SCE_T3_HTML_TAG); 148 sc.Forward(); 149 if (sc.ch == '/') { 150 sc.Forward(); 151 } 152 while (IsAnHTMLChar(sc.ch)) { 153 sc.Forward(); 154 } 155} 156 157static void ColouriseTADS3HTMLTag(StyleContext &sc, int &lineState) { 158 int endState = sc.state; 159 int chQuote = '"'; 160 int chString = '\''; 161 switch (endState) { 162 case SCE_T3_S_STRING: 163 ColouriseTADS3HTMLTagStart(sc); 164 sc.SetState(SCE_T3_HTML_DEFAULT); 165 chQuote = '\''; 166 chString = '"'; 167 break; 168 case SCE_T3_D_STRING: 169 case SCE_T3_X_STRING: 170 ColouriseTADS3HTMLTagStart(sc); 171 sc.SetState(SCE_T3_HTML_DEFAULT); 172 break; 173 case SCE_T3_HTML_DEFAULT: 174 if (lineState&T3_SINGLE_QUOTE) { 175 endState = SCE_T3_S_STRING; 176 chQuote = '\''; 177 chString = '"'; 178 } else if (lineState&T3_INT_EXPRESSION) { 179 endState = SCE_T3_X_STRING; 180 } else { 181 endState = SCE_T3_D_STRING; 182 } 183 break; 184 } 185 186 while (sc.More()) { 187 if (IsEOL(sc.ch, sc.chNext)) { 188 return; 189 } 190 if (sc.Match('/', '>')) { 191 sc.SetState(SCE_T3_HTML_TAG); 192 sc.Forward(2); 193 sc.SetState(endState); 194 return; 195 } 196 if (sc.ch == '>') { 197 sc.SetState(SCE_T3_HTML_TAG); 198 sc.ForwardSetState(endState); 199 return; 200 } 201 if (sc.ch == chQuote) { 202 sc.SetState(endState); 203 return; 204 } 205 if (sc.ch == chString) { 206 ColouriseTADSHTMLString(sc, lineState); 207 } else if (sc.ch == '=') { 208 ColouriseTADS3Operator(sc); 209 } else { 210 sc.Forward(); 211 } 212 } 213} 214 215static void ColouriseTADS3Keyword(StyleContext &sc, 216 WordList *keywordlists[], unsigned int endPos) { 217 char s[250]; 218 WordList &keywords = *keywordlists[0]; 219 WordList &userwords1 = *keywordlists[1]; 220 WordList &userwords2 = *keywordlists[2]; 221 WordList &userwords3 = *keywordlists[3]; 222 int initState = sc.state; 223 sc.SetState(SCE_T3_IDENTIFIER); 224 while (sc.More() && (IsAWordChar(sc.ch))) { 225 sc.Forward(); 226 } 227 sc.GetCurrent(s, sizeof(s)); 228 if ( strcmp(s, "is") == 0 || strcmp(s, "not") == 0) { 229 // have to find if "in" is next 230 int n = 1; 231 while (n + sc.currentPos < endPos && IsASpaceOrTab(sc.GetRelative(n))) 232 n++; 233 if (sc.GetRelative(n) == 'i' && sc.GetRelative(n+1) == 'n') { 234 sc.Forward(n+2); 235 sc.ChangeState(SCE_T3_KEYWORD); 236 } 237 } else if (keywords.InList(s)) { 238 sc.ChangeState(SCE_T3_KEYWORD); 239 } else if (userwords3.InList(s)) { 240 sc.ChangeState(SCE_T3_USER3); 241 } else if (userwords2.InList(s)) { 242 sc.ChangeState(SCE_T3_USER2); 243 } else if (userwords1.InList(s)) { 244 sc.ChangeState(SCE_T3_USER1); 245 } 246 sc.SetState(initState); 247} 248 249static void ColouriseTADS3MsgParam(StyleContext &sc, int &lineState) { 250 int endState = sc.state; 251 int chQuote = '"'; 252 switch (endState) { 253 case SCE_T3_S_STRING: 254 sc.SetState(SCE_T3_MSG_PARAM); 255 sc.Forward(); 256 chQuote = '\''; 257 break; 258 case SCE_T3_D_STRING: 259 case SCE_T3_X_STRING: 260 sc.SetState(SCE_T3_MSG_PARAM); 261 sc.Forward(); 262 break; 263 case SCE_T3_MSG_PARAM: 264 if (lineState&T3_SINGLE_QUOTE) { 265 endState = SCE_T3_S_STRING; 266 chQuote = '\''; 267 } else if (lineState&T3_INT_EXPRESSION) { 268 endState = SCE_T3_X_STRING; 269 } else { 270 endState = SCE_T3_D_STRING; 271 } 272 break; 273 } 274 while (sc.More() && sc.ch != '}' && sc.ch != chQuote) { 275 if (IsEOL(sc.ch, sc.chNext)) { 276 return; 277 } 278 if (sc.ch == '\\') { 279 sc.Forward(); 280 } 281 sc.Forward(); 282 } 283 if (sc.ch == chQuote) { 284 sc.SetState(endState); 285 } else { 286 sc.ForwardSetState(endState); 287 } 288} 289 290static void ColouriseTADS3LibDirective(StyleContext &sc, int &lineState) { 291 int initState = sc.state; 292 int chQuote = '"'; 293 switch (initState) { 294 case SCE_T3_S_STRING: 295 sc.SetState(SCE_T3_LIB_DIRECTIVE); 296 sc.Forward(2); 297 chQuote = '\''; 298 break; 299 case SCE_T3_D_STRING: 300 sc.SetState(SCE_T3_LIB_DIRECTIVE); 301 sc.Forward(2); 302 break; 303 case SCE_T3_LIB_DIRECTIVE: 304 if (lineState&T3_SINGLE_QUOTE) { 305 initState = SCE_T3_S_STRING; 306 chQuote = '\''; 307 } else { 308 initState = SCE_T3_D_STRING; 309 } 310 break; 311 } 312 while (sc.More() && IsADirectiveChar(sc.ch)) { 313 if (IsEOL(sc.ch, sc.chNext)) { 314 return; 315 } 316 sc.Forward(); 317 }; 318 if (sc.ch == '>' || !sc.More()) { 319 sc.ForwardSetState(initState); 320 } else if (sc.ch == chQuote) { 321 sc.SetState(initState); 322 } else { 323 sc.ChangeState(initState); 324 sc.Forward(); 325 } 326} 327 328static void ColouriseTADS3String(StyleContext &sc, int &lineState) { 329 int chQuote = sc.ch; 330 int endState = sc.state; 331 switch (sc.state) { 332 case SCE_T3_DEFAULT: 333 case SCE_T3_X_DEFAULT: 334 if (chQuote == '"') { 335 if (sc.state == SCE_T3_DEFAULT) { 336 sc.SetState(SCE_T3_D_STRING); 337 } else { 338 sc.SetState(SCE_T3_X_STRING); 339 } 340 lineState &= ~T3_SINGLE_QUOTE; 341 } else { 342 sc.SetState(SCE_T3_S_STRING); 343 lineState |= T3_SINGLE_QUOTE; 344 } 345 sc.Forward(); 346 break; 347 case SCE_T3_S_STRING: 348 chQuote = '\''; 349 endState = lineState&T3_INT_EXPRESSION ? 350 SCE_T3_X_DEFAULT : SCE_T3_DEFAULT; 351 break; 352 case SCE_T3_D_STRING: 353 chQuote = '"'; 354 endState = SCE_T3_DEFAULT; 355 break; 356 case SCE_T3_X_STRING: 357 chQuote = '"'; 358 endState = SCE_T3_X_DEFAULT; 359 break; 360 } 361 while (sc.More()) { 362 if (IsEOL(sc.ch, sc.chNext)) { 363 return; 364 } 365 if (sc.ch == chQuote) { 366 sc.ForwardSetState(endState); 367 return; 368 } 369 if (sc.state == SCE_T3_D_STRING && sc.Match('<', '<')) { 370 lineState |= T3_INT_EXPRESSION; 371 sc.SetState(SCE_T3_X_DEFAULT); 372 sc.Forward(2); 373 return; 374 } 375 if (sc.Match('\\', static_cast<char>(chQuote))) { 376 sc.Forward(2); 377 } else if (sc.ch == '{') { 378 ColouriseTADS3MsgParam(sc, lineState); 379 } else if (sc.Match('<', '.')) { 380 ColouriseTADS3LibDirective(sc, lineState); 381 } else if (sc.ch == '<') { 382 ColouriseTADS3HTMLTag(sc, lineState); 383 } else { 384 sc.Forward(); 385 } 386 } 387} 388 389static void ColouriseTADS3Comment(StyleContext &sc, int endState) { 390 sc.SetState(SCE_T3_BLOCK_COMMENT); 391 while (sc.More()) { 392 if (IsEOL(sc.ch, sc.chNext)) { 393 return; 394 } 395 if (sc.Match('*', '/')) { 396 sc.Forward(2); 397 sc.SetState(endState); 398 return; 399 } 400 sc.Forward(); 401 } 402} 403 404static void ColouriseToEndOfLine(StyleContext &sc, int initState, int endState) { 405 sc.SetState(initState); 406 while (sc.More()) { 407 if (sc.ch == '\\') { 408 sc.Forward(); 409 if (IsEOL(sc.ch, sc.chNext)) { 410 return; 411 } 412 } 413 if (IsEOL(sc.ch, sc.chNext)) { 414 sc.SetState(endState); 415 return; 416 } 417 sc.Forward(); 418 } 419} 420 421static void ColouriseTADS3Number(StyleContext &sc) { 422 int endState = sc.state; 423 bool inHexNumber = false; 424 bool seenE = false; 425 bool seenDot = sc.ch == '.'; 426 sc.SetState(SCE_T3_NUMBER); 427 if (sc.More()) { 428 sc.Forward(); 429 } 430 if (sc.chPrev == '0' && tolower(sc.ch) == 'x') { 431 inHexNumber = true; 432 sc.Forward(); 433 } 434 while (sc.More()) { 435 if (inHexNumber) { 436 if (!IsAHexDigit(sc.ch)) { 437 break; 438 } 439 } else if (!isdigit(sc.ch)) { 440 if (!seenE && tolower(sc.ch) == 'e') { 441 seenE = true; 442 seenDot = true; 443 if (sc.chNext == '+' || sc.chNext == '-') { 444 sc.Forward(); 445 } 446 } else if (!seenDot && sc.ch == '.') { 447 seenDot = true; 448 } else { 449 break; 450 } 451 } 452 sc.Forward(); 453 } 454 sc.SetState(endState); 455} 456 457static void ColouriseTADS3Doc(unsigned int startPos, int length, int initStyle, 458 WordList *keywordlists[], Accessor &styler) { 459 int visibleChars = 0; 460 int bracketLevel = 0; 461 int lineState = 0; 462 unsigned int endPos = startPos + length; 463 int lineCurrent = styler.GetLine(startPos); 464 if (lineCurrent > 0) { 465 lineState = styler.GetLineState(lineCurrent-1); 466 } 467 StyleContext sc(startPos, length, initStyle, styler); 468 469 while (sc.More()) { 470 471 if (IsEOL(sc.ch, sc.chNext)) { 472 styler.SetLineState(lineCurrent, lineState); 473 lineCurrent++; 474 visibleChars = 0; 475 sc.Forward(); 476 if (sc.ch == '\n') { 477 sc.Forward(); 478 } 479 } 480 481 switch(sc.state) { 482 case SCE_T3_PREPROCESSOR: 483 case SCE_T3_LINE_COMMENT: 484 ColouriseToEndOfLine(sc, sc.state, lineState&T3_INT_EXPRESSION ? 485 SCE_T3_X_DEFAULT : SCE_T3_DEFAULT); 486 break; 487 case SCE_T3_S_STRING: 488 case SCE_T3_D_STRING: 489 case SCE_T3_X_STRING: 490 ColouriseTADS3String(sc, lineState); 491 visibleChars++; 492 break; 493 case SCE_T3_MSG_PARAM: 494 ColouriseTADS3MsgParam(sc, lineState); 495 break; 496 case SCE_T3_LIB_DIRECTIVE: 497 ColouriseTADS3LibDirective(sc, lineState); 498 break; 499 case SCE_T3_HTML_DEFAULT: 500 ColouriseTADS3HTMLTag(sc, lineState); 501 break; 502 case SCE_T3_HTML_STRING: 503 ColouriseTADSHTMLString(sc, lineState); 504 break; 505 case SCE_T3_BLOCK_COMMENT: 506 ColouriseTADS3Comment(sc, lineState&T3_INT_EXPRESSION ? 507 SCE_T3_X_DEFAULT : SCE_T3_DEFAULT); 508 break; 509 case SCE_T3_DEFAULT: 510 case SCE_T3_X_DEFAULT: 511 if (IsASpaceOrTab(sc.ch)) { 512 sc.Forward(); 513 } else if (sc.ch == '#' && visibleChars == 0) { 514 ColouriseToEndOfLine(sc, SCE_T3_PREPROCESSOR, sc.state); 515 } else if (sc.Match('/', '*')) { 516 ColouriseTADS3Comment(sc, sc.state); 517 visibleChars++; 518 } else if (sc.Match('/', '/')) { 519 ColouriseToEndOfLine(sc, SCE_T3_LINE_COMMENT, sc.state); 520 } else if (sc.ch == '"') { 521 bracketLevel = 0; 522 ColouriseTADS3String(sc, lineState); 523 visibleChars++; 524 } else if (sc.ch == '\'') { 525 ColouriseTADS3String(sc, lineState); 526 visibleChars++; 527 } else if (sc.state == SCE_T3_X_DEFAULT && bracketLevel == 0 528 && sc.Match('>', '>')) { 529 sc.Forward(2); 530 sc.SetState(SCE_T3_D_STRING); 531 lineState &= ~(T3_SINGLE_QUOTE|T3_INT_EXPRESSION); 532 } else if (IsATADS3Operator(sc.ch)) { 533 if (sc.state == SCE_T3_X_DEFAULT) { 534 if (sc.ch == '(') { 535 bracketLevel++; 536 } else if (sc.ch == ')') { 537 bracketLevel--; 538 } 539 } 540 ColouriseTADS3Operator(sc); 541 visibleChars++; 542 } else if (IsANumberStart(sc)) { 543 ColouriseTADS3Number(sc); 544 visibleChars++; 545 } else if (IsAWordStart(sc.ch)) { 546 ColouriseTADS3Keyword(sc, keywordlists, endPos); 547 visibleChars++; 548 } else if (sc.Match("...")) { 549 sc.SetState(SCE_T3_IDENTIFIER); 550 sc.Forward(3); 551 sc.SetState(SCE_T3_DEFAULT); 552 } else { 553 sc.Forward(); 554 visibleChars++; 555 } 556 break; 557 default: 558 sc.SetState(SCE_T3_DEFAULT); 559 sc.Forward(); 560 } 561 } 562 sc.Complete(); 563} 564 565/* 566 TADS3 has two styles of top level block (TLB). Eg 567 568 // default style 569 silverKey : Key 'small silver key' 'small silver key' 570 "A small key glints in the sunlight. " 571 ; 572 573 and 574 575 silverKey : Key { 576 'small silver key' 577 'small silver key' 578 "A small key glints in the sunlight. " 579 } 580 581 Some constructs mandate one or the other, but usually the author has may choose 582 either. 583 584 T3_SEENSTART is used to indicate that a braceless TLB has been (potentially) 585 seen and is also used to match the closing ';' of the default style. 586 587 T3_EXPECTINGIDENTIFIER and T3_EXPECTINGPUNCTUATION are used to keep track of 588 what characters may be seen without incrementing the block level. The general 589 pattern is identifier <punc> identifier, acceptable punctuation characters 590 are ':', ',', '(' and ')'. No attempt is made to ensure that punctuation 591 characters are syntactically correct, eg parentheses match. A ')' always 592 signifies the start of a block. We just need to check if it is followed by a 593 '{', in which case we let the brace handling code handle the folding level. 594 595 expectingIdentifier == false && expectingIdentifier == false 596 Before the start of a TLB. 597 598 expectingIdentifier == true && expectingIdentifier == true 599 Currently in an identifier. Will accept identifier or punctuation. 600 601 expectingIdentifier == true && expectingIdentifier == false 602 Just seen a punctuation character & now waiting for an identifier to start. 603 604 expectingIdentifier == false && expectingIdentifier == truee 605 We were in an identifier and have seen space. Now waiting to see a punctuation 606 character 607 608 Space, comments & preprocessor directives are always acceptable and are 609 equivalent. 610*/ 611 612static const int T3_SEENSTART = 1 << 12; 613static const int T3_EXPECTINGIDENTIFIER = 1 << 13; 614static const int T3_EXPECTINGPUNCTUATION = 1 << 14; 615 616static inline bool IsStringTransition(int s1, int s2) { 617 return s1 != s2 618 && (s1 == SCE_T3_S_STRING || s1 == SCE_T3_X_STRING 619 || s1 == SCE_T3_D_STRING && s2 != SCE_T3_X_DEFAULT) 620 && s2 != SCE_T3_LIB_DIRECTIVE 621 && s2 != SCE_T3_MSG_PARAM 622 && s2 != SCE_T3_HTML_TAG 623 && s2 != SCE_T3_HTML_STRING; 624} 625 626static inline bool IsATADS3Punctuation(const int ch) { 627 return ch == ':' || ch == ',' || ch == '(' || ch == ')'; 628} 629 630static inline bool IsAnIdentifier(const int style) { 631 return style == SCE_T3_IDENTIFIER 632 || style == SCE_T3_USER1 633 || style == SCE_T3_USER2 634 || style == SCE_T3_USER3; 635} 636 637static inline bool IsSpaceEquivalent(const int ch, const int style) { 638 return isspace(ch) 639 || style == SCE_T3_BLOCK_COMMENT 640 || style == SCE_T3_LINE_COMMENT 641 || style == SCE_T3_PREPROCESSOR; 642} 643 644static char peekAhead(unsigned int startPos, unsigned int endPos, 645 Accessor &styler) { 646 for (unsigned int i = startPos; i < endPos; i++) { 647 int style = styler.StyleAt(i); 648 char ch = styler[i]; 649 if (!IsSpaceEquivalent(ch, style)) { 650 if (IsAnIdentifier(style)) { 651 return 'a'; 652 } 653 if (IsATADS3Punctuation(ch)) { 654 return ':'; 655 } 656 if (ch == '{') { 657 return '{'; 658 } 659 return '*'; 660 } 661 } 662 return ' '; 663} 664 665static void FoldTADS3Doc(unsigned int startPos, int length, int initStyle, 666 WordList *[], Accessor &styler) { 667 unsigned int endPos = startPos + length; 668 int lineCurrent = styler.GetLine(startPos); 669 int levelCurrent = SC_FOLDLEVELBASE; 670 if (lineCurrent > 0) 671 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; 672 int seenStart = levelCurrent & T3_SEENSTART; 673 int expectingIdentifier = levelCurrent & T3_EXPECTINGIDENTIFIER; 674 int expectingPunctuation = levelCurrent & T3_EXPECTINGPUNCTUATION; 675 levelCurrent &= SC_FOLDLEVELNUMBERMASK; 676 int levelMinCurrent = levelCurrent; 677 int levelNext = levelCurrent; 678 char chNext = styler[startPos]; 679 int styleNext = styler.StyleAt(startPos); 680 int style = initStyle; 681 char ch = chNext; 682 int stylePrev = style; 683 bool redo = false; 684 for (unsigned int i = startPos; i < endPos; i++) { 685 if (redo) { 686 redo = false; 687 i--; 688 } else { 689 ch = chNext; 690 chNext = styler.SafeGetCharAt(i + 1); 691 stylePrev = style; 692 style = styleNext; 693 styleNext = styler.StyleAt(i + 1); 694 } 695 bool atEOL = IsEOL(ch, chNext); 696 697 if (levelNext == SC_FOLDLEVELBASE) { 698 if (IsSpaceEquivalent(ch, style)) { 699 if (expectingPunctuation) { 700 expectingIdentifier = 0; 701 } 702 if (style == SCE_T3_BLOCK_COMMENT) { 703 levelNext++; 704 } 705 } else if (ch == '{') { 706 levelNext++; 707 seenStart = 0; 708 } else if (ch == '\'' || ch == '"' || ch == '[') { 709 levelNext++; 710 if (seenStart) { 711 redo = true; 712 } 713 } else if (ch == ';') { 714 seenStart = 0; 715 expectingIdentifier = 0; 716 expectingPunctuation = 0; 717 } else if (expectingIdentifier && expectingPunctuation) { 718 if (IsATADS3Punctuation(ch)) { 719 if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') { 720 levelNext++; 721 } else { 722 expectingPunctuation = 0; 723 } 724 } else if (!IsAnIdentifier(style)) { 725 levelNext++; 726 } 727 } else if (expectingIdentifier && !expectingPunctuation) { 728 if (!IsAnIdentifier(style)) { 729 levelNext++; 730 } else { 731 expectingPunctuation = T3_EXPECTINGPUNCTUATION; 732 } 733 } else if (!expectingIdentifier && expectingPunctuation) { 734 if (!IsATADS3Punctuation(ch)) { 735 levelNext++; 736 } else { 737 if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') { 738 levelNext++; 739 } else { 740 expectingIdentifier = T3_EXPECTINGIDENTIFIER; 741 expectingPunctuation = 0; 742 } 743 } 744 } else if (!expectingIdentifier && !expectingPunctuation) { 745 if (IsAnIdentifier(style)) { 746 seenStart = T3_SEENSTART; 747 expectingIdentifier = T3_EXPECTINGIDENTIFIER; 748 expectingPunctuation = T3_EXPECTINGPUNCTUATION; 749 } 750 } 751 752 if (levelNext != SC_FOLDLEVELBASE && style != SCE_T3_BLOCK_COMMENT) { 753 expectingIdentifier = 0; 754 expectingPunctuation = 0; 755 } 756 757 } else if (levelNext == SC_FOLDLEVELBASE+1 && seenStart 758 && ch == ';' && style == SCE_T3_OPERATOR ) { 759 levelNext--; 760 seenStart = 0; 761 } else if (style == SCE_T3_BLOCK_COMMENT) { 762 if (stylePrev != SCE_T3_BLOCK_COMMENT) { 763 levelNext++; 764 } else if (styleNext != SCE_T3_BLOCK_COMMENT && !atEOL) { 765 // Comments don't end at end of line and the next character may be unstyled. 766 levelNext--; 767 } 768 } else if (ch == '\'' || ch == '"') { 769 if (IsStringTransition(style, stylePrev)) { 770 if (levelMinCurrent > levelNext) { 771 levelMinCurrent = levelNext; 772 } 773 levelNext++; 774 } else if (IsStringTransition(style, styleNext)) { 775 levelNext--; 776 } 777 } else if (style == SCE_T3_OPERATOR) { 778 if (ch == '{' || ch == '[') { 779 // Measure the minimum before a '{' to allow 780 // folding on "} else {" 781 if (levelMinCurrent > levelNext) { 782 levelMinCurrent = levelNext; 783 } 784 levelNext++; 785 } else if (ch == '}' || ch == ']') { 786 levelNext--; 787 } 788 } 789 790 if (atEOL) { 791 if (seenStart && levelNext == SC_FOLDLEVELBASE) { 792 switch (peekAhead(i+1, endPos, styler)) { 793 case ' ': 794 case '{': 795 break; 796 case '*': 797 levelNext++; 798 break; 799 case 'a': 800 if (expectingPunctuation) { 801 levelNext++; 802 } 803 break; 804 case ':': 805 if (expectingIdentifier) { 806 levelNext++; 807 } 808 break; 809 } 810 if (levelNext != SC_FOLDLEVELBASE) { 811 expectingIdentifier = 0; 812 expectingPunctuation = 0; 813 } 814 } 815 int lev = levelMinCurrent | (levelNext | expectingIdentifier 816 | expectingPunctuation | seenStart) << 16; 817 if (levelMinCurrent < levelNext) 818 lev |= SC_FOLDLEVELHEADERFLAG; 819 if (lev != styler.LevelAt(lineCurrent)) { 820 styler.SetLevel(lineCurrent, lev); 821 } 822 lineCurrent++; 823 levelCurrent = levelNext; 824 levelMinCurrent = levelCurrent; 825 } 826 } 827} 828 829static const char * const tads3WordList[] = { 830 "TADS3 Keywords", 831 "User defined 1", 832 "User defined 2", 833 "User defined 3", 834 0 835}; 836 837LexerModule lmTADS3(SCLEX_TADS3, ColouriseTADS3Doc, "tads3", FoldTADS3Doc, tads3WordList); 838