diff --git a/lexilla/Lexilla.vcxproj b/lexilla/Lexilla.vcxproj index dd2e7b6bf..41fbde2be 100644 --- a/lexilla/Lexilla.vcxproj +++ b/lexilla/Lexilla.vcxproj @@ -70,7 +70,6 @@ - @@ -94,6 +93,7 @@ + diff --git a/lexilla/Lexilla.vcxproj.filters b/lexilla/Lexilla.vcxproj.filters index b9ef5702e..80c867fcc 100644 --- a/lexilla/Lexilla.vcxproj.filters +++ b/lexilla/Lexilla.vcxproj.filters @@ -152,9 +152,6 @@ lexers - - lexers - lexers @@ -266,6 +263,9 @@ lexers + + lexers_x + diff --git a/lexilla/lexers/LexBatch.cxx b/lexilla/lexers/LexBatch.cxx index 123ed712d..e95912c54 100644 --- a/lexilla/lexers/LexBatch.cxx +++ b/lexilla/lexers/LexBatch.cxx @@ -500,6 +500,14 @@ static void ColouriseBatchDoc( styler.ColourTo(startLine + offset - 1 - (wbl - 3), SCE_BAT_IDENTIFIER); // Reset Offset to re-process remainder of word offset -= (wbl - 3); + // escaped % + } else if ( + (wbl > 1) && + (wordBuffer[1] == '%')) { + + // Reset Offset to re-process remainder of word + styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_DEFAULT); + offset -= (wbl - 2); } // Check for Environment Variable (!x...!) } else if (wordBuffer[0] == '!') { diff --git a/lexilla/lexers_x/LexJSON.cxx b/lexilla/lexers_x/LexJSON.cxx index 0a4357c5c..9a0d6d9e4 100644 --- a/lexilla/lexers_x/LexJSON.cxx +++ b/lexilla/lexers_x/LexJSON.cxx @@ -30,8 +30,8 @@ #include "CharSetX.h" #include "SciXLexer.h" -using namespace Lexilla; using namespace Scintilla; +using namespace Lexilla; static const char *const JSONWordListDesc[] = { "JSON Keywords", @@ -590,4 +590,7 @@ void SCI_METHOD LexerJSON::Fold(Sci_PositionU startPos, } } -LexerModule lmJSON(SCLEX_JSON, LexerJSON::LexerFactoryJSON, "json", JSONWordListDesc); +LexerModule lmJSON(SCLEX_JSON, + LexerJSON::LexerFactoryJSON, + "json", + JSONWordListDesc); diff --git a/lexilla/lexers_x/LexMarkdown.cxx b/lexilla/lexers_x/LexMarkdown.cxx new file mode 100644 index 000000000..3fe5b4f19 --- /dev/null +++ b/lexilla/lexers_x/LexMarkdown.cxx @@ -0,0 +1,442 @@ +/****************************************************************** + * LexMarkdown.cxx + * + * A simple Markdown lexer for scintilla. + * + * Includes highlighting for some extra features from the + * Pandoc implementation; strikeout, using '#.' as a default + * ordered list item marker, and delimited code blocks. + * + * Limitations: + * + * Standard indented code blocks are not highlighted at all, + * as it would conflict with other indentation schemes. Use + * delimited code blocks for blanket highlighting of an + * entire code block. Embedded HTML is not highlighted either. + * Blanket HTML highlighting has issues, because some Markdown + * implementations allow Markdown markup inside of the HTML. Also, + * there is a following blank line issue that can't be ignored, + * explained in the next paragraph. Embedded HTML and code + * blocks would be better supported with language specific + * highlighting. + * + * The highlighting aims to accurately reflect correct syntax, + * but a few restrictions are relaxed. Delimited code blocks are + * highlighted, even if the line following the code block is not blank. + * Requiring a blank line after a block, breaks the highlighting + * in certain cases, because of the way Scintilla ends up calling + * the lexer. + * + * Written by Jon Strait - jstrait@moonloop.net + * + * The License.txt file describes the conditions under which this + * software may be distributed. + * + *****************************************************************/ + +#include +#include +#include +#include +#include + +#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 inline bool IsNewline(const int ch) { + return (ch == '\n' || ch == '\r'); +} + +// True if can follow ch down to the end with possibly trailing whitespace +static bool FollowToLineEnd(const int ch, const int state, const Sci_PositionU endPos, StyleContext &sc) { + Sci_PositionU i = 0; + while (sc.GetRelative(++i) == ch) + ; + // Skip over whitespace + while (IsASpaceOrTab(sc.GetRelative(i)) && sc.currentPos + i < endPos) + ++i; + if (IsNewline(sc.GetRelative(i)) || sc.currentPos + i == endPos) { + sc.Forward(i); + sc.ChangeState(state); + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + return true; + } + else return false; +} + +#if 0 + +// Set the state on text section from current to length characters, +// then set the rest until the newline to default, except for any characters matching token +static void SetStateAndZoom(const int state, const Sci_Position length, const int token, StyleContext &sc) { + sc.SetState(state); + sc.Forward(length); + sc.SetState(SCE_MARKDOWN_DEFAULT); + sc.Forward(); + bool started = false; + while (sc.More() && !IsNewline(sc.ch)) { + if (sc.ch == token && !started) { + sc.SetState(state); + started = true; + } + else if (sc.ch != token) { + sc.SetState(SCE_MARKDOWN_DEFAULT); + started = false; + } + sc.Forward(); + } + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); +} + +#else + +static void SetStateAndZoom(const int state, const Sci_Position length, const int token, StyleContext& sc) { + sc.SetState(state); + sc.Forward(length); + bool started = false; + while (sc.More() && !sc.atLineStart) { + if (sc.ch == token) { + sc.SetState(state); + started = !started; + } + else { + sc.SetState(started ? SCE_MARKDOWN_HDRTEXT : state); + } + sc.Forward(); + } + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); +} + +#endif + +// Does the previous line have more than spaces and tabs? +static bool HasPrevLineContent(StyleContext &sc) { + Sci_Position i = 0; + // Go back to the previous newline + while ((--i + (Sci_Position)sc.currentPos) >= 0 && !IsNewline(sc.GetRelative(i))) + ; + while ((--i + (Sci_Position)sc.currentPos) >= 0) { + const int ch = sc.GetRelative(i); + if (ch == '\n') + break; + if (!((ch == '\r' || IsASpaceOrTab(ch)))) + return true; + } + return false; +} + +static bool AtTermStart(StyleContext &sc) { + return sc.currentPos == 0 || sc.chPrev == 0 || isspacechar(sc.chPrev); +} + +static bool IsValidHrule(const Sci_PositionU endPos, StyleContext &sc) { + int count = 1; + Sci_PositionU i = 0; + for (;;) { + ++i; + int c = sc.GetRelative(i); + if (c == sc.ch) + ++count; + // hit a terminating character + else if (!IsASpaceOrTab(c) || sc.currentPos + i == endPos) { + // Are we a valid HRULE + if ((IsNewline(c) || sc.currentPos + i == endPos) && + count >= 3 && !HasPrevLineContent(sc)) { + sc.SetState(SCE_MARKDOWN_HRULE); + sc.Forward(i); + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + return true; + } + else { + sc.SetState(SCE_MARKDOWN_DEFAULT); + return false; + } + } + } +} + +static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, + WordList **, Accessor &styler) { + Sci_PositionU endPos = startPos + length; + int precharCount = 0; + bool isLinkNameDetecting = false; + // Don't advance on a new loop iteration and retry at the same position. + // Useful in the corner case of having to start at the beginning file position + // in the default state. + bool freezeCursor = false; + + StyleContext sc(startPos, length, initStyle, styler); + + while (sc.More()) { + // Skip past escaped characters + if (sc.ch == '\\') { + sc.Forward(); + continue; + } + + // A blockquotes resets the line semantics + if (sc.state == SCE_MARKDOWN_BLOCKQUOTE) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + + // Conditional state-based actions + if (sc.state == SCE_MARKDOWN_CODE2) { + if (sc.Match("``") && sc.GetRelative(-2) != ' ') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_CODE) { + if (sc.ch == '`' && sc.chPrev != ' ') + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + /* De-activated because it gets in the way of other valid indentation + * schemes, for example multiple paragraphs inside a list item. + // Code block + else if (sc.state == SCE_MARKDOWN_CODEBK) { + bool d = true; + if (IsNewline(sc.ch)) { + if (sc.chNext != '\t') { + for (int c = 1; c < 5; ++c) { + if (sc.GetRelative(c) != ' ') + d = false; + } + } + } + else if (sc.atLineStart) { + if (sc.ch != '\t' ) { + for (int i = 0; i < 4; ++i) { + if (sc.GetRelative(i) != ' ') + d = false; + } + } + } + if (!d) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + */ + // Strong + else if (sc.state == SCE_MARKDOWN_STRONG1) { + if (sc.Match("**") && sc.chPrev != ' ') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_STRONG2) { + if (sc.Match("__") && sc.chPrev != ' ') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + // Emphasis + else if (sc.state == SCE_MARKDOWN_EM1) { + if (sc.ch == '*' && sc.chPrev != ' ') + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.state == SCE_MARKDOWN_EM2) { + if (sc.ch == '_' && sc.chPrev != ' ') + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.state == SCE_MARKDOWN_CODEBK) { + if (sc.atLineStart && sc.Match("~~~")) { + Sci_Position i = 1; + while (!IsNewline(sc.GetRelative(i)) && sc.currentPos + i < endPos) + i++; + sc.Forward(i); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_STRIKEOUT) { + if (sc.Match("~~") && sc.chPrev != ' ') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_LINE_BEGIN) { + // Header + if (sc.Match("######")) + SetStateAndZoom(SCE_MARKDOWN_HEADER6, 6, '#', sc); + else if (sc.Match("#####")) + SetStateAndZoom(SCE_MARKDOWN_HEADER5, 5, '#', sc); + else if (sc.Match("####")) + SetStateAndZoom(SCE_MARKDOWN_HEADER4, 4, '#', sc); + else if (sc.Match("###")) + SetStateAndZoom(SCE_MARKDOWN_HEADER3, 3, '#', sc); + else if (sc.Match("##")) + SetStateAndZoom(SCE_MARKDOWN_HEADER2, 2, '#', sc); + else if (sc.Match("#")) { + // Catch the special case of an unordered list + if (sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) { + precharCount = 0; + sc.SetState(SCE_MARKDOWN_PRECHAR); + } + else + SetStateAndZoom(SCE_MARKDOWN_HEADER1, 1, '#', sc); + } + // Code block + else if (sc.Match("~~~")) { + if (!HasPrevLineContent(sc)) + sc.SetState(SCE_MARKDOWN_CODEBK); + else + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.ch == '=') { + if (HasPrevLineContent(sc) && FollowToLineEnd('=', SCE_MARKDOWN_HEADER1, endPos, sc)) + ; + else + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.ch == '-') { + if (HasPrevLineContent(sc) && FollowToLineEnd('-', SCE_MARKDOWN_HEADER2, endPos, sc)) + ; + else { + precharCount = 0; + sc.SetState(SCE_MARKDOWN_PRECHAR); + } + } + else if (IsNewline(sc.ch)) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + else { + precharCount = 0; + sc.SetState(SCE_MARKDOWN_PRECHAR); + } + } + + // The header lasts until the newline + else if (sc.state == SCE_MARKDOWN_HEADER1 || sc.state == SCE_MARKDOWN_HEADER2 || + sc.state == SCE_MARKDOWN_HEADER3 || sc.state == SCE_MARKDOWN_HEADER4 || + sc.state == SCE_MARKDOWN_HEADER5 || sc.state == SCE_MARKDOWN_HEADER6) { + if (IsNewline(sc.ch)) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + + // New state only within the initial whitespace + if (sc.state == SCE_MARKDOWN_PRECHAR) { + // Blockquote + if (sc.ch == '>' && precharCount < 5) + sc.SetState(SCE_MARKDOWN_BLOCKQUOTE); + /* + // Begin of code block + else if (!HasPrevLineContent(sc) && (sc.chPrev == '\t' || precharCount >= 4)) + sc.SetState(SCE_MARKDOWN_CODEBK); + */ + // HRule - Total of three or more hyphens, asterisks, or underscores + // on a line by themselves + else if ((sc.ch == '-' || sc.ch == '*' || sc.ch == '_') && IsValidHrule(endPos, sc)) + ; + // Unordered list + else if ((sc.ch == '-' || sc.ch == '*' || sc.ch == '+') && IsASpaceOrTab(sc.chNext)) { + sc.SetState(SCE_MARKDOWN_ULIST_ITEM); + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + // Ordered list + else if (IsADigit(sc.ch)) { + int digitCount = 0; + while (IsADigit(sc.GetRelative(++digitCount))) + ; + if (sc.GetRelative(digitCount) == '.' && + IsASpaceOrTab(sc.GetRelative(digitCount + 1))) { + sc.SetState(SCE_MARKDOWN_OLIST_ITEM); + sc.Forward(digitCount + 1); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + // Alternate Ordered list + else if (sc.ch == '#' && sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) { + sc.SetState(SCE_MARKDOWN_OLIST_ITEM); + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.ch != ' ' || precharCount > 2) + sc.SetState(SCE_MARKDOWN_DEFAULT); + else + ++precharCount; + } + + // Any link + if (sc.state == SCE_MARKDOWN_LINK) { + if (sc.Match("](") && sc.GetRelative(-1) != '\\') { + sc.Forward(2); + isLinkNameDetecting = true; + } + else if (sc.Match("]:") && sc.GetRelative(-1) != '\\') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (!isLinkNameDetecting && sc.ch == ']' && sc.GetRelative(-1) != '\\') { + sc.Forward(); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (isLinkNameDetecting && sc.ch == ')' && sc.GetRelative(-1) != '\\') { + sc.Forward(); + sc.SetState(SCE_MARKDOWN_DEFAULT); + isLinkNameDetecting = false; + } + } + + // New state anywhere in doc + if (sc.state == SCE_MARKDOWN_DEFAULT) { + if (sc.atLineStart && sc.ch == '#') { + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + freezeCursor = true; + } + // Links and Images + if (sc.Match("![")) { + sc.SetState(SCE_MARKDOWN_LINK); + sc.Forward(1); + } + else if (sc.ch == '[' && sc.GetRelative(-1) != '\\') { + sc.SetState(SCE_MARKDOWN_LINK); + } + // Code - also a special case for alternate inside spacing + else if (sc.Match("``") && sc.GetRelative(3) != ' ' && AtTermStart(sc)) { + sc.SetState(SCE_MARKDOWN_CODE2); + sc.Forward(); + } + else if (sc.ch == '`' && sc.chNext != ' ' && AtTermStart(sc)) { + sc.SetState(SCE_MARKDOWN_CODE); + } + // Strong + else if (sc.Match("**") && sc.GetRelative(2) != ' ' && AtTermStart(sc)) { + sc.SetState(SCE_MARKDOWN_STRONG1); + sc.Forward(); + } + else if (sc.Match("__") && sc.GetRelative(2) != ' ' && AtTermStart(sc)) { + sc.SetState(SCE_MARKDOWN_STRONG2); + sc.Forward(); + } + // Emphasis + else if (sc.ch == '*' && sc.chNext != ' ' && AtTermStart(sc)) { + sc.SetState(SCE_MARKDOWN_EM1); + } + else if (sc.ch == '_' && sc.chNext != ' ' && AtTermStart(sc)) { + sc.SetState(SCE_MARKDOWN_EM2); + } + // Strikeout + else if (sc.Match("~~") && sc.GetRelative(2) != ' ' && AtTermStart(sc)) { + sc.SetState(SCE_MARKDOWN_STRIKEOUT); + sc.Forward(); + } + // Beginning of line + else if (IsNewline(sc.ch)) { + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + } + // Advance if not holding back the cursor for this iteration. + if (!freezeCursor) + sc.Forward(); + freezeCursor = false; + } + sc.Complete(); +} + +LexerModule lmMarkdown(SCLEX_MARKDOWN, ColorizeMarkdownDoc, "markdown"); diff --git a/lexilla/lexers_x/LexPython.cxx b/lexilla/lexers_x/LexPython.cxx index c1de871fb..47e1944c9 100644 --- a/lexilla/lexers_x/LexPython.cxx +++ b/lexilla/lexers_x/LexPython.cxx @@ -30,8 +30,8 @@ #include "SubStyles.h" #include "DefaultLexer.h" -using namespace Lexilla; using namespace Scintilla; +using namespace Lexilla; namespace { // Use an unnamed namespace to protect the functions and classes from name conflicts diff --git a/lexilla/lexers_x/orig/LexJSON.cxx b/lexilla/lexers_x/orig/LexJSON.cxx new file mode 100644 index 000000000..d62508c14 --- /dev/null +++ b/lexilla/lexers_x/orig/LexJSON.cxx @@ -0,0 +1,503 @@ +// Scintilla source code edit control +/** + * @file LexJSON.cxx + * @date February 19, 2016 + * @brief Lexer for JSON and JSON-LD formats + * @author nkmathew + * + * The License.txt file describes the conditions under which this software may + * be distributed. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ILexer.h" +#include "Scintilla.h" +#include "SciLexer.h" +#include "WordList.h" +#include "LexAccessor.h" +#include "StyleContext.h" +#include "CharacterSet.h" +#include "LexerModule.h" +#include "OptionSet.h" +#include "DefaultLexer.h" + +using namespace Scintilla; +using namespace Lexilla; + +static const char *const JSONWordListDesc[] = { + "JSON Keywords", + "JSON-LD Keywords", + 0 +}; + +/** + * Used to detect compact IRI/URLs in JSON-LD without first looking ahead for the + * colon separating the prefix and suffix + * + * https://www.w3.org/TR/json-ld/#dfn-compact-iri + */ +struct CompactIRI { + int colonCount; + bool foundInvalidChar; + CharacterSet setCompactIRI; + CompactIRI() { + colonCount = 0; + foundInvalidChar = false; + setCompactIRI = CharacterSet(CharacterSet::setAlpha, "$_-"); + } + void resetState() { + colonCount = 0; + foundInvalidChar = false; + } + void checkChar(int ch) { + if (ch == ':') { + colonCount++; + } else { + foundInvalidChar |= !setCompactIRI.Contains(ch); + } + } + bool shouldHighlight() const { + return !foundInvalidChar && colonCount == 1; + } +}; + +/** + * Keeps track of escaped characters in strings as per: + * + * https://tools.ietf.org/html/rfc7159#section-7 + */ +struct EscapeSequence { + int digitsLeft; + CharacterSet setHexDigits; + CharacterSet setEscapeChars; + EscapeSequence() { + digitsLeft = 0; + setHexDigits = CharacterSet(CharacterSet::setDigits, "ABCDEFabcdef"); + setEscapeChars = CharacterSet(CharacterSet::setNone, "\\\"tnbfru/"); + } + // Returns true if the following character is a valid escaped character + bool newSequence(int nextChar) { + digitsLeft = 0; + if (nextChar == 'u') { + digitsLeft = 5; + } else if (!setEscapeChars.Contains(nextChar)) { + return false; + } + return true; + } + bool atEscapeEnd() const { + return digitsLeft <= 0; + } + bool isInvalidChar(int currChar) const { + return !setHexDigits.Contains(currChar); + } +}; + +struct OptionsJSON { + bool foldCompact; + bool fold; + bool allowComments; + bool escapeSequence; + OptionsJSON() { + foldCompact = false; + fold = false; + allowComments = false; + escapeSequence = false; + } +}; + +struct OptionSetJSON : public OptionSet { + OptionSetJSON() { + DefineProperty("lexer.json.escape.sequence", &OptionsJSON::escapeSequence, + "Set to 1 to enable highlighting of escape sequences in strings"); + + DefineProperty("lexer.json.allow.comments", &OptionsJSON::allowComments, + "Set to 1 to enable highlighting of line/block comments in JSON"); + + DefineProperty("fold.compact", &OptionsJSON::foldCompact); + DefineProperty("fold", &OptionsJSON::fold); + DefineWordListSets(JSONWordListDesc); + } +}; + +class LexerJSON : public DefaultLexer { + OptionsJSON options; + OptionSetJSON optSetJSON; + EscapeSequence escapeSeq; + WordList keywordsJSON; + WordList keywordsJSONLD; + CharacterSet setOperators; + CharacterSet setURL; + CharacterSet setKeywordJSONLD; + CharacterSet setKeywordJSON; + CompactIRI compactIRI; + + static bool IsNextNonWhitespace(LexAccessor &styler, Sci_Position start, char ch) { + Sci_Position i = 0; + while (i < 50) { + i++; + char curr = styler.SafeGetCharAt(start+i, '\0'); + char next = styler.SafeGetCharAt(start+i+1, '\0'); + bool atEOL = (curr == '\r' && next != '\n') || (curr == '\n'); + if (curr == ch) { + return true; + } else if (!isspacechar(curr) || atEOL) { + return false; + } + } + return false; + } + + /** + * Looks for the colon following the end quote + * + * Assumes property names of lengths no longer than a 100 characters. + * The colon is also expected to be less than 50 spaces after the end + * quote for the string to be considered a property name + */ + static bool AtPropertyName(LexAccessor &styler, Sci_Position start) { + Sci_Position i = 0; + bool escaped = false; + while (i < 100) { + i++; + char curr = styler.SafeGetCharAt(start+i, '\0'); + if (escaped) { + escaped = false; + continue; + } + escaped = curr == '\\'; + if (curr == '"') { + return IsNextNonWhitespace(styler, start+i, ':'); + } else if (!curr) { + return false; + } + } + return false; + } + + static bool IsNextWordInList(WordList &keywordList, CharacterSet wordSet, + StyleContext &context, LexAccessor &styler) { + char word[51]; + Sci_Position currPos = (Sci_Position) context.currentPos; + int i = 0; + while (i < 50) { + char ch = styler.SafeGetCharAt(currPos + i); + if (!wordSet.Contains(ch)) { + break; + } + word[i] = ch; + i++; + } + word[i] = '\0'; + return keywordList.InList(word); + } + + public: + LexerJSON() : + DefaultLexer("json", SCLEX_JSON), + setOperators(CharacterSet::setNone, "[{}]:,"), + setURL(CharacterSet::setAlphaNum, "-._~:/?#[]@!$&'()*+,),="), + setKeywordJSONLD(CharacterSet::setAlpha, ":@"), + setKeywordJSON(CharacterSet::setAlpha, "$_") { + } + virtual ~LexerJSON() {} + int SCI_METHOD Version() const override { + return lvRelease5; + } + void SCI_METHOD Release() override { + delete this; + } + const char *SCI_METHOD PropertyNames() override { + return optSetJSON.PropertyNames(); + } + int SCI_METHOD PropertyType(const char *name) override { + return optSetJSON.PropertyType(name); + } + const char *SCI_METHOD DescribeProperty(const char *name) override { + return optSetJSON.DescribeProperty(name); + } + Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override { + if (optSetJSON.PropertySet(&options, key, val)) { + return 0; + } + return -1; + } + const char * SCI_METHOD PropertyGet(const char *key) override { + return optSetJSON.PropertyGet(key); + } + Sci_Position SCI_METHOD WordListSet(int n, const char *wl) override { + WordList *wordListN = 0; + switch (n) { + case 0: + wordListN = &keywordsJSON; + break; + case 1: + wordListN = &keywordsJSONLD; + break; + } + Sci_Position firstModification = -1; + if (wordListN) { + WordList wlNew; + wlNew.Set(wl); + if (*wordListN != wlNew) { + wordListN->Set(wl); + firstModification = 0; + } + } + return firstModification; + } + void *SCI_METHOD PrivateCall(int, void *) override { + return 0; + } + static ILexer5 *LexerFactoryJSON() { + return new LexerJSON; + } + const char *SCI_METHOD DescribeWordListSets() override { + return optSetJSON.DescribeWordListSets(); + } + 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 LexerJSON::Lex(Sci_PositionU startPos, + Sci_Position length, + int initStyle, + IDocument *pAccess) { + LexAccessor styler(pAccess); + StyleContext context(startPos, length, initStyle, styler); + int stringStyleBefore = SCE_JSON_STRING; + while (context.More()) { + switch (context.state) { + case SCE_JSON_BLOCKCOMMENT: + if (context.Match("*/")) { + context.Forward(); + context.ForwardSetState(SCE_JSON_DEFAULT); + } + break; + case SCE_JSON_LINECOMMENT: + if (context.atLineEnd) { + context.SetState(SCE_JSON_DEFAULT); + } + break; + case SCE_JSON_STRINGEOL: + if (context.atLineStart) { + context.SetState(SCE_JSON_DEFAULT); + } + break; + case SCE_JSON_ESCAPESEQUENCE: + escapeSeq.digitsLeft--; + if (!escapeSeq.atEscapeEnd()) { + if (escapeSeq.isInvalidChar(context.ch)) { + context.SetState(SCE_JSON_ERROR); + } + break; + } + if (context.ch == '"') { + context.SetState(stringStyleBefore); + context.ForwardSetState(SCE_C_DEFAULT); + } else if (context.ch == '\\') { + if (!escapeSeq.newSequence(context.chNext)) { + context.SetState(SCE_JSON_ERROR); + } + context.Forward(); + } else { + context.SetState(stringStyleBefore); + if (context.atLineEnd) { + context.ChangeState(SCE_JSON_STRINGEOL); + } + } + break; + case SCE_JSON_PROPERTYNAME: + case SCE_JSON_STRING: + if (context.ch == '"') { + if (compactIRI.shouldHighlight()) { + context.ChangeState(SCE_JSON_COMPACTIRI); + context.ForwardSetState(SCE_JSON_DEFAULT); + compactIRI.resetState(); + } else { + context.ForwardSetState(SCE_JSON_DEFAULT); + } + } else if (context.atLineEnd) { + context.ChangeState(SCE_JSON_STRINGEOL); + } else if (context.ch == '\\') { + stringStyleBefore = context.state; + if (options.escapeSequence) { + context.SetState(SCE_JSON_ESCAPESEQUENCE); + if (!escapeSeq.newSequence(context.chNext)) { + context.SetState(SCE_JSON_ERROR); + } + } + context.Forward(); + } else if (context.Match("https://") || + context.Match("http://") || + context.Match("ssh://") || + context.Match("git://") || + context.Match("svn://") || + context.Match("ftp://") || + context.Match("mailto:")) { + // Handle most common URI schemes only + stringStyleBefore = context.state; + context.SetState(SCE_JSON_URI); + } else if (context.ch == '@') { + // https://www.w3.org/TR/json-ld/#dfn-keyword + if (IsNextWordInList(keywordsJSONLD, setKeywordJSONLD, context, styler)) { + stringStyleBefore = context.state; + context.SetState(SCE_JSON_LDKEYWORD); + } + } else { + compactIRI.checkChar(context.ch); + } + break; + case SCE_JSON_LDKEYWORD: + case SCE_JSON_URI: + if ((!setKeywordJSONLD.Contains(context.ch) && + (context.state == SCE_JSON_LDKEYWORD)) || + (!setURL.Contains(context.ch))) { + context.SetState(stringStyleBefore); + } + if (context.ch == '"') { + context.ForwardSetState(SCE_JSON_DEFAULT); + } else if (context.atLineEnd) { + context.ChangeState(SCE_JSON_STRINGEOL); + } + break; + case SCE_JSON_OPERATOR: + case SCE_JSON_NUMBER: + context.SetState(SCE_JSON_DEFAULT); + break; + case SCE_JSON_ERROR: + if (context.atLineEnd) { + context.SetState(SCE_JSON_DEFAULT); + } + break; + case SCE_JSON_KEYWORD: + if (!setKeywordJSON.Contains(context.ch)) { + context.SetState(SCE_JSON_DEFAULT); + } + break; + } + if (context.state == SCE_JSON_DEFAULT) { + if (context.ch == '"') { + compactIRI.resetState(); + context.SetState(SCE_JSON_STRING); + Sci_Position currPos = static_cast(context.currentPos); + if (AtPropertyName(styler, currPos)) { + context.SetState(SCE_JSON_PROPERTYNAME); + } + } else if (setOperators.Contains(context.ch)) { + context.SetState(SCE_JSON_OPERATOR); + } else if (options.allowComments && context.Match("/*")) { + context.SetState(SCE_JSON_BLOCKCOMMENT); + context.Forward(); + } else if (options.allowComments && context.Match("//")) { + context.SetState(SCE_JSON_LINECOMMENT); + } else if (setKeywordJSON.Contains(context.ch)) { + if (IsNextWordInList(keywordsJSON, setKeywordJSON, context, styler)) { + context.SetState(SCE_JSON_KEYWORD); + } + } + bool numberStart = + IsADigit(context.ch) && (context.chPrev == '+'|| + context.chPrev == '-' || + context.atLineStart || + IsASpace(context.chPrev) || + setOperators.Contains(context.chPrev)); + bool exponentPart = + tolower(context.ch) == 'e' && + IsADigit(context.chPrev) && + (IsADigit(context.chNext) || + context.chNext == '+' || + context.chNext == '-'); + bool signPart = + (context.ch == '-' || context.ch == '+') && + ((tolower(context.chPrev) == 'e' && IsADigit(context.chNext)) || + ((IsASpace(context.chPrev) || setOperators.Contains(context.chPrev)) + && IsADigit(context.chNext))); + bool adjacentDigit = + IsADigit(context.ch) && IsADigit(context.chPrev); + bool afterExponent = IsADigit(context.ch) && tolower(context.chPrev) == 'e'; + bool dotPart = context.ch == '.' && + IsADigit(context.chPrev) && + IsADigit(context.chNext); + bool afterDot = IsADigit(context.ch) && context.chPrev == '.'; + if (numberStart || + exponentPart || + signPart || + adjacentDigit || + dotPart || + afterExponent || + afterDot) { + context.SetState(SCE_JSON_NUMBER); + } else if (context.state == SCE_JSON_DEFAULT && !IsASpace(context.ch)) { + context.SetState(SCE_JSON_ERROR); + } + } + context.Forward(); + } + context.Complete(); +} + +void SCI_METHOD LexerJSON::Fold(Sci_PositionU startPos, + Sci_Position length, + int, + IDocument *pAccess) { + if (!options.fold) { + return; + } + LexAccessor styler(pAccess); + Sci_PositionU currLine = styler.GetLine(startPos); + Sci_PositionU endPos = startPos + length; + int currLevel = SC_FOLDLEVELBASE; + if (currLine > 0) + currLevel = styler.LevelAt(currLine - 1) >> 16; + int nextLevel = currLevel; + int visibleChars = 0; + for (Sci_PositionU i = startPos; i < endPos; i++) { + char curr = styler.SafeGetCharAt(i); + char next = styler.SafeGetCharAt(i+1); + bool atEOL = (curr == '\r' && next != '\n') || (curr == '\n'); + if (styler.StyleAt(i) == SCE_JSON_OPERATOR) { + if (curr == '{' || curr == '[') { + nextLevel++; + } else if (curr == '}' || curr == ']') { + nextLevel--; + } + } + if (atEOL || i == (endPos-1)) { + int level = currLevel | nextLevel << 16; + if (!visibleChars && options.foldCompact) { + level |= SC_FOLDLEVELWHITEFLAG; + } else if (nextLevel > currLevel) { + level |= SC_FOLDLEVELHEADERFLAG; + } + if (level != styler.LevelAt(currLine)) { + styler.SetLevel(currLine, level); + } + currLine++; + currLevel = nextLevel; + visibleChars = 0; + } + if (!isspacechar(curr)) { + visibleChars++; + } + } +} + +LexerModule lmJSON(SCLEX_JSON, + LexerJSON::LexerFactoryJSON, + "json", + JSONWordListDesc); diff --git a/lexilla/lexers/LexMarkdown.cxx b/lexilla/lexers_x/orig/LexMarkdown.cxx similarity index 100% rename from lexilla/lexers/LexMarkdown.cxx rename to lexilla/lexers_x/orig/LexMarkdown.cxx diff --git a/lexilla/lexers_x/orig/LexPython.cxx b/lexilla/lexers_x/orig/LexPython.cxx index d5ec1d558..3a326c93e 100644 --- a/lexilla/lexers_x/orig/LexPython.cxx +++ b/lexilla/lexers_x/orig/LexPython.cxx @@ -31,6 +31,7 @@ #include "DefaultLexer.h" using namespace Scintilla; +using namespace Lexilla; namespace { // Use an unnamed namespace to protect the functions and classes from name conflicts diff --git a/lexilla/src/deps.mak b/lexilla/src/deps.mak index 0117c408f..219383f3c 100644 --- a/lexilla/src/deps.mak +++ b/lexilla/src/deps.mak @@ -333,18 +333,6 @@ LexMake.o: \ ../lexlib/StyleContext.h \ ../lexlib/CharacterSet.h \ ../lexlib/LexerModule.h -LexMarkdown.o: \ - ../lexers/LexMarkdown.cxx \ - ../../scintilla/include/ILexer.h \ - ../../scintilla/include/Sci_Position.h \ - ../../scintilla/include/Scintilla.h \ - ../include/SciLexer.h \ - ../lexlib/WordList.h \ - ../lexlib/LexAccessor.h \ - ../lexlib/Accessor.h \ - ../lexlib/StyleContext.h \ - ../lexlib/CharacterSet.h \ - ../lexlib/LexerModule.h LexMatlab.o: \ ../lexers/LexMatlab.cxx \ ../../scintilla/include/ILexer.h \ @@ -662,6 +650,18 @@ LexKotlin.o: \ ../include/SciLexer.h \ ../lexers_x/StringUtils.h \ ../lexers_x/LexerUtils.h +LexMarkdown.o: \ + ../lexers_x/LexMarkdown.cxx \ + ../../scintilla/include/ILexer.h \ + ../../scintilla/include/Sci_Position.h \ + ../../scintilla/include/Scintilla.h \ + ../include/SciLexer.h \ + ../lexlib/WordList.h \ + ../lexlib/LexAccessor.h \ + ../lexlib/Accessor.h \ + ../lexlib/StyleContext.h \ + ../lexlib/CharacterSet.h \ + ../lexlib/LexerModule.h LexPython.o: \ ../lexers_x/LexPython.cxx \ ../../scintilla/include/ILexer.h \ diff --git a/lexilla/src/lexilla.mak b/lexilla/src/lexilla.mak index f3ed76304..372da4f11 100644 --- a/lexilla/src/lexilla.mak +++ b/lexilla/src/lexilla.mak @@ -92,7 +92,6 @@ LEX_OBJS=\ $(DIR_O)\LexLaTeX.obj \ $(DIR_O)\LexLua.obj \ $(DIR_O)\LexMake.obj \ - $(DIR_O)\LexMarkdown.obj \ $(DIR_O)\LexMatlab.obj \ $(DIR_O)\LexNim.obj \ $(DIR_O)\LexNsis.obj \ @@ -116,6 +115,7 @@ LEX_OBJS=\ $(DIR_O)\LexerUtils.obj \ $(DIR_O)\LexJSON.obj \ $(DIR_O)\LexKotlin.obj \ + $(DIR_O)\LexMarkdown.obj \ $(DIR_O)\LexPython.obj \ $(DIR_O)\LexTOML.obj \ diff --git a/lexilla/src/nmdeps.mak b/lexilla/src/nmdeps.mak index eedf90866..40ef90ac3 100644 --- a/lexilla/src/nmdeps.mak +++ b/lexilla/src/nmdeps.mak @@ -333,18 +333,6 @@ $(DIR_O)/LexMake.obj: \ ../lexlib/StyleContext.h \ ../lexlib/CharacterSet.h \ ../lexlib/LexerModule.h -$(DIR_O)/LexMarkdown.obj: \ - ../lexers/LexMarkdown.cxx \ - ../../scintilla/include/ILexer.h \ - ../../scintilla/include/Sci_Position.h \ - ../../scintilla/include/Scintilla.h \ - ../include/SciLexer.h \ - ../lexlib/WordList.h \ - ../lexlib/LexAccessor.h \ - ../lexlib/Accessor.h \ - ../lexlib/StyleContext.h \ - ../lexlib/CharacterSet.h \ - ../lexlib/LexerModule.h $(DIR_O)/LexMatlab.obj: \ ../lexers/LexMatlab.cxx \ ../../scintilla/include/ILexer.h \ @@ -662,6 +650,18 @@ $(DIR_O)/LexKotlin.obj: \ ../include/SciLexer.h \ ../lexers_x/StringUtils.h \ ../lexers_x/LexerUtils.h +$(DIR_O)/LexMarkdown.obj: \ + ../lexers_x/LexMarkdown.cxx \ + ../../scintilla/include/ILexer.h \ + ../../scintilla/include/Sci_Position.h \ + ../../scintilla/include/Scintilla.h \ + ../include/SciLexer.h \ + ../lexlib/WordList.h \ + ../lexlib/LexAccessor.h \ + ../lexlib/Accessor.h \ + ../lexlib/StyleContext.h \ + ../lexlib/CharacterSet.h \ + ../lexlib/LexerModule.h $(DIR_O)/LexPython.obj: \ ../lexers_x/LexPython.cxx \ ../../scintilla/include/ILexer.h \