diff options
author | Yuval Papish <Yuval@YuvCom.com> | 2016-09-21 21:16:04 +1000 |
---|---|---|
committer | Yuval Papish <Yuval@YuvCom.com> | 2016-09-21 21:16:04 +1000 |
commit | 4dff76d6ab5c2f08c128ee157bdefda9b2894fa4 (patch) | |
tree | 6416471df248f619c5b51f73589ca4ec5ce89e64 /lexers/LexProgress.cxx | |
parent | c14fadb44739d232fa04e7d984857a28471baf5d (diff) | |
download | scintilla-mirror-4dff76d6ab5c2f08c128ee157bdefda9b2894fa4.tar.gz |
Feature [feature-requests:#1143]. Replace "progress" lexer with "abl".
ILexer implementation
Lexer can now correctly handle:
"end triggers" phrase
"last-event:function" phrase
Indefinite comment level depth
Diffstat (limited to 'lexers/LexProgress.cxx')
-rw-r--r-- | lexers/LexProgress.cxx | 787 |
1 files changed, 515 insertions, 272 deletions
diff --git a/lexers/LexProgress.cxx b/lexers/LexProgress.cxx index 6c687efee..c1c4529f2 100644 --- a/lexers/LexProgress.cxx +++ b/lexers/LexProgress.cxx @@ -3,13 +3,18 @@ ** Lexer for Progress 4GL. ** Based on LexCPP.cxx of Neil Hodgson <neilh@scintilla.org> **/ -// Copyright 2006-2007 by Yuval Papish <Yuval@YuvCom.com> +// Copyright 2006-2016 by Yuval Papish <Yuval@YuvCom.com> // The License.txt file describes the conditions under which this software may be distributed. /** TODO: -WebSpeed support in html lexer -Support "end triggers" expression of the triggers phrase + +SpeedScript support in html lexer +Differentiate between labels and variables + Option 1: By symbols table + Option 2: As a single unidentified symbol in a sytactical line + **/ + #include <stdlib.h> #include <string.h> #include <stdio.h> @@ -17,300 +22,538 @@ Support "end triggers" expression of the triggers phrase #include <assert.h> #include <ctype.h> +#include <string> +#include <vector> +#include <map> + #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" +#include "OptionSet.h" +#include "SparseState.h" #ifdef SCI_NAMESPACE using namespace Scintilla; #endif -static inline bool IsAWordChar(int ch) { - return (ch < 0x80) && (isalnum(ch) || ch == '_'); +namespace { + // Use an unnamed namespace to protect the functions and classes from name conflicts + + bool IsSpaceEquiv(int state) { + return (state == SCE_ABL_COMMENT || + state == SCE_ABL_LINECOMMENT || + state == SCE_ABL_DEFAULT); + } + + void highlightTaskMarker(StyleContext &sc, LexAccessor &styler, WordList &markerList){ + if ((isoperator(sc.chPrev) || IsASpace(sc.chPrev)) && markerList.Length()) { + const int lengthMarker = 50; + char marker[lengthMarker+1]; + Sci_Position currPos = (Sci_Position) sc.currentPos; + Sci_Position i = 0; + while (i < lengthMarker) { + char ch = styler.SafeGetCharAt(currPos + i); + if (IsASpace(ch) || isoperator(ch)) { + break; + } + marker[i] = ch; + i++; + } + marker[i] = '\0'; + if (markerList.InListAbbreviated (marker,'(')) { + sc.SetState(SCE_ABL_TASKMARKER); + } + } + } + + bool IsStreamCommentStyle(int style) { + return style == SCE_ABL_COMMENT; + // style == SCE_ABL_LINECOMMENT; Only block comments are used for folding + } + + // Options used for LexerABL + struct OptionsABL { + bool fold; + bool foldSyntaxBased; + bool foldComment; + bool foldCommentMultiline; + bool foldCompact; + OptionsABL() { + fold = false; + foldSyntaxBased = true; + foldComment = true; + foldCommentMultiline = true; + foldCompact = false; + } + }; + + const char *const ablWordLists[] = { + "Primary keywords and identifiers", + "Keywords that opens a block, only when used to begin a syntactic line", + "Keywords that opens a block anywhere in a syntactic line", + "Task Marker", /* "END MODIFY START TODO" */ + 0, + }; + + struct OptionSetABL : public OptionSet<OptionsABL> { + OptionSetABL() { + DefineProperty("fold", &OptionsABL::fold); + + DefineProperty("fold.abl.syntax.based", &OptionsABL::foldSyntaxBased, + "Set this property to 0 to disable syntax based folding."); + + DefineProperty("fold.comment", &OptionsABL::foldComment, + "This option enables folding multi-line comments and explicit fold points when using the ABL lexer. "); + + DefineProperty("fold.abl.comment.multiline", &OptionsABL::foldCommentMultiline, + "Set this property to 0 to disable folding multi-line comments when fold.comment=1."); + + DefineProperty("fold.compact", &OptionsABL::foldCompact); + + DefineWordListSets(ablWordLists); + } + }; } -static inline bool IsAWordStart(int ch) { - return (ch < 0x80) && (isalpha(ch) || ch == '_'); +class LexerABL : public ILexer { + CharacterSet setWord; + CharacterSet setNegationOp; + CharacterSet setArithmethicOp; + CharacterSet setRelOp; + CharacterSet setLogicalOp; + CharacterSet setWordStart; + WordList keywords1; // regular keywords + WordList keywords2; // block opening keywords, only when isSentenceStart + WordList keywords3; // block opening keywords + WordList keywords4; // Task Marker + OptionsABL options; + OptionSetABL osABL; +public: + LexerABL() : + setWord(CharacterSet::setAlphaNum, "_", 0x80, true), + setNegationOp(CharacterSet::setNone, "!"), + setArithmethicOp(CharacterSet::setNone, "+-/*%"), + setRelOp(CharacterSet::setNone, "=!<>"), + setLogicalOp(CharacterSet::setNone, "|&"){ + } + virtual ~LexerABL() { + } + void SCI_METHOD Release() { + delete this; + } + int SCI_METHOD Version() const { + return lvOriginal; + } + const char * SCI_METHOD PropertyNames() { + return osABL.PropertyNames(); + } + int SCI_METHOD PropertyType(const char *name) { + return osABL.PropertyType(name); + } + const char * SCI_METHOD DescribeProperty(const char *name) { + return osABL.DescribeProperty(name); + } + Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) ; + + const char * SCI_METHOD DescribeWordListSets() { + return osABL.DescribeWordListSets(); + } + Sci_Position SCI_METHOD WordListSet(int n, const char *wl); + void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess); + void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess); + + void * SCI_METHOD PrivateCall(int, void *) { + return 0; + } + int SCI_METHOD LineEndTypesSupported() { + return SC_LINE_END_TYPE_DEFAULT; + } + static ILexer *LexerFactoryABL() { + return new LexerABL(); + } +}; + +Sci_Position SCI_METHOD LexerABL::PropertySet(const char *key, const char *val) { + if (osABL.PropertySet(&options, key, val)) { + return 0; + } + return -1; } -enum SentenceStart { SetSentenceStart = 0xf, ResetSentenceStart = 0x10}; // true -> bit = 0 - -static void Colourise4glDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], - Accessor &styler) { - - WordList &keywords1 = *keywordlists[0]; // regular keywords - WordList &keywords2 = *keywordlists[1]; // block opening keywords, only when SentenceStart - WordList &keywords3 = *keywordlists[2]; // block opening keywords - //WordList &keywords4 = *keywordlists[3]; // preprocessor keywords. Not implemented - - Sci_Position currentLine = styler.GetLine(startPos); - // Initialize the block comment /* */ nesting level, if we are inside such a comment. - int blockCommentLevel = 0; - if (initStyle == SCE_4GL_COMMENT1 || - initStyle == SCE_4GL_COMMENT1_) { - blockCommentLevel = styler.GetLineState(currentLine - 1); - } - - // Do not leak single-line comments onto next line - if (initStyle == SCE_4GL_COMMENT2 || - initStyle == SCE_4GL_COMMENT2_) { - initStyle = SCE_4GL_DEFAULT; - } - - int visibleChars = 0; - int mask; - - StyleContext sc(startPos, length, initStyle, styler); - - for (; sc.More(); sc.Forward()) { - - if (sc.atLineStart) { - // Reset states to begining of colourise so no surprises - // if different sets of lines lexed. - visibleChars = 0; - } - - if (sc.atLineEnd) { - // Update the line state, so it can be seen by next line - currentLine = styler.GetLine(sc.currentPos); - if (sc.state == SCE_4GL_COMMENT1 || - sc.state == SCE_4GL_COMMENT1_) { - // Inside a block comment, we set the line state - styler.SetLineState(currentLine, blockCommentLevel); - } else { - // Reset the line state - styler.SetLineState(currentLine, 0); - } - } - - // Handle line continuation generically. - if ((sc.state & 0xf) < SCE_4GL_COMMENT1) { - if (sc.ch == '~') { - if (sc.chNext > ' ') { - // skip special char after ~ - sc.Forward(); - continue; - } - else { - // Skip whitespace between ~ and EOL - while (sc.More() && (sc.chNext == ' ' || sc.chNext == '\t') ) { - sc.Forward(); - } - if (sc.chNext == '\n' || sc.chNext == '\r') { - sc.Forward(); - if (sc.ch == '\r' && sc.chNext == '\n') { - sc.Forward(); - } - sc.Forward(); - continue; - } - } - } - } - // Determine if a new state should be terminated. - mask = sc.state & 0x10; - switch (sc.state & 0xf) { - case SCE_4GL_OPERATOR: - sc.SetState(SCE_4GL_DEFAULT | mask); - break; - case SCE_4GL_NUMBER: - // Hex numbers (0xnnnn) are supported so accept any - // alphanumeric character if it follows a leading digit. - if (!(IsAlphaNumeric(sc.ch))) { - sc.SetState(SCE_4GL_DEFAULT | mask); - } - break; - case SCE_4GL_IDENTIFIER: - if (!IsAWordChar(sc.ch) && sc.ch != '-') { - char s[1000]; - sc.GetCurrentLowered(s, sizeof(s)); - if ((((sc.state & 0x10) == 0) && keywords2.InListAbbreviated(s, '(')) || keywords3.InListAbbreviated(s, '(')) { - sc.ChangeState(SCE_4GL_BLOCK | ResetSentenceStart); - } - else if (keywords1.InListAbbreviated(s, '(')) { - if ((s[0] == 'e' && s[1] =='n' && s[2] == 'd' && !isalnum(s[3]) && s[3] != '-') || - (s[0] == 'f' && s[1] =='o' && s[2] == 'r' && s[3] == 'w' && s[4] =='a' && s[5] == 'r' && s[6] == 'd'&& !isalnum(s[7]))) { - sc.ChangeState(SCE_4GL_END | ResetSentenceStart); - } - else if ((s[0] == 'e' && s[1] =='l' && s[2] == 's' && s[3] == 'e') || - (s[0] == 't' && s[1] =='h' && s[2] == 'e' && s[3] == 'n')) { - sc.ChangeState(SCE_4GL_WORD & SetSentenceStart); - } - else { - sc.ChangeState(SCE_4GL_WORD | ResetSentenceStart); - } - } - sc.SetState(SCE_4GL_DEFAULT | (sc.state & 0x10)); - } - break; - case SCE_4GL_PREPROCESSOR: - if (sc.atLineStart) { - sc.SetState(SCE_4GL_DEFAULT & SetSentenceStart); - } - /* code removed to allow comments inside preprocessor - else if (sc.ch == '*' && sc.chNext == '/') { - sc.ForwardSetState(SCE_4GL_DEFAULT | sentenceStartState); } */ - break; - case SCE_4GL_STRING: - if (sc.ch == '\"') { - sc.ForwardSetState(SCE_4GL_DEFAULT | mask); - } - break; - case SCE_4GL_CHARACTER: - if (sc.ch == '\'') { - sc.ForwardSetState(SCE_4GL_DEFAULT | mask); - } - break; - case SCE_4GL_COMMENT1: - if (sc.Match('/', '*')) { - blockCommentLevel++; - sc.Forward(); - } else if (sc.Match('*', '/') && blockCommentLevel > 0) { - blockCommentLevel--; - sc.Forward(); - if (blockCommentLevel == 0) { - sc.ForwardSetState(SCE_4GL_DEFAULT | mask); - } - } - break; - case SCE_4GL_COMMENT2: - if (sc.atLineEnd) { - sc.ForwardSetState(SCE_4GL_DEFAULT | mask); - } - break; - } - - // Determine if a new state should be entered. - mask = sc.state & 0x10; - if ((sc.state & 0xf) == SCE_4GL_DEFAULT) { - if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { - sc.SetState(SCE_4GL_NUMBER | ResetSentenceStart); - } else if (IsAWordStart(sc.ch) || (sc.ch == '@')) { - sc.SetState(SCE_4GL_IDENTIFIER | mask); - } else if (sc.Match('/', '*')) { - blockCommentLevel = 1; - sc.SetState(SCE_4GL_COMMENT1 | mask); - sc.Forward(); - } else if (sc.Match('/', '/') && - (sc.atLineStart || sc.chPrev == ' ' || sc.chPrev == '\t')) { - sc.SetState(SCE_4GL_COMMENT2 | mask); - } else if (sc.ch == '\"') { - sc.SetState(SCE_4GL_STRING | ResetSentenceStart); - } else if (sc.ch == '\'') { - sc.SetState(SCE_4GL_CHARACTER | ResetSentenceStart); - } else if (sc.ch == '&' && visibleChars == 0 && ((sc.state & 0x10) == 0)) { - sc.SetState(SCE_4GL_PREPROCESSOR | ResetSentenceStart); - // Skip whitespace between & and preprocessor word - do { - sc.Forward(); - } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More()); - // Handle syntactical line termination - } else if ((sc.ch == '.' || sc.ch == ':' || sc.ch == '}') && (sc.chNext == ' ' || sc.chNext == '\t' || sc.chNext == '\n' || sc.chNext == '\r')) { - sc.SetState(sc.state & SetSentenceStart); - } else if (isoperator(static_cast<char>(sc.ch))) { - /* This code allows highlight of handles. Alas, it would cause the phrase "last-event:function" - to be recognized as a BlockBegin */ - - if (sc.ch == ':') - sc.SetState(SCE_4GL_OPERATOR & SetSentenceStart); - /* else */ - sc.SetState(SCE_4GL_OPERATOR | ResetSentenceStart); - } - } - - if (!IsASpace(sc.ch)) { - visibleChars++; - } - } - sc.Complete(); +Sci_Position SCI_METHOD LexerABL::WordListSet(int n, const char *wl) { + WordList *wordListN = 0; + switch (n) { + case 0: + wordListN = &keywords1; + break; + case 1: + wordListN = &keywords2; + break; + case 2: + wordListN = &keywords3; + break; + case 3: + wordListN = &keywords4; + break; + } + Sci_Position firstModification = -1; + if (wordListN) { + WordList wlNew; + wlNew.Set(wl); + if (*wordListN != wlNew) { + wordListN->Set(wl); + firstModification = 0; + } + } + return firstModification; } -static bool IsStreamCommentStyle(int style) { - return (style & 0xf) == SCE_4GL_COMMENT1 ; +void SCI_METHOD LexerABL::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) { + LexAccessor styler(pAccess); + + setWordStart = CharacterSet(CharacterSet::setAlpha, "_", 0x80, true); + + int visibleChars = 0; + int visibleChars1 = 0; + int styleBeforeTaskMarker = SCE_ABL_DEFAULT; + bool continuationLine = false; + int commentNestingLevel = 0; + bool isSentenceStart = true; + bool possibleOOLChange = false; + + Sci_Position lineCurrent = styler.GetLine(startPos); + if (initStyle == SCE_ABL_PREPROCESSOR) { + // Set continuationLine if last character of previous line is '~' + if (lineCurrent > 0) { + Sci_Position endLinePrevious = styler.LineEnd(lineCurrent-1); + if (endLinePrevious > 0) { + continuationLine = styler.SafeGetCharAt(endLinePrevious-1) == '~'; + } + } + } + + // Look back to set variables that are actually invisible secondary states. The reason to avoid formal states is to cut down on state's bits + if (startPos > 0) { + Sci_Position back = startPos; + bool checkCommentNestingLevel = (initStyle == SCE_ABL_COMMENT); + bool checkIsSentenceStart = (initStyle == SCE_ABL_DEFAULT || initStyle == SCE_ABL_IDENTIFIER); + char ch; + char st; + char chPrev; + char chPrev_1; + char chPrev_2; + char chPrev_3; + + while (back >= 0 && (checkCommentNestingLevel || checkIsSentenceStart)) { + ch = styler.SafeGetCharAt(back); + styler.Flush(); // looking at styles so need to flush + st = styler.StyleAt(back); + + chPrev = styler.SafeGetCharAt(back-1); + // isSentenceStart is a non-visible state, used to identify where statements and preprocessor declerations can start + if (checkIsSentenceStart && st != SCE_ABL_COMMENT && st != SCE_ABL_LINECOMMENT && st != SCE_ABL_CHARACTER && st != SCE_ABL_STRING ) { + chPrev_1 = styler.SafeGetCharAt(back-2); + chPrev_2 = styler.SafeGetCharAt(back-3); + chPrev_3 = styler.SafeGetCharAt(back-4); + if ((chPrev == '.' || chPrev == ':' || chPrev == '}' || + (chPrev_3 == 'e' && chPrev_2 == 'l' && chPrev_1 == 's' && chPrev == 'e') || + (chPrev_3 == 't' && chPrev_2 == 'h' && chPrev_1 == 'e' && chPrev == 'n')) && + (IsASpace(ch) || (ch == '/' && styler.SafeGetCharAt(back+1) == '*')) + ) { + checkIsSentenceStart = false; + isSentenceStart = true; + } + else if (IsASpace(chPrev) && ch == '{') { + checkIsSentenceStart = false; + isSentenceStart = false; + } + } + + // commentNestingLevel is a non-visible state, used to identify the nesting level of a comment + if (checkCommentNestingLevel) { + if (chPrev == '/' && ch == '*') + commentNestingLevel++; + if (chPrev == '*' && ch == '/') { + commentNestingLevel--; + } + } + --back; + } + } + + StyleContext sc(startPos, length, initStyle, styler, static_cast<unsigned char>(0xff)); + Sci_Position lineEndNext = styler.LineEnd(lineCurrent); + + for (; sc.More();) { + if (sc.atLineStart) { + visibleChars = 0; + visibleChars1 = 0; + } + if (sc.atLineEnd) { + lineCurrent++; + lineEndNext = styler.LineEnd(lineCurrent); + } + // Handle line continuation generically. + if (sc.ch == '~') { + if (static_cast<Sci_Position>((sc.currentPos+1)) >= lineEndNext) { + lineCurrent++; + lineEndNext = styler.LineEnd(lineCurrent); + sc.Forward(); + if (sc.ch == '\r' && sc.chNext == '\n') { + sc.Forward(); + } + continuationLine = true; + sc.Forward(); + continue; + } + } + + const bool atLineEndBeforeSwitch = sc.atLineEnd; + // Determine if the current state should terminate. + switch (sc.state) { + case SCE_ABL_OPERATOR: + sc.SetState(SCE_ABL_DEFAULT); + break; + case SCE_ABL_NUMBER: + // We accept almost anything because of hex. and maybe number suffixes and scientific notations in the future + if (!(setWord.Contains(sc.ch) + || ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E' || + sc.chPrev == 'p' || sc.chPrev == 'P')))) { + sc.SetState(SCE_ABL_DEFAULT); + } + break; + case SCE_ABL_IDENTIFIER: + if (sc.atLineStart || sc.atLineEnd || (!setWord.Contains(sc.ch) && sc.ch != '-')) { + char s[1000]; + sc.GetCurrentLowered(s, sizeof(s)); + bool isLastWordEnd = (s[0] == 'e' && s[1] =='n' && s[2] == 'd' && !IsAlphaNumeric(s[3]) && s[3] != '-'); // helps to identify "end trigger" phrase + if ((isSentenceStart && keywords2.InListAbbreviated (s,'(')) || (!isLastWordEnd && keywords3.InListAbbreviated (s,'('))) { + sc.ChangeState(SCE_ABL_BLOCK); + isSentenceStart = false; + } + else if (keywords1.InListAbbreviated (s,'(')) { + if (isLastWordEnd || + (s[0] == 'f' && s[1] =='o' && s[2] == 'r' && s[3] == 'w' && s[4] =='a' && s[5] == 'r' && s[6] == 'd'&& !IsAlphaNumeric(s[7]))) { + sc.ChangeState(SCE_ABL_END); + isSentenceStart = false; + } + else if ((s[0] == 'e' && s[1] =='l' && s[2] == 's' && s[3] == 'e') || + (s[0] == 't' && s[1] =='h' && s[2] == 'e' && s[3] == 'n')) { + sc.ChangeState(SCE_ABL_WORD); + isSentenceStart = true; + } + else { + sc.ChangeState(SCE_ABL_WORD); + isSentenceStart = false; + } + } + sc.SetState(SCE_ABL_DEFAULT); + } + break; + case SCE_ABL_PREPROCESSOR: + if (sc.atLineStart && !continuationLine) { + sc.SetState(SCE_ABL_DEFAULT); + // Force Scintilla to acknowledge changed stated even though this change might happen outside of the current line + possibleOOLChange = true; + isSentenceStart = true; + } + break; + case SCE_ABL_LINECOMMENT: + if (sc.atLineStart && !continuationLine) { + sc.SetState(SCE_ABL_DEFAULT); + isSentenceStart = true; + } else { + styleBeforeTaskMarker = SCE_ABL_LINECOMMENT; + highlightTaskMarker(sc, styler, keywords4); + } + break; + case SCE_ABL_TASKMARKER: + if (isoperator(sc.ch) || IsASpace(sc.ch)) { + sc.SetState(styleBeforeTaskMarker); + styleBeforeTaskMarker = SCE_ABL_DEFAULT; + } + // fall through + case SCE_ABL_COMMENT: + if (sc.Match('*', '/')) { + sc.Forward(); + commentNestingLevel--; + if (commentNestingLevel == 0) { + sc.ForwardSetState(SCE_ABL_DEFAULT); + possibleOOLChange = true; + } + } else if (sc.Match('/', '*')) { + commentNestingLevel++; + sc.Forward(); + } + if (commentNestingLevel > 0) { + styleBeforeTaskMarker = SCE_ABL_COMMENT; + possibleOOLChange = true; + highlightTaskMarker(sc, styler, keywords4); + } + break; + case SCE_ABL_STRING: + if (sc.ch == '~') { + sc.Forward(); // Skip a character after a tilde + } else if (sc.ch == '\"') { + sc.ForwardSetState(SCE_ABL_DEFAULT); + } + break; + case SCE_ABL_CHARACTER: + if (sc.ch == '~') { + sc.Forward(); // Skip a character after a tilde + } else if (sc.ch == '\'') { + sc.ForwardSetState(SCE_ABL_DEFAULT); + } + break; + } + + if (sc.atLineEnd && !atLineEndBeforeSwitch) { + // State exit processing consumed characters up to end of line. + lineCurrent++; + lineEndNext = styler.LineEnd(lineCurrent); + } + + // Determine if a new state should be entered. + if (sc.state == SCE_ABL_DEFAULT) { + if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { + sc.SetState(SCE_ABL_NUMBER); + isSentenceStart = false; + } else if (!sc.atLineEnd && (setWordStart.Contains(sc.ch)) && sc.chPrev != '&') { + sc.SetState(SCE_ABL_IDENTIFIER); + } else if (sc.Match('/', '*')) { + if (sc.chPrev == '.' || sc.chPrev == ':' || sc.chPrev == '}') { + isSentenceStart = true; + } + sc.SetState(SCE_ABL_COMMENT); + possibleOOLChange = true; + commentNestingLevel++; + sc.Forward(); // Eat the * so it isn't used for the end of the comment + } else if (sc.ch == '\"') { + sc.SetState(SCE_ABL_STRING); + isSentenceStart = false; + } else if (sc.ch == '\'') { + sc.SetState(SCE_ABL_CHARACTER); + isSentenceStart = false; + } else if (sc.ch == '&' && visibleChars1 == 0 && isSentenceStart) { + // Preprocessor commands are alone on their line + sc.SetState(SCE_ABL_PREPROCESSOR); + // Force Scintilla to acknowledge changed stated even though this change might happen outside of the current line + possibleOOLChange = true; + // Skip whitespace between & and preprocessor word + do { + sc.Forward(); + } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More()); + if (sc.atLineEnd) { + sc.SetState(SCE_ABL_DEFAULT); + } + } else if (sc.Match('/','/') && (IsASpace(sc.chPrev) || isSentenceStart)) { + // Line comments are valid after a white space or EOL + sc.SetState(SCE_ABL_LINECOMMENT); + // Skip whitespace between // and preprocessor word + do { + sc.Forward(); + } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More()); + if (sc.atLineEnd) { + sc.SetState(SCE_ABL_DEFAULT); + } + } else if (isoperator(sc.ch)) { + sc.SetState(SCE_ABL_OPERATOR); + /* This code allows highlight of handles. Alas, it would cause the phrase "last-event:function" + to be recognized as a BlockBegin */ + isSentenceStart = false; + } + else if ((sc.chPrev == '.' || sc.chPrev == ':' || sc.chPrev == '}') && (IsASpace(sc.ch))) { + isSentenceStart = true; + } + } + if (!IsASpace(sc.ch)) { + visibleChars1++; + } + if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) { + visibleChars++; + } + continuationLine = false; + sc.Forward(); + } + if (possibleOOLChange) + styler.ChangeLexerState(startPos, startPos + length); + sc.Complete(); } + // 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 {". -static void FoldNoBox4glDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, - Accessor &styler) { - bool foldComment = styler.GetPropertyInt("fold.comment") != 0; - bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; - bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0; - Sci_PositionU endPos = startPos + length; - int visibleChars = 0; - Sci_Position lineCurrent = styler.GetLine(startPos); - int levelCurrent = SC_FOLDLEVELBASE; - if (lineCurrent > 0) - levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; - int levelMinCurrent = levelCurrent; - int levelNext = levelCurrent; - char chNext = static_cast<char>(tolower(styler[startPos])); - int styleNext = styler.StyleAt(startPos); - int style = initStyle; - for (Sci_PositionU i = startPos; i < endPos; i++) { - char ch = chNext; - chNext = static_cast<char>(tolower(styler.SafeGetCharAt(i + 1))); - int stylePrev = style; - style = styleNext; - styleNext = styler.StyleAt(i + 1); - bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); - if (foldComment && IsStreamCommentStyle(style)) { - if (!IsStreamCommentStyle(stylePrev)) { - levelNext++; - } else if (!IsStreamCommentStyle(styleNext)) { // && !atEOL) { - // Comments don't end at end of line and the next character may be unstyled. - levelNext--; - } - } - else if ((style & 0xf) == SCE_4GL_BLOCK && !isalnum(chNext)) { - levelNext++; - } - else if ((style & 0xf) == SCE_4GL_END && (ch == 'e' || ch == 'f')) { - levelNext--; - } - if (atEOL) { - int levelUse = levelCurrent; - if (foldAtElse) { - levelUse = levelMinCurrent; - } - int lev = levelUse | levelNext << 16; - if (visibleChars == 0 && foldCompact) - lev |= SC_FOLDLEVELWHITEFLAG; - if (levelUse < levelNext) - lev |= SC_FOLDLEVELHEADERFLAG; - if (lev != styler.LevelAt(lineCurrent)) { - styler.SetLevel(lineCurrent, lev); - } - lineCurrent++; - levelCurrent = levelNext; - levelMinCurrent = levelCurrent; - visibleChars = 0; - } - if (!isspacechar(ch)) - visibleChars++; - } -} -static void Fold4glDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], - Accessor &styler) { - FoldNoBox4glDoc(startPos, length, initStyle, styler); -} +void SCI_METHOD LexerABL::Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) { + + if (!options.fold) + return; -static const char * const FglWordLists[] = { - "Primary keywords and identifiers", - "Secondary keywords and identifiers", - "Documentation comment keywords", - "Unused", - "Global classes and typedefs", - 0, - }; + LexAccessor styler(pAccess); + + Sci_PositionU endPos = startPos + length; + int visibleChars = 0; + Sci_Position lineCurrent = styler.GetLine(startPos); + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) + levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; + Sci_PositionU lineStartNext = styler.LineStart(lineCurrent+1); + int levelNext = levelCurrent; + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + for (Sci_PositionU i = startPos; i < endPos; i++) { + chNext = static_cast<char>(tolower(chNext)); // check tolower + char ch = chNext; + chNext = styler.SafeGetCharAt(i+1); + int stylePrev = style; + style = styleNext; + styleNext = styler.StyleAt(i+1); + bool atEOL = i == (lineStartNext-1); + if (options.foldComment && options.foldCommentMultiline && IsStreamCommentStyle(style)) { + if (!IsStreamCommentStyle(stylePrev)) { + levelNext++; + } else if (!IsStreamCommentStyle(styleNext) && !atEOL) { + // Comments don't end at end of line and the next character may be unstyled. + levelNext--; + } + } + if (options.foldSyntaxBased) { + if (style == SCE_ABL_BLOCK && !IsAlphaNumeric(chNext)) { + levelNext++; + } + else if (style == SCE_ABL_END && (ch == 'e' || ch == 'f')) { + levelNext--; + } + } + if (!IsASpace(ch)) + visibleChars++; + if (atEOL || (i == endPos-1)) { + int lev = levelCurrent | levelNext << 16; + if (visibleChars == 0 && options.foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + if (levelCurrent < levelNext) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + lineStartNext = styler.LineStart(lineCurrent+1); + levelCurrent = levelNext; + 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(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG); + } + visibleChars = 0; + } + } +} -LexerModule lmProgress(SCLEX_PROGRESS, Colourise4glDoc, "progress", Fold4glDoc, FglWordLists); +LexerModule lmProgress(SCLEX_PROGRESS, LexerABL::LexerFactoryABL, "abl", ablWordLists); |