Christian Grasser ad79718fc8 Update to scintilla 5.5.2 & Lexilla 5.4.0
Release 5.5.2 ( https://www.scintilla.org/scintilla552.zip )

    Released 21 August 2024.
    Add SCI_SETCOPYSEPARATOR for separator between parts of a multiple selection when copied to the clipboard. Feature #1530.
    Add SCI_GETUNDOSEQUENCE to determine whether an undo sequence is active and its nesting depth.
    Add SCI_STYLESETSTRETCH to support condensed and expanded text styles.
    Add SCI_LINEINDENT and SCI_LINEDEDENT. Feature #1524.
    Fix bug on Cocoa where double-click stopped working when system had been running for a long time.
    On Cocoa implement more values of font weight and stretch.

Release 5.4.0 ( https://www.scintilla.org/lexilla540.zip )

    Released 21 August 2024.
    Inside Lexilla, LexerModule instances are now const. This will require changes to applications that modify Lexilla.cxx, which may be done to add custom lexers.
    Lexer added for TOML "toml".
    Bash: Handle backslash in heredoc delimiter. Issue #257.
    Progress: Fix lexing of nested comments. Pull request #258.
    Force lower-casing of case-insensitive keyword lists so keywords match in some lexers. Issue #259.

Close #15564
2024-08-23 02:59:58 +02:00

905 lines
37 KiB
C++

// Scintilla source code edit control
/** @file LexTADS3.cxx
** Lexer for TADS3.
**/
// Copyright 1998-2006 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
/*
* TADS3 is a language designed by Michael J. Roberts for the writing of text
* based games. TADS comes from Text Adventure Development System. It has good
* support for the processing and outputting of formatted text and much of a
* TADS program listing consists of strings.
*
* TADS has two types of strings, those enclosed in single quotes (') and those
* enclosed in double quotes ("). These strings have different symantics and
* can be given different highlighting if desired.
*
* There can be embedded within both types of strings html tags
* ( <tag key=value> ), library directives ( <.directive> ), and message
* parameters ( {The doctor's/his} ).
*
* Double quoted strings can also contain interpolated expressions
* ( << rug.moved ? ' and a hole in the floor. ' : nil >> ). These expressions
* may themselves contain single or double quoted strings, although the double
* quoted strings may not contain interpolated expressions.
*
* These embedded constructs influence the output and formatting and are an
* important part of a program and require highlighting.
*
* LINKS
* http://www.tads.org/
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>
#include <string>
#include <string_view>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "CharacterSet.h"
#include "LexerModule.h"
using namespace Lexilla;
static const int T3_SINGLE_QUOTE = 1;
static const int T3_INT_EXPRESSION = 2;
static const int T3_INT_EXPRESSION_IN_TAG = 4;
static const int T3_HTML_SQUOTE = 8;
static inline bool IsEOL(const int ch, const int chNext) {
return (ch == '\r' && chNext != '\n') || (ch == '\n');
}
/*
* Test the current character to see if it's the START of an EOL sequence;
* if so, skip ahead to the last character of the sequence and return true,
* and if not just return false. There are a few places where we want to
* check to see if a newline sequence occurs at a particular point, but
* where a caller expects a subroutine to stop only upon reaching the END
* of a newline sequence (in particular, CR-LF on Windows). That's why
* IsEOL() above only returns true on CR if the CR isn't followed by an LF
* - it doesn't want to admit that there's a newline until reaching the END
* of the sequence. We meet both needs by saying that there's a newline
* when we see the CR in a CR-LF, but skipping the CR before returning so
* that the caller's caller will see that we've stopped at the LF.
*/
static inline bool IsEOLSkip(StyleContext &sc)
{
/* test for CR-LF */
if (sc.ch == '\r' && sc.chNext == '\n')
{
/* got CR-LF - skip the CR and indicate that we're at a newline */
sc.Forward();
return true;
}
/*
* in other cases, we have at most a 1-character newline, so do the
* normal IsEOL test
*/
return IsEOL(sc.ch, sc.chNext);
}
static inline bool IsATADS3Operator(const int ch) {
return ch == '=' || ch == '{' || ch == '}' || ch == '(' || ch == ')'
|| ch == '[' || ch == ']' || ch == ',' || ch == ':' || ch == ';'
|| ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%'
|| ch == '?' || ch == '!' || ch == '<' || ch == '>' || ch == '|'
|| ch == '@' || ch == '&' || ch == '~';
}
static inline bool IsAWordChar(const int ch) {
return isalnum(ch) || ch == '_';
}
static inline bool IsAWordStart(const int ch) {
return isalpha(ch) || ch == '_';
}
static inline bool IsAHexDigit(const int ch) {
int lch = tolower(ch);
return isdigit(lch) || lch == 'a' || lch == 'b' || lch == 'c'
|| lch == 'd' || lch == 'e' || lch == 'f';
}
static inline bool IsAnHTMLChar(int ch) {
return isalnum(ch) || ch == '-' || ch == '_' || ch == '.';
}
static inline bool IsADirectiveChar(int ch) {
return isalnum(ch) || isspace(ch) || ch == '-' || ch == '/';
}
static inline bool IsANumberStart(StyleContext &sc) {
return isdigit(sc.ch)
|| (!isdigit(sc.chPrev) && sc.ch == '.' && isdigit(sc.chNext));
}
inline static void ColouriseTADS3Operator(StyleContext &sc) {
int initState = sc.state;
int c = sc.ch;
sc.SetState(c == '{' || c == '}' ? SCE_T3_BRACE : SCE_T3_OPERATOR);
sc.ForwardSetState(initState);
}
static void ColouriseTADSHTMLString(StyleContext &sc, int &lineState) {
int endState = sc.state;
int chQuote = sc.ch;
int chString = (lineState & T3_SINGLE_QUOTE) ? '\'' : '"';
if (endState == SCE_T3_HTML_STRING) {
if (lineState&T3_SINGLE_QUOTE) {
endState = SCE_T3_S_STRING;
chString = '\'';
} else if (lineState&T3_INT_EXPRESSION) {
endState = SCE_T3_X_STRING;
chString = '"';
} else {
endState = SCE_T3_HTML_DEFAULT;
chString = '"';
}
chQuote = (lineState & T3_HTML_SQUOTE) ? '\'' : '"';
} else {
sc.SetState(SCE_T3_HTML_STRING);
sc.Forward();
}
if (chQuote == '"')
lineState &= ~T3_HTML_SQUOTE;
else
lineState |= T3_HTML_SQUOTE;
while (sc.More()) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.ch == chQuote) {
sc.ForwardSetState(endState);
return;
}
if (sc.Match('\\', static_cast<char>(chQuote))) {
sc.Forward(2);
sc.SetState(endState);
return;
}
if (sc.ch == chString) {
sc.SetState(SCE_T3_DEFAULT);
return;
}
if (sc.Match('<', '<')) {
lineState |= T3_INT_EXPRESSION | T3_INT_EXPRESSION_IN_TAG;
sc.SetState(SCE_T3_X_DEFAULT);
sc.Forward(2);
return;
}
if (sc.Match('\\', static_cast<char>(chQuote))
|| sc.Match('\\', static_cast<char>(chString))
|| sc.Match('\\', '\\')) {
sc.Forward(2);
} else {
sc.Forward();
}
}
}
static void ColouriseTADS3HTMLTagStart(StyleContext &sc) {
sc.SetState(SCE_T3_HTML_TAG);
sc.Forward();
if (sc.ch == '/') {
sc.Forward();
}
while (IsAnHTMLChar(sc.ch)) {
sc.Forward();
}
}
static void ColouriseTADS3HTMLTag(StyleContext &sc, int &lineState) {
int endState = sc.state;
int chQuote = '"';
int chString = '\'';
switch (endState) {
case SCE_T3_S_STRING:
ColouriseTADS3HTMLTagStart(sc);
sc.SetState(SCE_T3_HTML_DEFAULT);
chQuote = '\'';
chString = '"';
break;
case SCE_T3_D_STRING:
case SCE_T3_X_STRING:
ColouriseTADS3HTMLTagStart(sc);
sc.SetState(SCE_T3_HTML_DEFAULT);
break;
case SCE_T3_HTML_DEFAULT:
if (lineState&T3_SINGLE_QUOTE) {
endState = SCE_T3_S_STRING;
chQuote = '\'';
chString = '"';
} else if (lineState&T3_INT_EXPRESSION) {
endState = SCE_T3_X_STRING;
} else {
endState = SCE_T3_D_STRING;
}
break;
}
while (sc.More()) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.Match('/', '>')) {
sc.SetState(SCE_T3_HTML_TAG);
sc.Forward(2);
sc.SetState(endState);
return;
}
if (sc.ch == '>') {
sc.SetState(SCE_T3_HTML_TAG);
sc.ForwardSetState(endState);
return;
}
if (sc.ch == chQuote) {
sc.SetState(endState);
return;
}
if (sc.Match('\\', static_cast<char>(chQuote))) {
sc.Forward();
ColouriseTADSHTMLString(sc, lineState);
if (sc.state == SCE_T3_X_DEFAULT)
break;
} else if (sc.ch == chString) {
ColouriseTADSHTMLString(sc, lineState);
} else if (sc.ch == '=') {
ColouriseTADS3Operator(sc);
} else {
sc.Forward();
}
}
}
static void ColouriseTADS3Keyword(StyleContext &sc,
WordList *keywordlists[], Sci_PositionU endPos) {
char s[250];
WordList &keywords = *keywordlists[0];
WordList &userwords1 = *keywordlists[1];
WordList &userwords2 = *keywordlists[2];
WordList &userwords3 = *keywordlists[3];
int initState = sc.state;
sc.SetState(SCE_T3_IDENTIFIER);
while (sc.More() && (IsAWordChar(sc.ch))) {
sc.Forward();
}
sc.GetCurrent(s, sizeof(s));
if ( strcmp(s, "is") == 0 || strcmp(s, "not") == 0) {
// have to find if "in" is next
Sci_Position n = 1;
while (n + sc.currentPos < endPos && IsASpaceOrTab(sc.GetRelative(n)))
n++;
if (sc.GetRelative(n) == 'i' && sc.GetRelative(n+1) == 'n') {
sc.Forward(n+2);
sc.ChangeState(SCE_T3_KEYWORD);
}
} else if (keywords.InList(s)) {
sc.ChangeState(SCE_T3_KEYWORD);
} else if (userwords3.InList(s)) {
sc.ChangeState(SCE_T3_USER3);
} else if (userwords2.InList(s)) {
sc.ChangeState(SCE_T3_USER2);
} else if (userwords1.InList(s)) {
sc.ChangeState(SCE_T3_USER1);
}
sc.SetState(initState);
}
static void ColouriseTADS3MsgParam(StyleContext &sc, int &lineState) {
int endState = sc.state;
int chQuote = '"';
switch (endState) {
case SCE_T3_S_STRING:
sc.SetState(SCE_T3_MSG_PARAM);
sc.Forward();
chQuote = '\'';
break;
case SCE_T3_D_STRING:
case SCE_T3_X_STRING:
sc.SetState(SCE_T3_MSG_PARAM);
sc.Forward();
break;
case SCE_T3_MSG_PARAM:
if (lineState&T3_SINGLE_QUOTE) {
endState = SCE_T3_S_STRING;
chQuote = '\'';
} else if (lineState&T3_INT_EXPRESSION) {
endState = SCE_T3_X_STRING;
} else {
endState = SCE_T3_D_STRING;
}
break;
}
while (sc.More() && sc.ch != '}' && sc.ch != chQuote) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.ch == '\\') {
sc.Forward();
}
sc.Forward();
}
if (sc.ch == chQuote) {
sc.SetState(endState);
} else {
sc.ForwardSetState(endState);
}
}
static void ColouriseTADS3LibDirective(StyleContext &sc, int &lineState) {
int initState = sc.state;
int chQuote = '"';
switch (initState) {
case SCE_T3_S_STRING:
sc.SetState(SCE_T3_LIB_DIRECTIVE);
sc.Forward(2);
chQuote = '\'';
break;
case SCE_T3_D_STRING:
sc.SetState(SCE_T3_LIB_DIRECTIVE);
sc.Forward(2);
break;
case SCE_T3_LIB_DIRECTIVE:
if (lineState&T3_SINGLE_QUOTE) {
initState = SCE_T3_S_STRING;
chQuote = '\'';
} else {
initState = SCE_T3_D_STRING;
}
break;
}
while (sc.More() && IsADirectiveChar(sc.ch)) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
sc.Forward();
};
if (sc.ch == '>' || !sc.More()) {
sc.ForwardSetState(initState);
} else if (sc.ch == chQuote) {
sc.SetState(initState);
} else {
sc.ChangeState(initState);
sc.Forward();
}
}
static void ColouriseTADS3String(StyleContext &sc, int &lineState) {
int chQuote = sc.ch;
int endState = sc.state;
switch (sc.state) {
case SCE_T3_DEFAULT:
case SCE_T3_X_DEFAULT:
if (chQuote == '"') {
if (sc.state == SCE_T3_DEFAULT) {
sc.SetState(SCE_T3_D_STRING);
} else {
sc.SetState(SCE_T3_X_STRING);
}
lineState &= ~T3_SINGLE_QUOTE;
} else {
sc.SetState(SCE_T3_S_STRING);
lineState |= T3_SINGLE_QUOTE;
}
sc.Forward();
break;
case SCE_T3_S_STRING:
chQuote = '\'';
endState = lineState&T3_INT_EXPRESSION ?
SCE_T3_X_DEFAULT : SCE_T3_DEFAULT;
break;
case SCE_T3_D_STRING:
chQuote = '"';
endState = SCE_T3_DEFAULT;
break;
case SCE_T3_X_STRING:
chQuote = '"';
endState = SCE_T3_X_DEFAULT;
break;
}
while (sc.More()) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.ch == chQuote) {
sc.ForwardSetState(endState);
return;
}
if (sc.state == SCE_T3_D_STRING && sc.Match('<', '<')) {
lineState |= T3_INT_EXPRESSION;
sc.SetState(SCE_T3_X_DEFAULT);
sc.Forward(2);
return;
}
if (sc.Match('\\', static_cast<char>(chQuote))
|| sc.Match('\\', '\\')) {
sc.Forward(2);
} else if (sc.ch == '{') {
ColouriseTADS3MsgParam(sc, lineState);
} else if (sc.Match('<', '.')) {
ColouriseTADS3LibDirective(sc, lineState);
} else if (sc.ch == '<') {
ColouriseTADS3HTMLTag(sc, lineState);
if (sc.state == SCE_T3_X_DEFAULT)
return;
} else {
sc.Forward();
}
}
}
static void ColouriseTADS3Comment(StyleContext &sc, int endState) {
sc.SetState(SCE_T3_BLOCK_COMMENT);
while (sc.More()) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.Match('*', '/')) {
sc.Forward(2);
sc.SetState(endState);
return;
}
sc.Forward();
}
}
static void ColouriseToEndOfLine(StyleContext &sc, int initState, int endState) {
sc.SetState(initState);
while (sc.More()) {
if (sc.ch == '\\') {
sc.Forward();
if (IsEOLSkip(sc)) {
return;
}
}
if (IsEOL(sc.ch, sc.chNext)) {
sc.SetState(endState);
return;
}
sc.Forward();
}
}
static void ColouriseTADS3Number(StyleContext &sc) {
int endState = sc.state;
bool inHexNumber = false;
bool seenE = false;
bool seenDot = sc.ch == '.';
sc.SetState(SCE_T3_NUMBER);
if (sc.More()) {
sc.Forward();
}
if (sc.chPrev == '0' && tolower(sc.ch) == 'x') {
inHexNumber = true;
sc.Forward();
}
while (sc.More()) {
if (inHexNumber) {
if (!IsAHexDigit(sc.ch)) {
break;
}
} else if (!isdigit(sc.ch)) {
if (!seenE && tolower(sc.ch) == 'e') {
seenE = true;
seenDot = true;
if (sc.chNext == '+' || sc.chNext == '-') {
sc.Forward();
}
} else if (!seenDot && sc.ch == '.') {
seenDot = true;
} else {
break;
}
}
sc.Forward();
}
sc.SetState(endState);
}
static void ColouriseTADS3Doc(Sci_PositionU startPos, Sci_Position length, int initStyle,
WordList *keywordlists[], Accessor &styler) {
int visibleChars = 0;
int bracketLevel = 0;
int lineState = 0;
Sci_PositionU endPos = startPos + length;
Sci_Position lineCurrent = styler.GetLine(startPos);
if (lineCurrent > 0) {
lineState = styler.GetLineState(lineCurrent-1);
}
StyleContext sc(startPos, length, initStyle, styler);
while (sc.More()) {
if (IsEOL(sc.ch, sc.chNext)) {
styler.SetLineState(lineCurrent, lineState);
lineCurrent++;
visibleChars = 0;
sc.Forward();
if (sc.ch == '\n') {
sc.Forward();
}
}
switch(sc.state) {
case SCE_T3_PREPROCESSOR:
case SCE_T3_LINE_COMMENT:
ColouriseToEndOfLine(sc, sc.state, lineState&T3_INT_EXPRESSION ?
SCE_T3_X_DEFAULT : SCE_T3_DEFAULT);
break;
case SCE_T3_S_STRING:
case SCE_T3_D_STRING:
case SCE_T3_X_STRING:
ColouriseTADS3String(sc, lineState);
visibleChars++;
break;
case SCE_T3_MSG_PARAM:
ColouriseTADS3MsgParam(sc, lineState);
break;
case SCE_T3_LIB_DIRECTIVE:
ColouriseTADS3LibDirective(sc, lineState);
break;
case SCE_T3_HTML_DEFAULT:
ColouriseTADS3HTMLTag(sc, lineState);
break;
case SCE_T3_HTML_STRING:
ColouriseTADSHTMLString(sc, lineState);
break;
case SCE_T3_BLOCK_COMMENT:
ColouriseTADS3Comment(sc, lineState&T3_INT_EXPRESSION ?
SCE_T3_X_DEFAULT : SCE_T3_DEFAULT);
break;
case SCE_T3_DEFAULT:
case SCE_T3_X_DEFAULT:
if (IsASpaceOrTab(sc.ch)) {
sc.Forward();
} else if (sc.ch == '#' && visibleChars == 0) {
ColouriseToEndOfLine(sc, SCE_T3_PREPROCESSOR, sc.state);
} else if (sc.Match('/', '*')) {
ColouriseTADS3Comment(sc, sc.state);
visibleChars++;
} else if (sc.Match('/', '/')) {
ColouriseToEndOfLine(sc, SCE_T3_LINE_COMMENT, sc.state);
} else if (sc.ch == '"') {
bracketLevel = 0;
ColouriseTADS3String(sc, lineState);
visibleChars++;
} else if (sc.ch == '\'') {
ColouriseTADS3String(sc, lineState);
visibleChars++;
} else if (sc.state == SCE_T3_X_DEFAULT && bracketLevel == 0
&& sc.Match('>', '>')) {
sc.Forward(2);
sc.SetState(SCE_T3_D_STRING);
if (lineState & T3_INT_EXPRESSION_IN_TAG)
sc.SetState(SCE_T3_HTML_STRING);
lineState &= ~(T3_SINGLE_QUOTE|T3_INT_EXPRESSION
|T3_INT_EXPRESSION_IN_TAG);
} else if (IsATADS3Operator(sc.ch)) {
if (sc.state == SCE_T3_X_DEFAULT) {
if (sc.ch == '(') {
bracketLevel++;
} else if (sc.ch == ')' && bracketLevel > 0) {
bracketLevel--;
}
}
ColouriseTADS3Operator(sc);
visibleChars++;
} else if (IsANumberStart(sc)) {
ColouriseTADS3Number(sc);
visibleChars++;
} else if (IsAWordStart(sc.ch)) {
ColouriseTADS3Keyword(sc, keywordlists, endPos);
visibleChars++;
} else if (sc.Match("...")) {
sc.SetState(SCE_T3_IDENTIFIER);
sc.Forward(3);
sc.SetState(SCE_T3_DEFAULT);
} else {
sc.Forward();
visibleChars++;
}
break;
default:
sc.SetState(SCE_T3_DEFAULT);
sc.Forward();
}
}
sc.Complete();
}
/*
TADS3 has two styles of top level block (TLB). Eg
// default style
silverKey : Key 'small silver key' 'small silver key'
"A small key glints in the sunlight. "
;
and
silverKey : Key {
'small silver key'
'small silver key'
"A small key glints in the sunlight. "
}
Some constructs mandate one or the other, but usually the author has may choose
either.
T3_SEENSTART is used to indicate that a braceless TLB has been (potentially)
seen and is also used to match the closing ';' of the default style.
T3_EXPECTINGIDENTIFIER and T3_EXPECTINGPUNCTUATION are used to keep track of
what characters may be seen without incrementing the block level. The general
pattern is identifier <punc> identifier, acceptable punctuation characters
are ':', ',', '(' and ')'. No attempt is made to ensure that punctuation
characters are syntactically correct, eg parentheses match. A ')' always
signifies the start of a block. We just need to check if it is followed by a
'{', in which case we let the brace handling code handle the folding level.
expectingIdentifier == false && expectingIdentifier == false
Before the start of a TLB.
expectingIdentifier == true && expectingIdentifier == true
Currently in an identifier. Will accept identifier or punctuation.
expectingIdentifier == true && expectingIdentifier == false
Just seen a punctuation character & now waiting for an identifier to start.
expectingIdentifier == false && expectingIdentifier == truee
We were in an identifier and have seen space. Now waiting to see a punctuation
character
Space, comments & preprocessor directives are always acceptable and are
equivalent.
*/
static const int T3_SEENSTART = 1 << 12;
static const int T3_EXPECTINGIDENTIFIER = 1 << 13;
static const int T3_EXPECTINGPUNCTUATION = 1 << 14;
static inline bool IsStringTransition(int s1, int s2) {
return s1 != s2
&& (s1 == SCE_T3_S_STRING || s1 == SCE_T3_X_STRING
|| (s1 == SCE_T3_D_STRING && s2 != SCE_T3_X_DEFAULT))
&& s2 != SCE_T3_LIB_DIRECTIVE
&& s2 != SCE_T3_MSG_PARAM
&& s2 != SCE_T3_HTML_TAG
&& s2 != SCE_T3_HTML_STRING;
}
static inline bool IsATADS3Punctuation(const int ch) {
return ch == ':' || ch == ',' || ch == '(' || ch == ')';
}
static inline bool IsAnIdentifier(const int style) {
return style == SCE_T3_IDENTIFIER
|| style == SCE_T3_USER1
|| style == SCE_T3_USER2
|| style == SCE_T3_USER3;
}
static inline bool IsAnOperator(const int style) {
return style == SCE_T3_OPERATOR || style == SCE_T3_BRACE;
}
static inline bool IsSpaceEquivalent(const int ch, const int style) {
return isspace(ch)
|| style == SCE_T3_BLOCK_COMMENT
|| style == SCE_T3_LINE_COMMENT
|| style == SCE_T3_PREPROCESSOR;
}
static char peekAhead(Sci_PositionU startPos, Sci_PositionU endPos,
Accessor &styler) {
for (Sci_PositionU i = startPos; i < endPos; i++) {
int style = styler.StyleAt(i);
char ch = styler[i];
if (!IsSpaceEquivalent(ch, style)) {
if (IsAnIdentifier(style)) {
return 'a';
}
if (IsATADS3Punctuation(ch)) {
return ':';
}
if (ch == '{') {
return '{';
}
return '*';
}
}
return ' ';
}
static void FoldTADS3Doc(Sci_PositionU startPos, Sci_Position length, int initStyle,
WordList *[], Accessor &styler) {
Sci_PositionU endPos = startPos + length;
Sci_Position lineCurrent = styler.GetLine(startPos);
int levelCurrent = SC_FOLDLEVELBASE;
if (lineCurrent > 0)
levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
int seenStart = levelCurrent & T3_SEENSTART;
int expectingIdentifier = levelCurrent & T3_EXPECTINGIDENTIFIER;
int expectingPunctuation = levelCurrent & T3_EXPECTINGPUNCTUATION;
levelCurrent &= SC_FOLDLEVELNUMBERMASK;
int levelMinCurrent = levelCurrent;
int levelNext = levelCurrent;
char chNext = styler[startPos];
int styleNext = styler.StyleAt(startPos);
int style = initStyle;
char ch = chNext;
int stylePrev = style;
bool redo = false;
for (Sci_PositionU i = startPos; i < endPos; i++) {
if (redo) {
redo = false;
i--;
} else {
ch = chNext;
chNext = styler.SafeGetCharAt(i + 1);
stylePrev = style;
style = styleNext;
styleNext = styler.StyleAt(i + 1);
}
bool atEOL = IsEOL(ch, chNext);
if (levelNext == SC_FOLDLEVELBASE) {
if (IsSpaceEquivalent(ch, style)) {
if (expectingPunctuation) {
expectingIdentifier = 0;
}
if (style == SCE_T3_BLOCK_COMMENT) {
levelNext++;
}
} else if (ch == '{') {
levelNext++;
seenStart = 0;
} else if (ch == '\'' || ch == '"' || ch == '[') {
levelNext++;
if (seenStart) {
redo = true;
}
} else if (ch == ';') {
seenStart = 0;
expectingIdentifier = 0;
expectingPunctuation = 0;
} else if (expectingIdentifier && expectingPunctuation) {
if (IsATADS3Punctuation(ch)) {
if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') {
levelNext++;
} else {
expectingPunctuation = 0;
}
} else if (!IsAnIdentifier(style)) {
levelNext++;
}
} else if (expectingIdentifier && !expectingPunctuation) {
if (!IsAnIdentifier(style)) {
levelNext++;
} else {
expectingPunctuation = T3_EXPECTINGPUNCTUATION;
}
} else if (!expectingIdentifier && expectingPunctuation) {
if (!IsATADS3Punctuation(ch)) {
levelNext++;
} else {
if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') {
levelNext++;
} else {
expectingIdentifier = T3_EXPECTINGIDENTIFIER;
expectingPunctuation = 0;
}
}
} else if (!expectingIdentifier && !expectingPunctuation) {
if (IsAnIdentifier(style)) {
seenStart = T3_SEENSTART;
expectingIdentifier = T3_EXPECTINGIDENTIFIER;
expectingPunctuation = T3_EXPECTINGPUNCTUATION;
}
}
if (levelNext != SC_FOLDLEVELBASE && style != SCE_T3_BLOCK_COMMENT) {
expectingIdentifier = 0;
expectingPunctuation = 0;
}
} else if (levelNext == SC_FOLDLEVELBASE+1 && seenStart
&& ch == ';' && IsAnOperator(style)) {
levelNext--;
seenStart = 0;
} else if (style == SCE_T3_BLOCK_COMMENT) {
if (stylePrev != SCE_T3_BLOCK_COMMENT) {
levelNext++;
} else if (styleNext != SCE_T3_BLOCK_COMMENT && !atEOL) {
// Comments don't end at end of line and the next character may be unstyled.
levelNext--;
}
} else if (ch == '\'' || ch == '"') {
if (IsStringTransition(style, stylePrev)) {
if (levelMinCurrent > levelNext) {
levelMinCurrent = levelNext;
}
levelNext++;
} else if (IsStringTransition(style, styleNext)) {
levelNext--;
}
} else if (IsAnOperator(style)) {
if (ch == '{' || ch == '[') {
// Measure the minimum before a '{' to allow
// folding on "} else {"
if (levelMinCurrent > levelNext) {
levelMinCurrent = levelNext;
}
levelNext++;
} else if (ch == '}' || ch == ']') {
levelNext--;
}
}
if (atEOL) {
if (seenStart && levelNext == SC_FOLDLEVELBASE) {
switch (peekAhead(i+1, endPos, styler)) {
case ' ':
case '{':
break;
case '*':
levelNext++;
break;
case 'a':
if (expectingPunctuation) {
levelNext++;
}
break;
case ':':
if (expectingIdentifier) {
levelNext++;
}
break;
}
if (levelNext != SC_FOLDLEVELBASE) {
expectingIdentifier = 0;
expectingPunctuation = 0;
}
}
int lev = levelMinCurrent | (levelNext | expectingIdentifier
| expectingPunctuation | seenStart) << 16;
if (levelMinCurrent < levelNext)
lev |= SC_FOLDLEVELHEADERFLAG;
if (lev != styler.LevelAt(lineCurrent)) {
styler.SetLevel(lineCurrent, lev);
}
lineCurrent++;
levelCurrent = levelNext;
levelMinCurrent = levelCurrent;
}
}
}
static const char * const tads3WordList[] = {
"TADS3 Keywords",
"User defined 1",
"User defined 2",
"User defined 3",
0
};
extern const LexerModule lmTADS3(SCLEX_TADS3, ColouriseTADS3Doc, "tads3", FoldTADS3Doc, tads3WordList);