1 // Scintilla source code edit control
5 // Copyright 1998-2006 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
9 * TADS3 is a language designed by Michael J. Roberts for the writing of text
10 * based games. TADS comes from Text Adventure Development System. It has good
11 * support for the processing and outputting of formatted text and much of a
12 * TADS program listing consists of strings.
14 * TADS has two types of strings, those enclosed in single quotes (') and those
15 * enclosed in double quotes ("). These strings have different symantics and
16 * can be given different highlighting if desired.
18 * There can be embedded within both types of strings html tags
19 * ( <tag key=value> ), library directives ( <.directive> ), and message
20 * parameters ( {The doctor's/his} ).
22 * Double quoted strings can also contain interpolated expressions
23 * ( << rug.moved ? ' and a hole in the floor. ' : nil >> ). These expressions
24 * may themselves contain single or double quoted strings, although the double
25 * quoted strings may not contain interpolated expressions.
27 * These embedded constructs influence the output and formatting and are an
28 * important part of a program and require highlighting.
31 * http://www.tads.org/
42 #include "Scintilla.h"
46 #include "LexAccessor.h"
48 #include "StyleContext.h"
49 #include "CharacterSet.h"
50 #include "LexerModule.h"
53 using namespace Scintilla
;
56 static const int T3_SINGLE_QUOTE
= 1;
57 static const int T3_INT_EXPRESSION
= 2;
58 static const int T3_INT_EXPRESSION_IN_TAG
= 4;
59 static const int T3_HTML_SQUOTE
= 8;
61 static inline bool IsEOL(const int ch
, const int chNext
) {
62 return (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
66 * Test the current character to see if it's the START of an EOL sequence;
67 * if so, skip ahead to the last character of the sequence and return true,
68 * and if not just return false. There are a few places where we want to
69 * check to see if a newline sequence occurs at a particular point, but
70 * where a caller expects a subroutine to stop only upon reaching the END
71 * of a newline sequence (in particular, CR-LF on Windows). That's why
72 * IsEOL() above only returns true on CR if the CR isn't followed by an LF
73 * - it doesn't want to admit that there's a newline until reaching the END
74 * of the sequence. We meet both needs by saying that there's a newline
75 * when we see the CR in a CR-LF, but skipping the CR before returning so
76 * that the caller's caller will see that we've stopped at the LF.
78 static inline bool IsEOLSkip(StyleContext
&sc
)
81 if (sc
.ch
== '\r' && sc
.chNext
== '\n')
83 /* got CR-LF - skip the CR and indicate that we're at a newline */
89 * in other cases, we have at most a 1-character newline, so do the
92 return IsEOL(sc
.ch
, sc
.chNext
);
95 static inline bool IsATADS3Operator(const int ch
) {
96 return ch
== '=' || ch
== '{' || ch
== '}' || ch
== '(' || ch
== ')'
97 || ch
== '[' || ch
== ']' || ch
== ',' || ch
== ':' || ch
== ';'
98 || ch
== '+' || ch
== '-' || ch
== '*' || ch
== '/' || ch
== '%'
99 || ch
== '?' || ch
== '!' || ch
== '<' || ch
== '>' || ch
== '|'
100 || ch
== '@' || ch
== '&' || ch
== '~';
103 static inline bool IsAWordChar(const int ch
) {
104 return isalnum(ch
) || ch
== '_';
107 static inline bool IsAWordStart(const int ch
) {
108 return isalpha(ch
) || ch
== '_';
111 static inline bool IsAHexDigit(const int ch
) {
112 int lch
= tolower(ch
);
113 return isdigit(lch
) || lch
== 'a' || lch
== 'b' || lch
== 'c'
114 || lch
== 'd' || lch
== 'e' || lch
== 'f';
117 static inline bool IsAnHTMLChar(int ch
) {
118 return isalnum(ch
) || ch
== '-' || ch
== '_' || ch
== '.';
121 static inline bool IsADirectiveChar(int ch
) {
122 return isalnum(ch
) || isspace(ch
) || ch
== '-' || ch
== '/';
125 static inline bool IsANumberStart(StyleContext
&sc
) {
126 return isdigit(sc
.ch
)
127 || (!isdigit(sc
.chPrev
) && sc
.ch
== '.' && isdigit(sc
.chNext
));
130 inline static void ColouriseTADS3Operator(StyleContext
&sc
) {
131 int initState
= sc
.state
;
133 sc
.SetState(c
== '{' || c
== '}' ? SCE_T3_BRACE
: SCE_T3_OPERATOR
);
134 sc
.ForwardSetState(initState
);
137 static void ColouriseTADSHTMLString(StyleContext
&sc
, int &lineState
) {
138 int endState
= sc
.state
;
140 int chString
= (lineState
& T3_SINGLE_QUOTE
) ? '\'' : '"';
141 if (endState
== SCE_T3_HTML_STRING
) {
142 if (lineState
&T3_SINGLE_QUOTE
) {
143 endState
= SCE_T3_S_STRING
;
145 } else if (lineState
&T3_INT_EXPRESSION
) {
146 endState
= SCE_T3_X_STRING
;
149 endState
= SCE_T3_HTML_DEFAULT
;
152 chQuote
= (lineState
& T3_HTML_SQUOTE
) ? '\'' : '"';
154 sc
.SetState(SCE_T3_HTML_STRING
);
158 lineState
&= ~T3_HTML_SQUOTE
;
160 lineState
|= T3_HTML_SQUOTE
;
163 if (IsEOL(sc
.ch
, sc
.chNext
)) {
166 if (sc
.ch
== chQuote
) {
167 sc
.ForwardSetState(endState
);
170 if (sc
.Match('\\', static_cast<char>(chQuote
))) {
172 sc
.SetState(endState
);
175 if (sc
.ch
== chString
) {
176 sc
.SetState(SCE_T3_DEFAULT
);
180 if (sc
.Match('<', '<')) {
181 lineState
|= T3_INT_EXPRESSION
| T3_INT_EXPRESSION_IN_TAG
;
182 sc
.SetState(SCE_T3_X_DEFAULT
);
187 if (sc
.Match('\\', static_cast<char>(chQuote
))
188 || sc
.Match('\\', static_cast<char>(chString
))
189 || sc
.Match('\\', '\\')) {
197 static void ColouriseTADS3HTMLTagStart(StyleContext
&sc
) {
198 sc
.SetState(SCE_T3_HTML_TAG
);
203 while (IsAnHTMLChar(sc
.ch
)) {
208 static void ColouriseTADS3HTMLTag(StyleContext
&sc
, int &lineState
) {
209 int endState
= sc
.state
;
213 case SCE_T3_S_STRING
:
214 ColouriseTADS3HTMLTagStart(sc
);
215 sc
.SetState(SCE_T3_HTML_DEFAULT
);
219 case SCE_T3_D_STRING
:
220 case SCE_T3_X_STRING
:
221 ColouriseTADS3HTMLTagStart(sc
);
222 sc
.SetState(SCE_T3_HTML_DEFAULT
);
224 case SCE_T3_HTML_DEFAULT
:
225 if (lineState
&T3_SINGLE_QUOTE
) {
226 endState
= SCE_T3_S_STRING
;
229 } else if (lineState
&T3_INT_EXPRESSION
) {
230 endState
= SCE_T3_X_STRING
;
232 endState
= SCE_T3_D_STRING
;
238 if (IsEOL(sc
.ch
, sc
.chNext
)) {
241 if (sc
.Match('/', '>')) {
242 sc
.SetState(SCE_T3_HTML_TAG
);
244 sc
.SetState(endState
);
248 sc
.SetState(SCE_T3_HTML_TAG
);
249 sc
.ForwardSetState(endState
);
252 if (sc
.ch
== chQuote
) {
253 sc
.SetState(endState
);
256 if (sc
.Match('\\', static_cast<char>(chQuote
))) {
258 ColouriseTADSHTMLString(sc
, lineState
);
259 if (sc
.state
== SCE_T3_X_DEFAULT
)
261 } else if (sc
.ch
== chString
) {
262 ColouriseTADSHTMLString(sc
, lineState
);
263 } else if (sc
.ch
== '=') {
264 ColouriseTADS3Operator(sc
);
271 static void ColouriseTADS3Keyword(StyleContext
&sc
,
272 WordList
*keywordlists
[], unsigned int endPos
) {
274 WordList
&keywords
= *keywordlists
[0];
275 WordList
&userwords1
= *keywordlists
[1];
276 WordList
&userwords2
= *keywordlists
[2];
277 WordList
&userwords3
= *keywordlists
[3];
278 int initState
= sc
.state
;
279 sc
.SetState(SCE_T3_IDENTIFIER
);
280 while (sc
.More() && (IsAWordChar(sc
.ch
))) {
283 sc
.GetCurrent(s
, sizeof(s
));
284 if ( strcmp(s
, "is") == 0 || strcmp(s
, "not") == 0) {
285 // have to find if "in" is next
287 while (n
+ sc
.currentPos
< endPos
&& IsASpaceOrTab(sc
.GetRelative(n
)))
289 if (sc
.GetRelative(n
) == 'i' && sc
.GetRelative(n
+1) == 'n') {
291 sc
.ChangeState(SCE_T3_KEYWORD
);
293 } else if (keywords
.InList(s
)) {
294 sc
.ChangeState(SCE_T3_KEYWORD
);
295 } else if (userwords3
.InList(s
)) {
296 sc
.ChangeState(SCE_T3_USER3
);
297 } else if (userwords2
.InList(s
)) {
298 sc
.ChangeState(SCE_T3_USER2
);
299 } else if (userwords1
.InList(s
)) {
300 sc
.ChangeState(SCE_T3_USER1
);
302 sc
.SetState(initState
);
305 static void ColouriseTADS3MsgParam(StyleContext
&sc
, int &lineState
) {
306 int endState
= sc
.state
;
309 case SCE_T3_S_STRING
:
310 sc
.SetState(SCE_T3_MSG_PARAM
);
314 case SCE_T3_D_STRING
:
315 case SCE_T3_X_STRING
:
316 sc
.SetState(SCE_T3_MSG_PARAM
);
319 case SCE_T3_MSG_PARAM
:
320 if (lineState
&T3_SINGLE_QUOTE
) {
321 endState
= SCE_T3_S_STRING
;
323 } else if (lineState
&T3_INT_EXPRESSION
) {
324 endState
= SCE_T3_X_STRING
;
326 endState
= SCE_T3_D_STRING
;
330 while (sc
.More() && sc
.ch
!= '}' && sc
.ch
!= chQuote
) {
331 if (IsEOL(sc
.ch
, sc
.chNext
)) {
339 if (sc
.ch
== chQuote
) {
340 sc
.SetState(endState
);
342 sc
.ForwardSetState(endState
);
346 static void ColouriseTADS3LibDirective(StyleContext
&sc
, int &lineState
) {
347 int initState
= sc
.state
;
350 case SCE_T3_S_STRING
:
351 sc
.SetState(SCE_T3_LIB_DIRECTIVE
);
355 case SCE_T3_D_STRING
:
356 sc
.SetState(SCE_T3_LIB_DIRECTIVE
);
359 case SCE_T3_LIB_DIRECTIVE
:
360 if (lineState
&T3_SINGLE_QUOTE
) {
361 initState
= SCE_T3_S_STRING
;
364 initState
= SCE_T3_D_STRING
;
368 while (sc
.More() && IsADirectiveChar(sc
.ch
)) {
369 if (IsEOL(sc
.ch
, sc
.chNext
)) {
374 if (sc
.ch
== '>' || !sc
.More()) {
375 sc
.ForwardSetState(initState
);
376 } else if (sc
.ch
== chQuote
) {
377 sc
.SetState(initState
);
379 sc
.ChangeState(initState
);
384 static void ColouriseTADS3String(StyleContext
&sc
, int &lineState
) {
386 int endState
= sc
.state
;
389 case SCE_T3_X_DEFAULT
:
390 if (chQuote
== '"') {
391 if (sc
.state
== SCE_T3_DEFAULT
) {
392 sc
.SetState(SCE_T3_D_STRING
);
394 sc
.SetState(SCE_T3_X_STRING
);
396 lineState
&= ~T3_SINGLE_QUOTE
;
398 sc
.SetState(SCE_T3_S_STRING
);
399 lineState
|= T3_SINGLE_QUOTE
;
403 case SCE_T3_S_STRING
:
405 endState
= lineState
&T3_INT_EXPRESSION
?
406 SCE_T3_X_DEFAULT
: SCE_T3_DEFAULT
;
408 case SCE_T3_D_STRING
:
410 endState
= SCE_T3_DEFAULT
;
412 case SCE_T3_X_STRING
:
414 endState
= SCE_T3_X_DEFAULT
;
418 if (IsEOL(sc
.ch
, sc
.chNext
)) {
421 if (sc
.ch
== chQuote
) {
422 sc
.ForwardSetState(endState
);
425 if (sc
.state
== SCE_T3_D_STRING
&& sc
.Match('<', '<')) {
426 lineState
|= T3_INT_EXPRESSION
;
427 sc
.SetState(SCE_T3_X_DEFAULT
);
431 if (sc
.Match('\\', static_cast<char>(chQuote
))
432 || sc
.Match('\\', '\\')) {
434 } else if (sc
.ch
== '{') {
435 ColouriseTADS3MsgParam(sc
, lineState
);
436 } else if (sc
.Match('<', '.')) {
437 ColouriseTADS3LibDirective(sc
, lineState
);
438 } else if (sc
.ch
== '<') {
439 ColouriseTADS3HTMLTag(sc
, lineState
);
440 if (sc
.state
== SCE_T3_X_DEFAULT
)
448 static void ColouriseTADS3Comment(StyleContext
&sc
, int endState
) {
449 sc
.SetState(SCE_T3_BLOCK_COMMENT
);
451 if (IsEOL(sc
.ch
, sc
.chNext
)) {
454 if (sc
.Match('*', '/')) {
456 sc
.SetState(endState
);
463 static void ColouriseToEndOfLine(StyleContext
&sc
, int initState
, int endState
) {
464 sc
.SetState(initState
);
472 if (IsEOL(sc
.ch
, sc
.chNext
)) {
473 sc
.SetState(endState
);
480 static void ColouriseTADS3Number(StyleContext
&sc
) {
481 int endState
= sc
.state
;
482 bool inHexNumber
= false;
484 bool seenDot
= sc
.ch
== '.';
485 sc
.SetState(SCE_T3_NUMBER
);
489 if (sc
.chPrev
== '0' && tolower(sc
.ch
) == 'x') {
495 if (!IsAHexDigit(sc
.ch
)) {
498 } else if (!isdigit(sc
.ch
)) {
499 if (!seenE
&& tolower(sc
.ch
) == 'e') {
502 if (sc
.chNext
== '+' || sc
.chNext
== '-') {
505 } else if (!seenDot
&& sc
.ch
== '.') {
513 sc
.SetState(endState
);
516 static void ColouriseTADS3Doc(unsigned int startPos
, int length
, int initStyle
,
517 WordList
*keywordlists
[], Accessor
&styler
) {
518 int visibleChars
= 0;
519 int bracketLevel
= 0;
521 unsigned int endPos
= startPos
+ length
;
522 int lineCurrent
= styler
.GetLine(startPos
);
523 if (lineCurrent
> 0) {
524 lineState
= styler
.GetLineState(lineCurrent
-1);
526 StyleContext
sc(startPos
, length
, initStyle
, styler
);
530 if (IsEOL(sc
.ch
, sc
.chNext
)) {
531 styler
.SetLineState(lineCurrent
, lineState
);
541 case SCE_T3_PREPROCESSOR
:
542 case SCE_T3_LINE_COMMENT
:
543 ColouriseToEndOfLine(sc
, sc
.state
, lineState
&T3_INT_EXPRESSION
?
544 SCE_T3_X_DEFAULT
: SCE_T3_DEFAULT
);
546 case SCE_T3_S_STRING
:
547 case SCE_T3_D_STRING
:
548 case SCE_T3_X_STRING
:
549 ColouriseTADS3String(sc
, lineState
);
552 case SCE_T3_MSG_PARAM
:
553 ColouriseTADS3MsgParam(sc
, lineState
);
555 case SCE_T3_LIB_DIRECTIVE
:
556 ColouriseTADS3LibDirective(sc
, lineState
);
558 case SCE_T3_HTML_DEFAULT
:
559 ColouriseTADS3HTMLTag(sc
, lineState
);
561 case SCE_T3_HTML_STRING
:
562 ColouriseTADSHTMLString(sc
, lineState
);
564 case SCE_T3_BLOCK_COMMENT
:
565 ColouriseTADS3Comment(sc
, lineState
&T3_INT_EXPRESSION
?
566 SCE_T3_X_DEFAULT
: SCE_T3_DEFAULT
);
569 case SCE_T3_X_DEFAULT
:
570 if (IsASpaceOrTab(sc
.ch
)) {
572 } else if (sc
.ch
== '#' && visibleChars
== 0) {
573 ColouriseToEndOfLine(sc
, SCE_T3_PREPROCESSOR
, sc
.state
);
574 } else if (sc
.Match('/', '*')) {
575 ColouriseTADS3Comment(sc
, sc
.state
);
577 } else if (sc
.Match('/', '/')) {
578 ColouriseToEndOfLine(sc
, SCE_T3_LINE_COMMENT
, sc
.state
);
579 } else if (sc
.ch
== '"') {
581 ColouriseTADS3String(sc
, lineState
);
583 } else if (sc
.ch
== '\'') {
584 ColouriseTADS3String(sc
, lineState
);
586 } else if (sc
.state
== SCE_T3_X_DEFAULT
&& bracketLevel
== 0
587 && sc
.Match('>', '>')) {
589 sc
.SetState(SCE_T3_D_STRING
);
590 if (lineState
& T3_INT_EXPRESSION_IN_TAG
)
591 sc
.SetState(SCE_T3_HTML_STRING
);
592 lineState
&= ~(T3_SINGLE_QUOTE
|T3_INT_EXPRESSION
593 |T3_INT_EXPRESSION_IN_TAG
);
594 } else if (IsATADS3Operator(sc
.ch
)) {
595 if (sc
.state
== SCE_T3_X_DEFAULT
) {
598 } else if (sc
.ch
== ')' && bracketLevel
> 0) {
602 ColouriseTADS3Operator(sc
);
604 } else if (IsANumberStart(sc
)) {
605 ColouriseTADS3Number(sc
);
607 } else if (IsAWordStart(sc
.ch
)) {
608 ColouriseTADS3Keyword(sc
, keywordlists
, endPos
);
610 } else if (sc
.Match("...")) {
611 sc
.SetState(SCE_T3_IDENTIFIER
);
613 sc
.SetState(SCE_T3_DEFAULT
);
620 sc
.SetState(SCE_T3_DEFAULT
);
628 TADS3 has two styles of top level block (TLB). Eg
631 silverKey : Key 'small silver key' 'small silver key'
632 "A small key glints in the sunlight. "
640 "A small key glints in the sunlight. "
643 Some constructs mandate one or the other, but usually the author has may choose
646 T3_SEENSTART is used to indicate that a braceless TLB has been (potentially)
647 seen and is also used to match the closing ';' of the default style.
649 T3_EXPECTINGIDENTIFIER and T3_EXPECTINGPUNCTUATION are used to keep track of
650 what characters may be seen without incrementing the block level. The general
651 pattern is identifier <punc> identifier, acceptable punctuation characters
652 are ':', ',', '(' and ')'. No attempt is made to ensure that punctuation
653 characters are syntactically correct, eg parentheses match. A ')' always
654 signifies the start of a block. We just need to check if it is followed by a
655 '{', in which case we let the brace handling code handle the folding level.
657 expectingIdentifier == false && expectingIdentifier == false
658 Before the start of a TLB.
660 expectingIdentifier == true && expectingIdentifier == true
661 Currently in an identifier. Will accept identifier or punctuation.
663 expectingIdentifier == true && expectingIdentifier == false
664 Just seen a punctuation character & now waiting for an identifier to start.
666 expectingIdentifier == false && expectingIdentifier == truee
667 We were in an identifier and have seen space. Now waiting to see a punctuation
670 Space, comments & preprocessor directives are always acceptable and are
674 static const int T3_SEENSTART
= 1 << 12;
675 static const int T3_EXPECTINGIDENTIFIER
= 1 << 13;
676 static const int T3_EXPECTINGPUNCTUATION
= 1 << 14;
678 static inline bool IsStringTransition(int s1
, int s2
) {
680 && (s1
== SCE_T3_S_STRING
|| s1
== SCE_T3_X_STRING
681 || (s1
== SCE_T3_D_STRING
&& s2
!= SCE_T3_X_DEFAULT
))
682 && s2
!= SCE_T3_LIB_DIRECTIVE
683 && s2
!= SCE_T3_MSG_PARAM
684 && s2
!= SCE_T3_HTML_TAG
685 && s2
!= SCE_T3_HTML_STRING
;
688 static inline bool IsATADS3Punctuation(const int ch
) {
689 return ch
== ':' || ch
== ',' || ch
== '(' || ch
== ')';
692 static inline bool IsAnIdentifier(const int style
) {
693 return style
== SCE_T3_IDENTIFIER
694 || style
== SCE_T3_USER1
695 || style
== SCE_T3_USER2
696 || style
== SCE_T3_USER3
;
699 static inline bool IsAnOperator(const int style
) {
700 return style
== SCE_T3_OPERATOR
|| style
== SCE_T3_BRACE
;
703 static inline bool IsSpaceEquivalent(const int ch
, const int style
) {
705 || style
== SCE_T3_BLOCK_COMMENT
706 || style
== SCE_T3_LINE_COMMENT
707 || style
== SCE_T3_PREPROCESSOR
;
710 static char peekAhead(unsigned int startPos
, unsigned int endPos
,
712 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
713 int style
= styler
.StyleAt(i
);
715 if (!IsSpaceEquivalent(ch
, style
)) {
716 if (IsAnIdentifier(style
)) {
719 if (IsATADS3Punctuation(ch
)) {
731 static void FoldTADS3Doc(unsigned int startPos
, int length
, int initStyle
,
732 WordList
*[], Accessor
&styler
) {
733 unsigned int endPos
= startPos
+ length
;
734 int lineCurrent
= styler
.GetLine(startPos
);
735 int levelCurrent
= SC_FOLDLEVELBASE
;
737 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
738 int seenStart
= levelCurrent
& T3_SEENSTART
;
739 int expectingIdentifier
= levelCurrent
& T3_EXPECTINGIDENTIFIER
;
740 int expectingPunctuation
= levelCurrent
& T3_EXPECTINGPUNCTUATION
;
741 levelCurrent
&= SC_FOLDLEVELNUMBERMASK
;
742 int levelMinCurrent
= levelCurrent
;
743 int levelNext
= levelCurrent
;
744 char chNext
= styler
[startPos
];
745 int styleNext
= styler
.StyleAt(startPos
);
746 int style
= initStyle
;
748 int stylePrev
= style
;
750 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
756 chNext
= styler
.SafeGetCharAt(i
+ 1);
759 styleNext
= styler
.StyleAt(i
+ 1);
761 bool atEOL
= IsEOL(ch
, chNext
);
763 if (levelNext
== SC_FOLDLEVELBASE
) {
764 if (IsSpaceEquivalent(ch
, style
)) {
765 if (expectingPunctuation
) {
766 expectingIdentifier
= 0;
768 if (style
== SCE_T3_BLOCK_COMMENT
) {
771 } else if (ch
== '{') {
774 } else if (ch
== '\'' || ch
== '"' || ch
== '[') {
779 } else if (ch
== ';') {
781 expectingIdentifier
= 0;
782 expectingPunctuation
= 0;
783 } else if (expectingIdentifier
&& expectingPunctuation
) {
784 if (IsATADS3Punctuation(ch
)) {
785 if (ch
== ')' && peekAhead(i
+1, endPos
, styler
) != '{') {
788 expectingPunctuation
= 0;
790 } else if (!IsAnIdentifier(style
)) {
793 } else if (expectingIdentifier
&& !expectingPunctuation
) {
794 if (!IsAnIdentifier(style
)) {
797 expectingPunctuation
= T3_EXPECTINGPUNCTUATION
;
799 } else if (!expectingIdentifier
&& expectingPunctuation
) {
800 if (!IsATADS3Punctuation(ch
)) {
803 if (ch
== ')' && peekAhead(i
+1, endPos
, styler
) != '{') {
806 expectingIdentifier
= T3_EXPECTINGIDENTIFIER
;
807 expectingPunctuation
= 0;
810 } else if (!expectingIdentifier
&& !expectingPunctuation
) {
811 if (IsAnIdentifier(style
)) {
812 seenStart
= T3_SEENSTART
;
813 expectingIdentifier
= T3_EXPECTINGIDENTIFIER
;
814 expectingPunctuation
= T3_EXPECTINGPUNCTUATION
;
818 if (levelNext
!= SC_FOLDLEVELBASE
&& style
!= SCE_T3_BLOCK_COMMENT
) {
819 expectingIdentifier
= 0;
820 expectingPunctuation
= 0;
823 } else if (levelNext
== SC_FOLDLEVELBASE
+1 && seenStart
824 && ch
== ';' && IsAnOperator(style
)) {
827 } else if (style
== SCE_T3_BLOCK_COMMENT
) {
828 if (stylePrev
!= SCE_T3_BLOCK_COMMENT
) {
830 } else if (styleNext
!= SCE_T3_BLOCK_COMMENT
&& !atEOL
) {
831 // Comments don't end at end of line and the next character may be unstyled.
834 } else if (ch
== '\'' || ch
== '"') {
835 if (IsStringTransition(style
, stylePrev
)) {
836 if (levelMinCurrent
> levelNext
) {
837 levelMinCurrent
= levelNext
;
840 } else if (IsStringTransition(style
, styleNext
)) {
843 } else if (IsAnOperator(style
)) {
844 if (ch
== '{' || ch
== '[') {
845 // Measure the minimum before a '{' to allow
846 // folding on "} else {"
847 if (levelMinCurrent
> levelNext
) {
848 levelMinCurrent
= levelNext
;
851 } else if (ch
== '}' || ch
== ']') {
857 if (seenStart
&& levelNext
== SC_FOLDLEVELBASE
) {
858 switch (peekAhead(i
+1, endPos
, styler
)) {
866 if (expectingPunctuation
) {
871 if (expectingIdentifier
) {
876 if (levelNext
!= SC_FOLDLEVELBASE
) {
877 expectingIdentifier
= 0;
878 expectingPunctuation
= 0;
881 int lev
= levelMinCurrent
| (levelNext
| expectingIdentifier
882 | expectingPunctuation
| seenStart
) << 16;
883 if (levelMinCurrent
< levelNext
)
884 lev
|= SC_FOLDLEVELHEADERFLAG
;
885 if (lev
!= styler
.LevelAt(lineCurrent
)) {
886 styler
.SetLevel(lineCurrent
, lev
);
889 levelCurrent
= levelNext
;
890 levelMinCurrent
= levelCurrent
;
895 static const char * const tads3WordList
[] = {
903 LexerModule
lmTADS3(SCLEX_TADS3
, ColouriseTADS3Doc
, "tads3", FoldTADS3Doc
, tads3WordList
);