You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
695 lines
26 KiB
695 lines
26 KiB
// Scintilla source code edit control |
|
/** @file LexVisualProlog.cxx |
|
** Lexer for Visual Prolog. |
|
**/ |
|
// Author Thomas Linder Puls, PDC A/S, http://www.visual-prolog.com |
|
// Based on Lexer for C++, C, Java, and JavaScript. |
|
// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org> |
|
// The License.txt file describes the conditions under which this software may be distributed. |
|
|
|
// The line state contains: |
|
// In SCE_VISUALPROLOG_STRING: The closing quote and information about verbatim string. |
|
// and a stack of nesting kinds: comment, embedded (syntax) and (syntax) place holder |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdio.h> |
|
#include <stdarg.h> |
|
#include <assert.h> |
|
#include <ctype.h> |
|
|
|
#ifdef _MSC_VER |
|
#pragma warning(disable: 4786) |
|
#endif |
|
|
|
#include <string> |
|
#include <string_view> |
|
#include <vector> |
|
#include <map> |
|
#include <algorithm> |
|
#include <functional> |
|
|
|
#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 "CharacterCategory.h" |
|
#include "LexerModule.h" |
|
#include "OptionSet.h" |
|
#include "DefaultLexer.h" |
|
|
|
using namespace Scintilla; |
|
using namespace Lexilla; |
|
|
|
namespace { |
|
// Options used for LexerVisualProlog |
|
struct OptionsVisualProlog { |
|
bool verbatimStrings; |
|
bool backQuotedStrings; |
|
OptionsVisualProlog() { |
|
verbatimStrings = true; |
|
backQuotedStrings = false; |
|
} |
|
}; |
|
|
|
static const char* const visualPrologWordLists[] = { |
|
"Major keywords (class, predicates, ...)", |
|
"Minor keywords (if, then, try, ...)", |
|
"Directive keywords without the '#' (include, requires, ...)", |
|
"Documentation keywords without the '@' (short, detail, ...)", |
|
0, |
|
}; |
|
|
|
struct OptionSetVisualProlog : public OptionSet<OptionsVisualProlog> { |
|
OptionSetVisualProlog() { |
|
DefineProperty("lexer.visualprolog.verbatim.strings", &OptionsVisualProlog::verbatimStrings, |
|
"Set to 0 to disable highlighting verbatim strings using '@'."); |
|
DefineProperty("lexer.visualprolog.backquoted.strings", &OptionsVisualProlog::backQuotedStrings, |
|
"Set to 1 to enable using back quotes (``) to delimit strings."); |
|
DefineWordListSets(visualPrologWordLists); |
|
} |
|
}; |
|
|
|
LexicalClass lexicalClasses[] = { |
|
SCE_VISUALPROLOG_DEFAULT, "SCE_VISUALPROLOG_DEFAULT", "default", "Default style", |
|
SCE_VISUALPROLOG_KEY_MAJOR, "SCE_VISUALPROLOG_KEY_MAJOR", "keyword major", "Major keyword", |
|
SCE_VISUALPROLOG_KEY_MINOR, "SCE_VISUALPROLOG_KEY_MINOR", "keyword minor", "Minor keyword", |
|
SCE_VISUALPROLOG_KEY_DIRECTIVE, "SCE_VISUALPROLOG_KEY_DIRECTIVE", "keyword preprocessor", "Directove keyword", |
|
SCE_VISUALPROLOG_COMMENT_BLOCK, "SCE_VISUALPROLOG_COMMENT_BLOCK", "comment", "Multiline comment /* */", |
|
SCE_VISUALPROLOG_COMMENT_LINE, "SCE_VISUALPROLOG_COMMENT_LINE", "comment line", "Line comment % ...", |
|
SCE_VISUALPROLOG_COMMENT_KEY, "SCE_VISUALPROLOG_COMMENT_KEY", "comment documentation keyword", "Doc keyword in comment % @short ...", |
|
SCE_VISUALPROLOG_COMMENT_KEY_ERROR, "SCE_VISUALPROLOG_COMMENT_KEY_ERROR", "comment", "A non recognized doc keyword % @qqq ...", |
|
SCE_VISUALPROLOG_IDENTIFIER, "SCE_VISUALPROLOG_IDENTIFIER", "identifier", "Identifier (black)", |
|
SCE_VISUALPROLOG_VARIABLE, "SCE_VISUALPROLOG_VARIABLE", "variable identifier", "Variable (green)", |
|
SCE_VISUALPROLOG_ANONYMOUS, "SCE_VISUALPROLOG_ANONYMOUS", "variable anonymous identifier", "Anonymous Variable _XXX (dimmed green)", |
|
SCE_VISUALPROLOG_NUMBER, "SCE_VISUALPROLOG_NUMBER", "numeric", "Number", |
|
SCE_VISUALPROLOG_OPERATOR, "SCE_VISUALPROLOG_OPERATOR", "operator", "Operator", |
|
SCE_VISUALPROLOG_STRING, "SCE_VISUALPROLOG_STRING", "literal string", "String literal", |
|
SCE_VISUALPROLOG_STRING_QUOTE, "SCE_VISUALPROLOG_STRING_QUOTE", "literal string quote", "Quotes surrounding string literals", |
|
SCE_VISUALPROLOG_STRING_ESCAPE, "SCE_VISUALPROLOG_STRING_ESCAPE", "literal string escapesequence", "Escape sequence in string literal", |
|
SCE_VISUALPROLOG_STRING_ESCAPE_ERROR, "SCE_VISUALPROLOG_STRING_ESCAPE_ERROR", "error literal string escapesequence", "Error in escape sequence in string literal", |
|
SCE_VISUALPROLOG_STRING_EOL, "SCE_VISUALPROLOG_STRING_EOL", "literal string multiline raw escapesequence", "Verbatim/multiline string literal EOL", |
|
SCE_VISUALPROLOG_EMBEDDED, "SCE_VISUALPROLOG_EMBEDDED", "literal string embedded", "Embedded syntax [| ... |]", |
|
SCE_VISUALPROLOG_PLACEHOLDER, "SCE_VISUALPROLOG_PLACEHOLDER", "operator embedded", "Syntax place holder {| ... |}:ident in embedded syntax" |
|
}; |
|
|
|
LexicalClass getLexicalClass(int style) { |
|
for (auto lc : lexicalClasses) { |
|
if (style == lc.value) { |
|
return lc; |
|
} |
|
} |
|
return {style, "", "unused", ""}; |
|
} |
|
|
|
|
|
class LexerVisualProlog : public DefaultLexer { |
|
WordList majorKeywords; |
|
WordList minorKeywords; |
|
WordList directiveKeywords; |
|
WordList docKeywords; |
|
OptionsVisualProlog options; |
|
OptionSetVisualProlog osVisualProlog; |
|
public: |
|
LexerVisualProlog() : DefaultLexer("visualprolog", SCLEX_VISUALPROLOG) { |
|
} |
|
virtual ~LexerVisualProlog() { |
|
} |
|
void SCI_METHOD Release() override { |
|
delete this; |
|
} |
|
int SCI_METHOD Version() const override { |
|
return lvRelease5; |
|
} |
|
const char* SCI_METHOD PropertyNames() override { |
|
return osVisualProlog.PropertyNames(); |
|
} |
|
int SCI_METHOD PropertyType(const char* name) override { |
|
return osVisualProlog.PropertyType(name); |
|
} |
|
const char* SCI_METHOD DescribeProperty(const char* name) override { |
|
return osVisualProlog.DescribeProperty(name); |
|
} |
|
Sci_Position SCI_METHOD PropertySet(const char* key, const char* val) override; |
|
const char* SCI_METHOD PropertyGet(const char* key) override { |
|
return osVisualProlog.PropertyGet(key); |
|
} |
|
const char* SCI_METHOD DescribeWordListSets() override { |
|
return osVisualProlog.DescribeWordListSets(); |
|
} |
|
Sci_Position SCI_METHOD WordListSet(int n, const char* wl) override; |
|
void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument* pAccess) override; |
|
void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument* pAccess) override; |
|
|
|
void* SCI_METHOD PrivateCall(int, void*) override { |
|
return 0; |
|
} |
|
|
|
int SCI_METHOD NamedStyles() override { |
|
int namedStyles = 0; |
|
for (auto lc : lexicalClasses) { |
|
if (namedStyles < lc.value) { |
|
namedStyles = lc.value; |
|
} |
|
} |
|
return namedStyles; |
|
} |
|
const char* SCI_METHOD NameOfStyle(int style) override { |
|
return getLexicalClass(style).name; |
|
} |
|
const char* SCI_METHOD TagsOfStyle(int style) override { |
|
return getLexicalClass(style).tags; |
|
} |
|
const char* SCI_METHOD DescriptionOfStyle(int style) override { |
|
return getLexicalClass(style).description; |
|
} |
|
|
|
static ILexer5* LexerFactoryVisualProlog() { |
|
return new LexerVisualProlog(); |
|
} |
|
}; |
|
|
|
Sci_Position SCI_METHOD LexerVisualProlog::PropertySet(const char* key, const char* val) { |
|
if (osVisualProlog.PropertySet(&options, key, val)) { |
|
return 0; |
|
} |
|
return -1; |
|
} |
|
|
|
Sci_Position SCI_METHOD LexerVisualProlog::WordListSet(int n, const char* wl) { |
|
WordList* wordListN = 0; |
|
switch (n) { |
|
case 0: |
|
wordListN = &majorKeywords; |
|
break; |
|
case 1: |
|
wordListN = &minorKeywords; |
|
break; |
|
case 2: |
|
wordListN = &directiveKeywords; |
|
break; |
|
case 3: |
|
wordListN = &docKeywords; |
|
break; |
|
} |
|
Sci_Position firstModification = -1; |
|
if (wordListN) { |
|
WordList wlNew; |
|
wlNew.Set(wl); |
|
if (*wordListN != wlNew) { |
|
wordListN->Set(wl); |
|
firstModification = 0; |
|
} |
|
} |
|
return firstModification; |
|
} |
|
|
|
static bool isLowerLetter(int ch) { |
|
return ccLl == CategoriseCharacter(ch); |
|
} |
|
|
|
static bool isUpperLetter(int ch) { |
|
return ccLu == CategoriseCharacter(ch); |
|
} |
|
|
|
static bool isAlphaNum(int ch) { |
|
CharacterCategory cc = CategoriseCharacter(ch); |
|
return (ccLu == cc || ccLl == cc || ccLt == cc || ccLm == cc || ccLo == cc || ccNd == cc || ccNl == cc || ccNo == cc); |
|
} |
|
|
|
static bool isStringVerbatimOpenClose(int ch) { |
|
CharacterCategory cc = CategoriseCharacter(ch); |
|
return (ccPc <= cc && cc <= ccSo); |
|
} |
|
|
|
static bool isIdChar(int ch) { |
|
return ('_') == ch || isAlphaNum(ch); |
|
} |
|
|
|
// Look ahead to see which colour "end" should have (takes colour after the following keyword) |
|
static void endLookAhead(char s[], LexAccessor& styler, Sci_Position start) { |
|
char ch = styler.SafeGetCharAt(start, '\n'); |
|
while (' ' == ch) { |
|
start++; |
|
ch = styler.SafeGetCharAt(start, '\n'); |
|
} |
|
Sci_Position i = 0; |
|
while (i < 100 && isLowerLetter(ch)) { |
|
s[i] = ch; |
|
i++; |
|
ch = styler.SafeGetCharAt(start + i, '\n'); |
|
} |
|
s[i] = '\0'; |
|
} |
|
|
|
|
|
class lineState { |
|
public: |
|
bool verbatim = false; |
|
int closingQuote = 0; |
|
int kindStack = 0; |
|
|
|
bool isOpenStringVerbatim(int next) { |
|
if (next > 0x7FFF) { |
|
return false; |
|
} |
|
switch (next) { |
|
case L'<': |
|
closingQuote = L'>'; |
|
return true; |
|
case L'>': |
|
closingQuote = L'<'; |
|
return true; |
|
case L'(': |
|
closingQuote = L')'; |
|
return true; |
|
case L')': |
|
closingQuote = L'('; |
|
return true; |
|
case L'[': |
|
closingQuote = L']'; |
|
return true; |
|
case L']': |
|
closingQuote = L'['; |
|
return true; |
|
case L'{': |
|
closingQuote = L'}'; |
|
return true; |
|
case L'}': |
|
closingQuote = L'{'; |
|
return true; |
|
case L'_': |
|
case L'.': |
|
case L',': |
|
case L';': |
|
return false; |
|
default: |
|
if (isStringVerbatimOpenClose(next)) { |
|
closingQuote = next; |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
enum kind { |
|
none = 0, |
|
comment = 1, |
|
embedded = 2, |
|
placeholder = 3 |
|
}; |
|
|
|
void setState(int state) { |
|
verbatim = state >> 31; |
|
closingQuote = state >> 16 & 0x7FFF; |
|
kindStack = state & 0xFFFF; |
|
} |
|
|
|
int getState() { |
|
return verbatim << 31 | closingQuote << 16 | (kindStack & 0xFFFF); |
|
} |
|
|
|
void enter(kind k) { |
|
kindStack = kindStack << 2 | k; |
|
} |
|
|
|
void leave(kind k) { |
|
if (k == currentKind()) { |
|
kindStack = kindStack >> 2; |
|
} |
|
} |
|
kind currentKind() { |
|
return static_cast<kind>(kindStack & 0x3); |
|
} |
|
kind stateKind2(int ks) { |
|
if (0 == ks) { |
|
return none; |
|
} else { |
|
kind k1 = stateKind2(ks >> 2); |
|
kind k2 = static_cast<kind>(ks & 0x3); |
|
if (embedded == k1 && k2 == comment) { |
|
return embedded; |
|
} else { |
|
return k2; |
|
} |
|
} |
|
} |
|
kind stateKind() { |
|
return stateKind2(kindStack); |
|
} |
|
}; |
|
|
|
void SCI_METHOD LexerVisualProlog::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument* pAccess) { |
|
LexAccessor styler(pAccess); |
|
CharacterSet setDoxygen(CharacterSet::setAlpha, ""); |
|
CharacterSet setNumber(CharacterSet::setNone, "0123456789abcdefABCDEFxoXO_"); |
|
|
|
StyleContext sc(startPos, length, initStyle, styler, 0x7f); |
|
|
|
int styleBeforeDocKeyword = SCE_VISUALPROLOG_DEFAULT; |
|
|
|
lineState ls; |
|
if (sc.currentLine >= 1) { |
|
ls.setState(styler.GetLineState(sc.currentLine - 1)); |
|
} |
|
|
|
bool newState = false; |
|
|
|
for (; sc.More(); sc.Forward()) { |
|
|
|
Sci_Position currentLineEntry = sc.currentLine; |
|
|
|
if (newState) { |
|
newState = false; |
|
int state; |
|
switch (ls.stateKind()) { |
|
case lineState::comment: |
|
state = SCE_VISUALPROLOG_COMMENT_BLOCK; |
|
break; |
|
case lineState::embedded: |
|
state = SCE_VISUALPROLOG_EMBEDDED; |
|
break; |
|
case lineState::placeholder: |
|
state = SCE_VISUALPROLOG_PLACEHOLDER; |
|
break; |
|
default: |
|
state = SCE_VISUALPROLOG_DEFAULT; |
|
break; |
|
} |
|
sc.SetState(state); |
|
} |
|
|
|
// Determine if the current state should terminate. |
|
switch (sc.state) { |
|
case SCE_VISUALPROLOG_OPERATOR: |
|
sc.SetState(SCE_VISUALPROLOG_DEFAULT); |
|
break; |
|
case SCE_VISUALPROLOG_NUMBER: |
|
// We accept almost anything because of hex, '.' and number suffixes |
|
if (!(setNumber.Contains(sc.ch)) || (sc.Match('.') && IsADigit(sc.chNext))) { |
|
sc.SetState(SCE_VISUALPROLOG_DEFAULT); |
|
} |
|
break; |
|
case SCE_VISUALPROLOG_IDENTIFIER: |
|
if (!isIdChar(sc.ch)) { |
|
char s[1000]; |
|
sc.GetCurrent(s, sizeof(s)); |
|
if (0 == strcmp(s, "end")) { |
|
endLookAhead(s, styler, sc.currentPos); |
|
} |
|
if (majorKeywords.InList(s)) { |
|
sc.ChangeState(SCE_VISUALPROLOG_KEY_MAJOR); |
|
} else if (minorKeywords.InList(s)) { |
|
sc.ChangeState(SCE_VISUALPROLOG_KEY_MINOR); |
|
} |
|
sc.SetState(SCE_VISUALPROLOG_DEFAULT); |
|
} |
|
break; |
|
case SCE_VISUALPROLOG_VARIABLE: |
|
case SCE_VISUALPROLOG_ANONYMOUS: |
|
if (!isIdChar(sc.ch)) { |
|
sc.SetState(SCE_VISUALPROLOG_DEFAULT); |
|
} |
|
break; |
|
case SCE_VISUALPROLOG_KEY_DIRECTIVE: |
|
if (!isLowerLetter(sc.ch)) { |
|
char s[1000]; |
|
sc.GetCurrent(s, sizeof(s)); |
|
if (!directiveKeywords.InList(s + 1)) { |
|
sc.ChangeState(SCE_VISUALPROLOG_IDENTIFIER); |
|
} |
|
sc.SetState(SCE_VISUALPROLOG_DEFAULT); |
|
} |
|
break; |
|
case SCE_VISUALPROLOG_COMMENT_LINE: |
|
if (sc.MatchLineEnd()) { |
|
int nextState = (lineState::comment == ls.currentKind()) ? SCE_VISUALPROLOG_COMMENT_BLOCK : SCE_VISUALPROLOG_DEFAULT; |
|
sc.SetState(nextState); |
|
} else if (sc.Match('@')) { |
|
styleBeforeDocKeyword = SCE_VISUALPROLOG_COMMENT_LINE; |
|
sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR); |
|
} |
|
break; |
|
case SCE_VISUALPROLOG_COMMENT_BLOCK: |
|
if (sc.Match('*', '/')) { |
|
sc.Forward(); |
|
ls.leave(lineState::comment); |
|
newState = true; |
|
} else if (sc.Match('/', '*')) { |
|
sc.Forward(); |
|
ls.enter(lineState::comment); |
|
} else if (sc.Match('@')) { |
|
styleBeforeDocKeyword = SCE_VISUALPROLOG_COMMENT_BLOCK; |
|
sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR); |
|
} |
|
break; |
|
case SCE_VISUALPROLOG_COMMENT_KEY_ERROR: |
|
if (!setDoxygen.Contains(sc.ch) || sc.MatchLineEnd()) { |
|
char s[1000]; |
|
sc.GetCurrent(s, sizeof(s)); |
|
if (docKeywords.InList(s + 1)) { |
|
sc.ChangeState(SCE_VISUALPROLOG_COMMENT_KEY); |
|
} |
|
if (SCE_VISUALPROLOG_COMMENT_LINE == styleBeforeDocKeyword && sc.MatchLineEnd()) { |
|
// end line comment |
|
int nextState = (lineState::comment == ls.currentKind()) ? SCE_VISUALPROLOG_COMMENT_BLOCK : SCE_VISUALPROLOG_DEFAULT; |
|
sc.SetState(nextState); |
|
} else { |
|
sc.SetState(styleBeforeDocKeyword); |
|
if (SCE_VISUALPROLOG_COMMENT_BLOCK == styleBeforeDocKeyword && sc.Match('*', '/')) { |
|
// we have consumed the '*' if it comes immediately after the docKeyword |
|
sc.Forward(); |
|
ls.leave(lineState::comment); |
|
newState = true; |
|
} |
|
} |
|
} |
|
break; |
|
case SCE_VISUALPROLOG_STRING_ESCAPE_ERROR: |
|
if (sc.atLineStart) { |
|
sc.SetState(SCE_VISUALPROLOG_DEFAULT); |
|
break; |
|
} |
|
[[fallthrough]]; |
|
case SCE_VISUALPROLOG_STRING_ESCAPE: |
|
case SCE_VISUALPROLOG_STRING_QUOTE: |
|
case SCE_VISUALPROLOG_STRING_EOL: |
|
// return to SCE_VISUALPROLOG_STRING and treat as such (fallthrough) |
|
sc.SetState(SCE_VISUALPROLOG_STRING); |
|
[[fallthrough]]; |
|
case SCE_VISUALPROLOG_STRING: |
|
if (sc.MatchLineEnd() | sc.atLineEnd) { |
|
if (ls.verbatim) { |
|
sc.SetState(SCE_VISUALPROLOG_STRING_EOL); |
|
} else { |
|
ls.closingQuote = 0; |
|
sc.SetState(SCE_VISUALPROLOG_STRING_ESCAPE_ERROR); |
|
} |
|
} else if (sc.Match(ls.closingQuote)) { |
|
if (ls.verbatim && ls.closingQuote == sc.chNext) { |
|
sc.SetState(SCE_VISUALPROLOG_STRING_ESCAPE); |
|
sc.Forward(); |
|
} else { |
|
ls.closingQuote = 0; |
|
sc.SetState(SCE_VISUALPROLOG_STRING_QUOTE); |
|
sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT); |
|
} |
|
} else if (!ls.verbatim && sc.Match('\\')) { |
|
sc.SetState(SCE_VISUALPROLOG_STRING_ESCAPE_ERROR); |
|
sc.Forward(); |
|
if (sc.MatchLineEnd()) { |
|
sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT); |
|
} else { |
|
if (sc.Match('"') || sc.Match('\'') || sc.Match('\\') || sc.Match('n') || sc.Match('l') || sc.Match('r') || sc.Match('t')) { |
|
sc.ChangeState(SCE_VISUALPROLOG_STRING_ESCAPE); |
|
} else if (sc.Match('u')) { |
|
if (IsADigit(sc.chNext, 16)) { |
|
sc.Forward(); |
|
if (IsADigit(sc.chNext, 16)) { |
|
sc.Forward(); |
|
if (IsADigit(sc.chNext, 16)) { |
|
sc.Forward(); |
|
if (IsADigit(sc.chNext, 16)) { |
|
sc.Forward(); |
|
sc.ChangeState(SCE_VISUALPROLOG_STRING_ESCAPE); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
case SCE_VISUALPROLOG_EMBEDDED: |
|
if (sc.Match('|', ']')) { |
|
sc.Forward(); |
|
ls.leave(lineState::embedded); |
|
newState = true; |
|
} else if (sc.Match('[', '|')) { |
|
sc.Forward(); |
|
ls.enter(lineState::embedded); |
|
} else if (sc.Match('{', '|') && lineState::comment != ls.currentKind()) { |
|
sc.SetState(SCE_VISUALPROLOG_DEFAULT); |
|
} else if (sc.Match('/', '*')) { |
|
sc.Forward(); |
|
ls.enter(lineState::comment); |
|
} else if (sc.Match('*', '/')) { |
|
sc.Forward(); |
|
ls.leave(lineState::comment); |
|
newState = true; |
|
} |
|
break; |
|
case SCE_VISUALPROLOG_PLACEHOLDER: |
|
if (lineState::embedded == ls.currentKind()) { |
|
sc.SetState(SCE_VISUALPROLOG_EMBEDDED); |
|
} else { |
|
sc.SetState(SCE_VISUALPROLOG_DEFAULT); |
|
} |
|
break; |
|
} |
|
|
|
if (currentLineEntry != sc.currentLine) { |
|
styler.SetLineState(currentLineEntry, ls.getState()); |
|
} |
|
if (sc.MatchLineEnd() | sc.atLineEnd) { |
|
if (sc.More()) { // currentLine can be outside the document |
|
styler.SetLineState(sc.currentLine, ls.getState()); |
|
} |
|
} |
|
|
|
// Determine if a new state should be entered. |
|
if (sc.state == SCE_VISUALPROLOG_DEFAULT) { |
|
if (options.verbatimStrings && sc.Match('@') && ls.isOpenStringVerbatim(sc.chNext)) { |
|
ls.verbatim = true; |
|
sc.SetState(SCE_VISUALPROLOG_STRING_QUOTE); |
|
sc.Forward(); |
|
} else if (IsADigit(sc.ch) || (sc.Match('.') && IsADigit(sc.chNext))) { |
|
sc.SetState(SCE_VISUALPROLOG_NUMBER); |
|
} else if (isLowerLetter(sc.ch)) { |
|
sc.SetState(SCE_VISUALPROLOG_IDENTIFIER); |
|
} else if (isUpperLetter(sc.ch)) { |
|
sc.SetState(SCE_VISUALPROLOG_VARIABLE); |
|
} else if (sc.Match('_')) { |
|
sc.SetState(SCE_VISUALPROLOG_ANONYMOUS); |
|
} else if (sc.Match('/', '*')) { |
|
sc.SetState(SCE_VISUALPROLOG_COMMENT_BLOCK); |
|
ls.enter(lineState::comment); |
|
sc.Forward(); |
|
} else if (sc.Match('%')) { |
|
sc.SetState(SCE_VISUALPROLOG_COMMENT_LINE); |
|
} else if (sc.Match('[', '|')) { |
|
sc.SetState(SCE_VISUALPROLOG_EMBEDDED); |
|
ls.enter(lineState::embedded); |
|
sc.Forward(); |
|
} else if (sc.Match('{', '|')) { |
|
sc.SetState(SCE_VISUALPROLOG_PLACEHOLDER); |
|
ls.enter(lineState::placeholder); |
|
sc.Forward(); |
|
} else if (sc.Match('|', '}')) { |
|
sc.SetState(SCE_VISUALPROLOG_PLACEHOLDER); |
|
sc.Forward(); |
|
if (':' == sc.chNext) { |
|
sc.Forward(); |
|
for (; isIdChar(sc.chNext); sc.Forward()) { |
|
} |
|
} |
|
ls.leave(lineState::placeholder); |
|
newState = true; |
|
} else if (sc.Match('\'')) { |
|
ls.verbatim = false; |
|
ls.closingQuote = '\''; |
|
sc.SetState(SCE_VISUALPROLOG_STRING_QUOTE); |
|
} else if (sc.Match('"')) { |
|
ls.verbatim = false; |
|
ls.closingQuote = '"'; |
|
sc.SetState(SCE_VISUALPROLOG_STRING_QUOTE); |
|
} else if (options.backQuotedStrings && sc.Match('`')) { |
|
ls.verbatim = false; |
|
ls.closingQuote = '`'; |
|
sc.SetState(SCE_VISUALPROLOG_STRING_QUOTE); |
|
} else if (sc.Match('#')) { |
|
sc.SetState(SCE_VISUALPROLOG_KEY_DIRECTIVE); |
|
} else if (isoperator(static_cast<char>(sc.ch)) || sc.Match('\\') || |
|
(!options.verbatimStrings && sc.Match('@'))) { |
|
sc.SetState(SCE_VISUALPROLOG_OPERATOR); |
|
} |
|
} |
|
} |
|
sc.Complete(); |
|
styler.Flush(); |
|
} |
|
|
|
// Store both the current line's fold level and the next lines in the |
|
// level store to make it easy to pick up with each increment |
|
// and to make it possible to fiddle the current level for "} else {". |
|
|
|
#if defined(__clang__) |
|
#if __has_warning("-Wunused-but-set-variable") |
|
// Disable warning for visibleChars |
|
#pragma clang diagnostic ignored "-Wunused-but-set-variable" |
|
#endif |
|
#endif |
|
|
|
void SCI_METHOD LexerVisualProlog::Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument* pAccess) { |
|
|
|
LexAccessor styler(pAccess); |
|
|
|
Sci_PositionU endPos = startPos + length; |
|
int visibleChars = 0; |
|
Sci_Position currentLine = styler.GetLine(startPos); |
|
int levelCurrent = SC_FOLDLEVELBASE; |
|
if (currentLine > 0) |
|
levelCurrent = styler.LevelAt(currentLine - 1) >> 16; |
|
int levelMinCurrent = levelCurrent; |
|
int levelNext = levelCurrent; |
|
char chNext = styler[startPos]; |
|
int styleNext = styler.StyleAt(startPos); |
|
int style = initStyle; |
|
for (Sci_PositionU i = startPos; i < endPos; i++) { |
|
char ch = chNext; |
|
chNext = styler.SafeGetCharAt(i + 1); |
|
style = styleNext; |
|
styleNext = styler.StyleAt(i + 1); |
|
bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
|
if (style == SCE_VISUALPROLOG_OPERATOR) { |
|
if (ch == '{') { |
|
// Measure the minimum before a '{' to allow |
|
// folding on "} else {" |
|
if (levelMinCurrent > levelNext) { |
|
levelMinCurrent = levelNext; |
|
} |
|
levelNext++; |
|
} else if (ch == '}') { |
|
levelNext--; |
|
} |
|
} |
|
if (!IsASpace(ch)) |
|
visibleChars++; |
|
if (atEOL || (i == endPos - 1)) { |
|
int levelUse = levelCurrent; |
|
int lev = levelUse | levelNext << 16; |
|
if (levelUse < levelNext) |
|
lev |= SC_FOLDLEVELHEADERFLAG; |
|
if (lev != styler.LevelAt(currentLine)) { |
|
styler.SetLevel(currentLine, lev); |
|
} |
|
currentLine++; |
|
levelCurrent = levelNext; |
|
levelMinCurrent = levelCurrent; |
|
if (atEOL && (i == static_cast<Sci_PositionU>(styler.Length() - 1))) { |
|
// There is an empty line at end of file so give it same level and empty |
|
styler.SetLevel(currentLine, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG); |
|
} |
|
visibleChars = 0; |
|
} |
|
} |
|
} |
|
} |
|
|
|
LexerModule lmVisualProlog(SCLEX_VISUALPROLOG, LexerVisualProlog::LexerFactoryVisualProlog, "visualprolog", visualPrologWordLists);
|
|
|