diff --git a/lexilla/lexers_x/LexJSON.cxx b/lexilla/lexers_x/LexJSON.cxx index 0fa412b5b..12415641e 100644 --- a/lexilla/lexers_x/LexJSON.cxx +++ b/lexilla/lexers_x/LexJSON.cxx @@ -111,10 +111,10 @@ struct OptionsJSON { bool allowComments; bool escapeSequence; OptionsJSON() { - foldCompact = true; - fold = true; - allowComments = true; - escapeSequence = true; + foldCompact = false; + fold = false; + allowComments = false; + escapeSequence = false; } }; diff --git a/lexilla/lexers_x/LexTOML.cxx b/lexilla/lexers_x/LexTOML.cxx index 9c3b0c469..947d4cf56 100644 --- a/lexilla/lexers_x/LexTOML.cxx +++ b/lexilla/lexers_x/LexTOML.cxx @@ -236,13 +236,13 @@ constexpr bool IsAssignChar(const int ch) noexcept } // ---------------------------------------------------------------------------- -inline bool IsAIdentifierChar(const int ch) +constexpr bool IsAIdentifierChar(const int ch) noexcept { return (IsAlphaNumeric(ch) || ch == '_' || ch == '.'); } // ---------------------------------------------------------------------------- -inline bool IsAKeywordChar(const int ch) +constexpr bool IsAKeywordChar(const int ch) noexcept { return (IsAIdentifierChar(ch) || ch == '+' || ch == '-'); } diff --git a/lexilla/lexers_x/orig/zufuliu/LexDart.cxx b/lexilla/lexers_x/orig/zufuliu/LexDart.cxx new file mode 100644 index 000000000..b509a778f --- /dev/null +++ b/lexilla/lexers_x/orig/zufuliu/LexDart.cxx @@ -0,0 +1,554 @@ +// This file is part of Notepad2. +// See License.txt for details about distribution and modification. +//! Lexer for Dart. + +#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 "StringUtils.h" +#include "LexerModule.h" +#include "LexerUtils.h" + +using namespace Lexilla; + +namespace { + +struct EscapeSequence { + int outerState = SCE_DART_DEFAULT; + int digitsLeft = 0; + bool brace = false; + + // highlight any character as escape sequence. + bool resetEscapeState(int state, int chNext) noexcept { + if (IsEOLChar(chNext)) { + return false; + } + outerState = state; + brace = false; + digitsLeft = (chNext == 'x')? 3 : ((chNext == 'u') ? 5 : 1); + return true; + } + bool atEscapeEnd(int ch) noexcept { + --digitsLeft; + return digitsLeft <= 0 || !IsHexDigit(ch); + } +}; + +enum { + DartLineStateMaskLineComment = 1, // line comment + DartLineStateMaskImport = (1 << 1), // import +}; + +//KeywordIndex++Autogenerated -- start of section automatically generated +enum { + KeywordIndex_Keyword = 0, + KeywordIndex_Type = 1, + KeywordIndex_Class = 2, + KeywordIndex_Enumeration = 3, +}; +//KeywordIndex--Autogenerated -- end of section automatically generated + +enum class KeywordType { + None = SCE_DART_DEFAULT, + Class = SCE_DART_CLASS, + Enum = SCE_DART_ENUM, + Label = SCE_DART_LABEL, + Return = 0x40, + While, +}; + +static_assert(DefaultNestedStateBaseStyle + 1 == SCE_DART_STRING_SQ); +static_assert(DefaultNestedStateBaseStyle + 2 == SCE_DART_STRING_DQ); +static_assert(DefaultNestedStateBaseStyle + 3 == SCE_DART_TRIPLE_STRING_SQ); +static_assert(DefaultNestedStateBaseStyle + 4 == SCE_DART_TRIPLE_STRING_DQ); + +constexpr bool IsDeclarableOperator(int ch) noexcept { + // https://github.com/dart-lang/sdk/blob/main/sdk/lib/core/symbol.dart + return AnyOf(ch, '+', '-', '*', '/', '%', '~', '&', '|', + '^', '<', '>', '=', '[', ']'); +} + +constexpr bool IsSpaceEquiv(int state) noexcept { + return state <= SCE_DART_TASKMARKER; +} + +void ColouriseDartDoc(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, LexerWordList keywordLists, Accessor &styler) { + int lineStateLineType = 0; + int commentLevel = 0; // nested block comment level + + KeywordType kwType = KeywordType::None; + int chBeforeIdentifier = 0; + + std::vector nestedState; // string interpolation "${}" + + int visibleChars = 0; + int chBefore = 0; + int visibleCharsBefore = 0; + int chPrevNonWhite = 0; + EscapeSequence escSeq; + + StyleContext sc(startPos, lengthDoc, initStyle, styler); + if (sc.currentLine > 0) { + int lineState = styler.GetLineState(sc.currentLine - 1); + /* + 2: lineStateLineType + 6: commentLevel + 3: nestedState count + 3*4: nestedState + */ + commentLevel = (lineState >> 2) & 0x3f; + lineState >>= 8; + if (lineState) { + UnpackLineState(lineState, nestedState); + } + } else if (startPos == 0 && sc.Match('#', '!')) { + // Shell Shebang at beginning of file + sc.SetState(SCE_DART_COMMENTLINE); + sc.Forward(); + lineStateLineType = DartLineStateMaskLineComment; + } + + while (sc.More()) { + switch (sc.state) { + case SCE_DART_OPERATOR: + case SCE_DART_OPERATOR2: + sc.SetState(SCE_DART_DEFAULT); + break; + + case SCE_DART_NUMBER: + if (!IsDecimalNumber(sc.chPrev, sc.ch, sc.chNext)) { + sc.SetState(SCE_DART_DEFAULT); + } + break; + + case SCE_DART_IDENTIFIER: + case SCE_DART_VARIABLE: + case SCE_DART_VARIABLE2: + case SCE_DART_METADATA: + case SCE_DART_SYMBOL_IDENTIFIER: + if (!IsIdentifierCharEx(sc.ch)) { + switch (sc.state) { + case SCE_DART_VARIABLE2: + sc.SetState(escSeq.outerState); + continue; + + case SCE_DART_METADATA: + case SCE_DART_SYMBOL_IDENTIFIER: + if (sc.ch == '.') { + const int state = sc.state; + sc.SetState(SCE_DART_OPERATOR); + sc.ForwardSetState(state); + continue; + } + break; + + case SCE_DART_IDENTIFIER: { + char s[128]; + sc.GetCurrent(s, sizeof(s)); + if (keywordLists[KeywordIndex_Keyword].InList(s)) { + sc.ChangeState(SCE_DART_WORD); + if (StrEqualsAny(s, "import", "part")) { + if (visibleChars == sc.LengthCurrent()) { + lineStateLineType = DartLineStateMaskImport; + } + } else if (StrEqualsAny(s, "class", "extends", "implements", "new", "throw", "as", "is")) { + kwType = KeywordType::Class; + } else if (StrEqual(s, "enum")) { + kwType = KeywordType::Enum; + } else if (StrEqualsAny(s, "break", "continue")) { + kwType = KeywordType::Label; + } else if (StrEqualsAny(s, "return", "await", "yield")) { + kwType = KeywordType::Return; + } + if (kwType > KeywordType::None && kwType < KeywordType::Return) { + const int chNext = sc.GetLineNextChar(); + if (!IsIdentifierStartEx(chNext)) { + kwType = KeywordType::None; + } + } + } else if (keywordLists[KeywordIndex_Type].InList(s)) { + sc.ChangeState(SCE_DART_WORD2); + } else if (keywordLists[KeywordIndex_Class].InList(s)) { + sc.ChangeState(SCE_DART_CLASS); + } else if (keywordLists[KeywordIndex_Enumeration].InList(s)) { + sc.ChangeState(SCE_DART_ENUM); + } else if (sc.ch == ':') { + if (visibleChars == sc.LengthCurrent()) { + const int chNext = sc.GetLineNextChar(true); + if (IsJumpLabelNextChar(chNext)) { + sc.ChangeState(SCE_DART_LABEL); + } + } + } else if (sc.ch != '.') { + if (kwType > KeywordType::None && kwType < KeywordType::Return) { + sc.ChangeState(static_cast(kwType)); + } else { + const int chNext = sc.GetDocNextChar(sc.ch == '?'); + if (chNext == '(') { + // type method() + // type[] method() + // type method() + if (kwType != KeywordType::Return && (IsIdentifierCharEx(chBefore) || chBefore == ']')) { + sc.ChangeState(SCE_DART_FUNCTION_DEFINITION); + } else { + sc.ChangeState(SCE_DART_FUNCTION); + } + } else if ((chBeforeIdentifier == '<' && (chNext == '>' || chNext == '<')) + || IsIdentifierStartEx(chNext)) { + // type + // type + // type> + // type identifier + // type? identifier + sc.ChangeState(SCE_DART_CLASS); + } + } + } + if (sc.state != SCE_DART_WORD && sc.ch != '.') { + kwType = KeywordType::None; + } + } break; + } + + sc.SetState(SCE_DART_DEFAULT); + } + break; + + case SCE_DART_SYMBOL_OPERATOR: + if (!IsDeclarableOperator(sc.ch)) { + sc.SetState(SCE_DART_DEFAULT); + } + break; + + case SCE_DART_COMMENTLINE: + case SCE_DART_COMMENTLINEDOC: + if (sc.atLineStart) { + sc.SetState(SCE_DART_DEFAULT); + } else { + HighlightTaskMarker(sc, visibleChars, visibleCharsBefore, SCE_DART_TASKMARKER); + } + break; + + case SCE_DART_COMMENTBLOCK: + case SCE_DART_COMMENTBLOCKDOC: + if (sc.Match('*', '/')) { + sc.Forward(); + --commentLevel; + if (commentLevel == 0) { + sc.ForwardSetState(SCE_DART_DEFAULT); + } + } else if (sc.Match('/', '*')) { + sc.Forward(); + ++commentLevel; + } else if (HighlightTaskMarker(sc, visibleChars, visibleCharsBefore, SCE_DART_TASKMARKER)) { + continue; + } + break; + + case SCE_DART_RAWSTRING_SQ: + case SCE_DART_RAWSTRING_DQ: + if (sc.atLineStart) { + sc.SetState(SCE_DART_DEFAULT); + } else if ((sc.state == SCE_DART_RAWSTRING_SQ && sc.ch == '\'') + || (sc.state == SCE_DART_RAWSTRING_DQ && sc.ch == '"')) { + sc.ForwardSetState(SCE_DART_DEFAULT); + } + break; + + case SCE_DART_TRIPLE_RAWSTRING_SQ: + case SCE_DART_TRIPLE_RAWSTRING_DQ: + if ((sc.state == SCE_DART_TRIPLE_RAWSTRING_SQ && sc.Match('\'', '\'', '\'')) + || (sc.state == SCE_DART_TRIPLE_RAWSTRING_DQ && sc.Match('"', '"', '"'))) { + sc.Advance(2); + sc.ForwardSetState(SCE_DART_DEFAULT); + } + break; + + case SCE_DART_STRING_SQ: + case SCE_DART_STRING_DQ: + case SCE_DART_TRIPLE_STRING_SQ: + case SCE_DART_TRIPLE_STRING_DQ: + if ((sc.state == SCE_DART_STRING_SQ || sc.state == SCE_DART_STRING_DQ) && sc.atLineStart) { + sc.SetState(SCE_DART_DEFAULT); + } else if (sc.ch == '\\') { + if (escSeq.resetEscapeState(sc.state, sc.chNext)) { + sc.SetState(SCE_DART_ESCAPECHAR); + sc.Forward(); + if (sc.Match('u', '{')) { + escSeq.brace = true; + escSeq.digitsLeft = 7; // Unicode code point + sc.Forward(); + } + } + } else if (sc.ch == '$') { + if (sc.chNext == '{') { + nestedState.push_back(sc.state); + sc.SetState(SCE_DART_OPERATOR2); + sc.Forward(); + } else if (IsIdentifierStartEx(sc.chNext)) { + escSeq.outerState = sc.state; + sc.SetState(SCE_DART_VARIABLE2); + } + } else if ((sc.ch == '\'' && (sc.state == SCE_DART_STRING_SQ || (sc.state == SCE_DART_TRIPLE_STRING_SQ && sc.MatchNext('\'', '\'')))) + || (sc.ch == '"' && (sc.state == SCE_DART_STRING_DQ || (sc.state == SCE_DART_TRIPLE_STRING_DQ && sc.MatchNext('"', '"'))))) { + if (sc.state == SCE_DART_TRIPLE_STRING_SQ || sc.state == SCE_DART_TRIPLE_STRING_DQ) { + sc.Advance(2); + } + sc.ForwardSetState(SCE_DART_DEFAULT); + } + break; + + case SCE_DART_ESCAPECHAR: + if (escSeq.atEscapeEnd(sc.ch)) { + if (escSeq.brace && sc.ch == '}') { + sc.Forward(); + } + sc.SetState(escSeq.outerState); + continue; + } + break; + } + + if (sc.state == SCE_DART_DEFAULT) { + if (sc.ch == '/' && (sc.chNext == '/' || sc.chNext == '*')) { + visibleCharsBefore = visibleChars; + const int chNext = sc.chNext; + sc.SetState((chNext == '/') ? SCE_DART_COMMENTLINE : SCE_DART_COMMENTBLOCK); + sc.Forward(2); + if (sc.ch == chNext && sc.chNext != chNext) { + sc.ChangeState((chNext == '/') ? SCE_DART_COMMENTLINEDOC : SCE_DART_COMMENTBLOCKDOC); + } + if (chNext == '/') { + if (visibleChars == 0) { + lineStateLineType = DartLineStateMaskLineComment; + } + } else { + commentLevel = 1; + } + continue; + } + if (sc.ch == 'r' && (sc.chNext == '\'' || sc.chNext == '"')) { + sc.SetState((sc.chNext == '\'') ? SCE_DART_RAWSTRING_SQ : SCE_DART_RAWSTRING_DQ); + sc.Forward(2); + if (sc.chPrev == '\'' && sc.Match('\'', '\'')) { + sc.ChangeState(SCE_DART_TRIPLE_RAWSTRING_SQ); + sc.Forward(2); + } else if (sc.chPrev == '"' && sc.Match('"', '"')) { + sc.ChangeState(SCE_DART_TRIPLE_RAWSTRING_DQ); + sc.Forward(2); + } + continue; + } + if (sc.ch == '"') { + if (sc.MatchNext('"', '"')) { + sc.SetState(SCE_DART_TRIPLE_STRING_DQ); + sc.Advance(2); + } else { + sc.SetState(SCE_DART_STRING_DQ); + } + } else if (sc.ch == '\'') { + if (sc.MatchNext('\'', '\'')) { + sc.SetState(SCE_DART_TRIPLE_STRING_SQ); + sc.Advance(2); + } else { + sc.SetState(SCE_DART_STRING_SQ); + } + } else if (IsNumberStart(sc.ch, sc.chNext)) { + sc.SetState(SCE_DART_NUMBER); + } else if ((sc.ch == '@' || sc.ch == '$') && IsIdentifierStartEx(sc.chNext)) { + sc.SetState((sc.ch == '@') ? SCE_DART_METADATA : SCE_DART_VARIABLE); + } else if (sc.ch == '#') { + if (IsIdentifierStartEx(sc.chNext)) { + sc.SetState(SCE_DART_SYMBOL_IDENTIFIER); + } else if (IsDeclarableOperator(sc.chNext)) { + sc.SetState(SCE_DART_SYMBOL_OPERATOR); + } + } else if (IsIdentifierStartEx(sc.ch)) { + chBefore = chPrevNonWhite; + if (chPrevNonWhite != '.') { + chBeforeIdentifier = chPrevNonWhite; + } + sc.SetState(SCE_DART_IDENTIFIER); + } else if (isoperator(sc.ch)) { + sc.SetState(SCE_DART_OPERATOR); + if (!nestedState.empty()) { + if (sc.ch == '{') { + nestedState.push_back(SCE_DART_DEFAULT); + } else if (sc.ch == '}') { + const int outerState = TakeAndPop(nestedState); + if (outerState != SCE_DART_DEFAULT) { + sc.ChangeState(SCE_DART_OPERATOR2); + } + sc.ForwardSetState(outerState); + continue; + } + } + } + } + + if (!isspacechar(sc.ch)) { + visibleChars++; + if (!IsSpaceEquiv(sc.state)) { + chPrevNonWhite = sc.ch; + } + } + if (sc.atLineEnd) { + int lineState = (commentLevel << 2) | lineStateLineType; + if (!nestedState.empty()) { + lineState |= PackLineState(nestedState) << 8; + } + styler.SetLineState(sc.currentLine, lineState); + lineStateLineType = 0; + visibleChars = 0; + visibleCharsBefore = 0; + kwType = KeywordType::None; + } + sc.Forward(); + } + + sc.Complete(); +} + +struct FoldLineState { + int lineComment; + int packageImport; + constexpr explicit FoldLineState(int lineState) noexcept: + lineComment(lineState & DartLineStateMaskLineComment), + packageImport((lineState >> 1) & 1) { + } +}; + +constexpr bool IsMultilineStringStyle(int style) noexcept { + return style == SCE_DART_TRIPLE_STRING_SQ + || style == SCE_DART_TRIPLE_STRING_DQ + || style == SCE_DART_OPERATOR2 + || style == SCE_DART_VARIABLE2 + || style == SCE_DART_ESCAPECHAR; +} + +void FoldDartDoc(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, LexerWordList, Accessor &styler) { + const Sci_PositionU endPos = startPos + lengthDoc; + Sci_Line lineCurrent = styler.GetLine(startPos); + FoldLineState foldPrev(0); + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) { + levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16; + foldPrev = FoldLineState(styler.GetLineState(lineCurrent - 1)); + const Sci_PositionU bracePos = CheckBraceOnNextLine(styler, lineCurrent - 1, SCE_DART_OPERATOR, SCE_DART_TASKMARKER); + if (bracePos) { + startPos = bracePos + 1; // skip the brace + } + } + + int levelNext = levelCurrent; + FoldLineState foldCurrent(styler.GetLineState(lineCurrent)); + Sci_PositionU lineStartNext = styler.LineStart(lineCurrent + 1); + lineStartNext = sci::min(lineStartNext, endPos); + + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + int visibleChars = 0; + + while (startPos < endPos) { + const char ch = chNext; + const int stylePrev = style; + style = styleNext; + chNext = styler[++startPos]; + styleNext = styler.StyleAt(startPos); + + switch (style) { + case SCE_DART_COMMENTBLOCKDOC: + case SCE_DART_COMMENTBLOCK: { + const int level = (ch == '/' && chNext == '*') ? 1 : ((ch == '*' && chNext == '/') ? -1 : 0); + if (level != 0) { + levelNext += level; + startPos++; + chNext = styler[startPos]; + styleNext = styler.StyleAt(startPos); + } + } break; + + case SCE_DART_TRIPLE_RAWSTRING_SQ: + case SCE_DART_TRIPLE_RAWSTRING_DQ: + if (style != stylePrev) { + levelNext++; + } else if (style != styleNext) { + levelNext--; + } + break; + + case SCE_DART_TRIPLE_STRING_SQ: + case SCE_DART_TRIPLE_STRING_DQ: + if (!IsMultilineStringStyle(stylePrev)) { + levelNext++; + } else if (!IsMultilineStringStyle(styleNext)) { + levelNext--; + } + break; + + case SCE_DART_OPERATOR: + if (ch == '{' || ch == '[' || ch == '(') { + levelNext++; + } else if (ch == '}' || ch == ']' || ch == ')') { + levelNext--; + } + break; + } + + if (visibleChars == 0 && !IsSpaceEquiv(style)) { + ++visibleChars; + } + if (startPos == lineStartNext) { + const FoldLineState foldNext(styler.GetLineState(lineCurrent + 1)); + if (foldCurrent.lineComment) { + levelNext += foldNext.lineComment - foldPrev.lineComment; + } else if (foldCurrent.packageImport) { + levelNext += foldNext.packageImport - foldPrev.packageImport; + } else if (visibleChars) { + const Sci_PositionU bracePos = CheckBraceOnNextLine(styler, lineCurrent, SCE_DART_OPERATOR, SCE_DART_TASKMARKER); + if (bracePos) { + levelNext++; + startPos = bracePos + 1; // skip the brace + style = SCE_DART_OPERATOR; + chNext = styler[startPos]; + styleNext = styler.StyleAt(startPos); + } + } + + const int levelUse = levelCurrent; + int lev = levelUse | levelNext << 16; + if (levelUse < levelNext) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + + lineCurrent++; + lineStartNext = styler.LineStart(lineCurrent + 1); + lineStartNext = sci::min(lineStartNext, endPos); + levelCurrent = levelNext; + foldPrev = foldCurrent; + foldCurrent = foldNext; + visibleChars = 0; + } + } +} + +} + +LexerModule lmDart(SCLEX_DART, ColouriseDartDoc, "dart", FoldDartDoc); diff --git a/lexilla/lexers_x/orig/zufuliu/LexJavaScript.cxx b/lexilla/lexers_x/orig/zufuliu/LexJavaScript.cxx new file mode 100644 index 000000000..ac10fca7c --- /dev/null +++ b/lexilla/lexers_x/orig/zufuliu/LexJavaScript.cxx @@ -0,0 +1,731 @@ +// This file is part of Notepad2. +// See License.txt for details about distribution and modification. +//! Lexer for JavaScript, JScript, TypeScript, ActionScript. + +#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 "StringUtils.h" +#include "LexerModule.h" +#include "LexerUtils.h" +#include "DocUtils.h" + +using namespace Lexilla; + +namespace { + +// https://tc39.es/ecma262/#prod-StringLiteral +struct EscapeSequence { + int outerState = SCE_JS_DEFAULT; + int digitsLeft = 0; + bool brace = false; + + // highlight any character as escape sequence. + void resetEscapeState(int state, int chNext) noexcept { + outerState = state; + digitsLeft = (chNext == 'x')? 3 : ((chNext == 'u') ? 5 : 1); + } + bool atEscapeEnd(int ch) noexcept { + --digitsLeft; + return digitsLeft <= 0 || !IsHexDigit(ch); + } +}; + +enum { + JsLineStateMaskLineComment = 1, // line comment + JsLineStateMaskImport = (1 << 1), // import + + JsLineStateInsideJsxExpression = 1 << 3, + JsLineStateLineContinuation = 1 << 4, +}; + +//KeywordIndex++Autogenerated -- start of section automatically generated +enum { + KeywordIndex_Keyword = 0, + KeywordIndex_FutureReservedWord = 1, + KeywordIndex_Type = 1, + KeywordIndex_Directive = 2, + KeywordIndex_Class = 3, + KeywordIndex_Interface = 4, + KeywordIndex_Enumeration = 5, + KeywordIndex_Constant = 6, + KeywordIndex_Decorator = 7, + KeywordIndex_Metadata = 7, +}; +//KeywordIndex--Autogenerated -- end of section automatically generated + +enum class KeywordType { + None = SCE_JS_DEFAULT, + Class = SCE_JS_CLASS, + Interface = SCE_JS_INTERFACE, + Enum = SCE_JS_ENUM, + Function = SCE_JS_FUNCTION_DEFINITION, + Label = SCE_JS_LABEL, +}; + +enum class DocTagState { + None, + At, /// @param x + InlineAt, /// {@link https://tsdoc.org/} + XmlOpen, /// + XmlClose, /// , No this (C# like) style +}; + +static_assert(DefaultNestedStateBaseStyle + 1 == SCE_JS_STRING_BT); +static_assert(DefaultNestedStateBaseStyle + 2 == SCE_JSX_TEXT); + +inline bool IsJsIdentifierStartNext(const StyleContext &sc) noexcept { + return IsJsIdentifierStart(sc.chNext) || (sc.chNext == '\\' && sc.GetRelative(2) == 'u'); +} + +constexpr bool IsSpaceEquiv(int state) noexcept { + return state <= SCE_JS_TASKMARKER; +} + +constexpr bool FollowExpression(int chPrevNonWhite, int stylePrevNonWhite) noexcept { + return chPrevNonWhite == ')' || chPrevNonWhite == ']' + || stylePrevNonWhite == SCE_JS_OPERATOR_PF + || IsJsIdentifierChar(chPrevNonWhite); +} + +constexpr bool IsRegexStart(int chPrevNonWhite, int stylePrevNonWhite) noexcept { + return stylePrevNonWhite == SCE_JS_WORD || !FollowExpression(chPrevNonWhite, stylePrevNonWhite); +} + +inline bool IsJsxTagStart(const StyleContext &sc, int chPrevNonWhite, int stylePrevNonWhite) noexcept { + // https://facebook.github.io/jsx/ + // https://reactjs.org/docs/jsx-in-depth.html + return IsRegexStart(chPrevNonWhite, stylePrevNonWhite) + && (IsJsIdentifierStartNext(sc) || sc.chNext == '>' || sc.chNext == '{'); +} + +void ColouriseJsDoc(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, LexerWordList keywordLists, Accessor &styler) { + int lineStateLineType = 0; + int lineContinuation = 0; + bool insideRegexRange = false; // inside regex character range [] + + KeywordType kwType = KeywordType::None; + int chBeforeIdentifier = 0; + + std::vector nestedState; // string interpolation "${}" + bool insideJsxTag = false; + int jsxTagLevel = 0; + std::vector jsxTagLevels;// nested JSX tag in expression + + // JSX syntax conflicts with TypeScript type assert. + // https://www.typescriptlang.org/docs/handbook/jsx.html + const bool enableJsx = styler.GetPropertyBool("lexer.lang", false); + + int visibleChars = 0; + int visibleCharsBefore = 0; + int operatorBefore = 0; + int chPrevNonWhite = 0; + int stylePrevNonWhite = SCE_JS_DEFAULT; + DocTagState docTagState = DocTagState::None; + EscapeSequence escSeq; + + if (enableJsx && startPos != 0) { + // backtrack to the line starts JSX for better coloring on typing. + BacktrackToStart(styler, JsLineStateInsideJsxExpression, startPos, lengthDoc, initStyle); + } + + StyleContext sc(startPos, lengthDoc, initStyle, styler); + if (sc.currentLine > 0) { + int lineState = styler.GetLineState(sc.currentLine - 1); + /* + 2: lineStateLineType + 1: JsLineStateInsideJsxExpression + 1: lineContinuation + 3: nestedState count + 3*4: nestedState + */ + lineContinuation = lineState & JsLineStateLineContinuation; + lineState >>= 8; + if (lineState) { + UnpackLineState(lineState, nestedState); + } + } + if (startPos == 0) { + if (sc.Match('#', '!')) { + // Shell Shebang at beginning of file + sc.SetState(SCE_JS_COMMENTLINE); + sc.Forward(); + lineStateLineType = JsLineStateMaskLineComment; + } + } else if (IsSpaceEquiv(initStyle)) { + // look back for better regex colouring + LookbackNonWhite(styler, startPos, SCE_JS_TASKMARKER, chPrevNonWhite, stylePrevNonWhite); + } + + while (sc.More()) { + switch (sc.state) { + case SCE_JS_OPERATOR: + case SCE_JS_OPERATOR2: + case SCE_JS_OPERATOR_PF: + sc.SetState(SCE_JS_DEFAULT); + break; + + case SCE_JS_NUMBER: + if (!IsDecimalNumberEx(sc.chPrev, sc.ch, sc.chNext)) { + sc.SetState(SCE_JS_DEFAULT); + } + break; + + case SCE_JS_IDENTIFIER: + case SCE_JSX_TAG: + case SCE_JSX_ATTRIBUTE: + case SCE_JSX_ATTRIBUTE_AT: + case SCE_JS_DECORATOR: + if ((sc.ch == '.' && !(sc.state == SCE_JS_IDENTIFIER || sc.state == SCE_JSX_ATTRIBUTE_AT)) + || (sc.ch == ':' && (sc.state == SCE_JSX_TAG || sc.state == SCE_JSX_ATTRIBUTE))) { + const int state = sc.state; + sc.SetState(SCE_JS_OPERATOR2); + sc.ForwardSetState(state); + } + if (sc.Match('\\', 'u') || (sc.ch == '-' && (sc.state == SCE_JSX_TAG || sc.state == SCE_JSX_ATTRIBUTE))) { + sc.Forward(); + } else if (!IsJsIdentifierChar(sc.ch)) { + if (sc.state == SCE_JS_IDENTIFIER) { + char s[128]; + sc.GetCurrent(s, sizeof(s)); + if (keywordLists[KeywordIndex_Directive].InList(s)) { + sc.ChangeState(SCE_JS_DIRECTIVE); + if (StrEqualsAny(s, "import", "require")) { + lineStateLineType = JsLineStateMaskImport; + } + } else if (keywordLists[KeywordIndex_Keyword].InList(s)) { + sc.ChangeState(SCE_JS_WORD); + if (StrEqualsAny(s, "class", "extends","new", "type", "as", "is")) { + kwType = KeywordType::Class; + } else if (StrEqual(s, "function")) { + kwType = KeywordType::Function; + } else if (StrEqualsAny(s, "interface", "implements")) { + kwType = KeywordType::Interface; + } else if (StrEqual(s, "enum")) { + kwType = KeywordType::Enum; + } else if (StrEqualsAny(s, "break", "continue")) { + kwType = KeywordType::Label; + } + if (kwType != KeywordType::None) { + const int chNext = sc.GetLineNextChar(); + if (!(IsJsIdentifierStart(chNext) || chNext == '\\')) { + kwType = KeywordType::None; + } + } + } else if (keywordLists[KeywordIndex_FutureReservedWord].InList(s)) { + sc.ChangeState(SCE_JS_WORD2); + } else if (keywordLists[KeywordIndex_Class].InList(s)) { + sc.ChangeState(SCE_JS_CLASS); + } else if (keywordLists[KeywordIndex_Interface].InList(s)) { + sc.ChangeState(SCE_JS_INTERFACE); + } else if (keywordLists[KeywordIndex_Enumeration].InList(s)) { + sc.ChangeState(SCE_JS_ENUM); + } else if (keywordLists[KeywordIndex_Constant].InList(s)) { + sc.ChangeState(SCE_JS_CONSTANT); + } else if (sc.ch == ':') { + if (visibleChars == sc.LengthCurrent()) { + const int chNext = sc.GetLineNextChar(true); + if (IsJumpLabelNextChar(chNext)) { + sc.ChangeState(SCE_JS_LABEL); + } + } + } else if (sc.ch != '.') { + if (kwType != KeywordType::None) { + sc.ChangeState(static_cast(kwType)); + } else { + const int chNext = sc.GetDocNextChar(sc.ch == '?'); + if (chNext == '(') { + sc.ChangeState(SCE_JS_FUNCTION); + } else if (sc.Match('[', ']') + || (chBeforeIdentifier == '<' && (chNext == '>' || chNext == '<'))) { + // type[] + // type + // type + // type> + sc.ChangeState(SCE_JS_CLASS); + } + } + } + stylePrevNonWhite = sc.state; + if (sc.state != SCE_JS_WORD && sc.ch != '.') { + kwType = KeywordType::None; + } + } + sc.SetState((sc.state == SCE_JSX_TAG || sc.state == SCE_JSX_ATTRIBUTE) ? SCE_JSX_TEXT : SCE_JS_DEFAULT); + continue; + } + break; + + case SCE_JS_STRING_SQ: + case SCE_JS_STRING_DQ: + case SCE_JSX_STRING_SQ: + case SCE_JSX_STRING_DQ: + if (sc.atLineStart) { + if (lineContinuation) { + lineContinuation = 0; + } else { + sc.SetState((sc.state == SCE_JSX_STRING_SQ || sc.state == SCE_JSX_STRING_DQ) ? SCE_JSX_TEXT : SCE_JS_DEFAULT); + continue; + } + } + if (sc.ch == '\\') { + if (IsEOLChar(sc.chNext)) { + lineContinuation = JsLineStateLineContinuation; + } else { + escSeq.resetEscapeState(sc.state, sc.chNext); + sc.SetState(SCE_JS_ESCAPECHAR); + sc.Forward(); + if (sc.Match('u', '{')) { + escSeq.brace = true; + escSeq.digitsLeft = 9; // Unicode code point + sc.Forward(); + } + } + } else if ((sc.ch == '\'' && (sc.state == SCE_JS_STRING_SQ || sc.state == SCE_JSX_STRING_SQ)) + || (sc.ch == '"' && (sc.state == SCE_JS_STRING_DQ || sc.state == SCE_JSX_STRING_DQ))) { + sc.Forward(); + if (operatorBefore == ',' || operatorBefore == '{') { + // json key + const int chNext = sc.GetLineNextChar(); + if (chNext == ':') { + sc.ChangeState(SCE_JS_KEY); + } + } + sc.SetState((sc.state == SCE_JSX_STRING_SQ || sc.state == SCE_JSX_STRING_DQ) ? SCE_JSX_TEXT : SCE_JS_DEFAULT); + continue; + } + break; + + case SCE_JS_STRING_BT: + if (sc.ch == '\\') { + if (!IsEOLChar(sc.chNext)) { + escSeq.resetEscapeState(sc.state, sc.chNext); + sc.SetState(SCE_JS_ESCAPECHAR); + sc.Forward(); + if (sc.Match('u', '{')) { + escSeq.brace = true; + escSeq.digitsLeft = 9; // Unicode code point + sc.Forward(); + } + } + } else if (sc.Match('$', '{')) { + nestedState.push_back(sc.state); + sc.SetState(SCE_JS_OPERATOR2); + sc.Forward(); + } else if (sc.ch == '`') { + sc.ForwardSetState(SCE_JS_DEFAULT); + } + break; + + case SCE_JS_ESCAPECHAR: + if (escSeq.atEscapeEnd(sc.ch)) { + if (escSeq.brace && sc.ch == '}') { + sc.Forward(); + } + sc.SetState(escSeq.outerState); + continue; + } + break; + + case SCE_JS_REGEX: + if (sc.atLineStart) { + sc.SetState(SCE_JS_DEFAULT); + } else if (sc.ch == '\\') { + sc.Forward(); + } else if (sc.ch == '[' || sc.ch == ']') { + insideRegexRange = sc.ch == '['; + } else if (sc.ch == '/' && !insideRegexRange) { + sc.Forward(); + // regex flags + while (IsLowerCase(sc.ch)) { + sc.Forward(); + } + sc.SetState(SCE_JS_DEFAULT); + } + break; + + case SCE_JS_COMMENTLINE: + case SCE_JS_COMMENTLINEDOC: + case SCE_JS_COMMENTBLOCK: + case SCE_JS_COMMENTBLOCKDOC: + if (sc.state == SCE_JS_COMMENTLINE || sc.state == SCE_JS_COMMENTLINEDOC) { + if (sc.atLineStart) { + sc.SetState(SCE_JS_DEFAULT); + break; + } + } else if (sc.Match('*', '/')) { + sc.Forward(); + sc.ForwardSetState(SCE_JS_DEFAULT); + break; + } + switch (docTagState) { + case DocTagState::At: + docTagState = DocTagState::None; + break; + case DocTagState::InlineAt: + if (sc.ch == '}') { + docTagState = DocTagState::None; + sc.SetState(SCE_JS_COMMENTTAGAT); + sc.ForwardSetState(SCE_JS_COMMENTBLOCKDOC); + } + break; + case DocTagState::XmlOpen: + case DocTagState::XmlClose: + if (sc.Match('/', '>') || sc.ch == '>') { + docTagState = DocTagState::None; + sc.SetState(SCE_JS_COMMENTTAGXML); + sc.Forward((sc.ch == '/') ? 2 : 1); + sc.SetState(SCE_JS_COMMENTLINEDOC); + } + break; + default: + break; + } + if (docTagState == DocTagState::None) { + if (sc.ch == '@' && IsLowerCase(sc.chNext) && IsCommentTagPrev(sc.chPrev)) { + docTagState = DocTagState::At; + escSeq.outerState = sc.state; + sc.SetState(SCE_JS_COMMENTTAGAT); + } else if (sc.state == SCE_JS_COMMENTBLOCKDOC && sc.Match('{', '@') && IsLowerCase(sc.GetRelative(2))) { + docTagState = DocTagState::InlineAt; + escSeq.outerState = sc.state; + sc.SetState(SCE_JS_COMMENTTAGAT); + sc.Forward(); + } else if (sc.state == SCE_JS_COMMENTLINEDOC && sc.ch == '<') { + if (IsLowerCase(sc.chNext)) { + docTagState = DocTagState::XmlOpen; + escSeq.outerState = sc.state; + sc.SetState(SCE_JS_COMMENTTAGXML); + } else if (sc.chNext == '/' && IsLowerCase(sc.GetRelative(2))) { + docTagState = DocTagState::XmlClose; + escSeq.outerState = sc.state; + sc.SetState(SCE_JS_COMMENTTAGXML); + sc.Forward(); + } + } else if (HighlightTaskMarker(sc, visibleChars, visibleCharsBefore, SCE_JS_TASKMARKER)) { + continue; + } + } + break; + + case SCE_JS_COMMENTTAGAT: + case SCE_JS_COMMENTTAGXML: + if (!(IsIdentifierChar(sc.ch) || sc.ch == '-')) { + sc.SetState(escSeq.outerState); + continue; + } + break; + + case SCE_JSX_TEXT: + if (sc.ch == '>' || sc.Match('/', '>')) { + insideJsxTag = false; + sc.SetState(SCE_JSX_TAG); + if (sc.ch == '/') { + // self closing + --jsxTagLevel; + sc.Forward(); + } + sc.Forward(); + if (jsxTagLevel == 0) { + sc.SetState(SCE_JS_DEFAULT); + } else { + sc.SetState(SCE_JSX_TEXT); + continue; + } + } else if (sc.ch == '=' && insideJsxTag) { + sc.SetState(SCE_JS_OPERATOR2); + sc.ForwardSetState(SCE_JSX_TEXT); + continue; + } else if ((sc.ch == '\'' || sc.ch == '\"') && insideJsxTag) { + operatorBefore = 0; + sc.SetState((sc.ch == '\'') ? SCE_JSX_STRING_SQ : SCE_JSX_STRING_DQ); + } else if (insideJsxTag && (IsJsIdentifierStart(sc.ch) || sc.Match('\\', 'u'))) { + sc.SetState(SCE_JSX_ATTRIBUTE); + } else if (sc.ch == '{') { + jsxTagLevels.push_back(jsxTagLevel); + nestedState.push_back(sc.state); + sc.SetState(SCE_JS_OPERATOR2); + jsxTagLevel = 0; + } else if (sc.Match('<', '/')) { + --jsxTagLevel; + insideJsxTag = false; + sc.SetState(SCE_JSX_TAG); + sc.Forward(); + } else if (sc.ch == '<') { + ++jsxTagLevel; + insideJsxTag = true; + sc.SetState(SCE_JSX_TAG); + } + break; + } + + if (sc.state == SCE_JS_DEFAULT) { + if (sc.ch == '/') { + if (sc.chNext == '/' || sc.chNext == '*') { + docTagState = DocTagState::None; + visibleCharsBefore = visibleChars; + const int chNext = sc.chNext; + sc.SetState((chNext == '/') ? SCE_JS_COMMENTLINE : SCE_JS_COMMENTBLOCK); + sc.Forward(2); + if (sc.ch == '!' || (sc.ch == chNext && sc.chNext != chNext)) { + sc.ChangeState((chNext == '/') ? SCE_JS_COMMENTLINEDOC : SCE_JS_COMMENTBLOCKDOC); + } + if (chNext == '/') { + if (visibleChars == 0) { + lineStateLineType = JsLineStateMaskLineComment; + } + } + continue; + } + if (!IsEOLChar(sc.chNext) && IsRegexStart(chPrevNonWhite, stylePrevNonWhite)) { + insideRegexRange = false; + sc.SetState(SCE_JS_REGEX); + } else { + sc.SetState(SCE_JS_OPERATOR); + } + } + else if (sc.ch == '\'' || sc.ch == '\"') { + operatorBefore = (stylePrevNonWhite == SCE_JS_OPERATOR) ? chPrevNonWhite : 0; + sc.SetState((sc.ch == '\'') ? SCE_JS_STRING_SQ : SCE_JS_STRING_DQ); + } else if (sc.ch == '`') { + sc.SetState(SCE_JS_STRING_BT); + } else if (IsNumberStartEx(sc.chPrev, sc.ch, sc.chNext)) { + sc.SetState(SCE_JS_NUMBER); + } else if (sc.ch == '@' && IsJsIdentifierStartNext(sc)) { + sc.SetState((sc.chPrev == '.') ? SCE_JSX_ATTRIBUTE_AT : SCE_JS_DECORATOR); + } else if (IsJsIdentifierStart(sc.ch) || sc.Match('\\', 'u')) { + if (sc.chPrev != '.') { + chBeforeIdentifier = sc.chPrev; + } + sc.SetState(SCE_JS_IDENTIFIER); + } + else if (sc.ch == '+' || sc.ch == '-') { + if (sc.ch == sc.chNext) { + // highlight ++ and -- as different style to simplify regex detection. + sc.SetState(SCE_JS_OPERATOR_PF); + sc.Forward(); + } else { + sc.SetState(SCE_JS_OPERATOR); + } + } else if (sc.ch == '<' && enableJsx) { + // + if (sc.chNext == '/') { + insideJsxTag = false; + --jsxTagLevel; + sc.SetState(SCE_JSX_TAG); + sc.Forward(); + } else if (IsJsxTagStart(sc, chPrevNonWhite, stylePrevNonWhite)) { + insideJsxTag = true; + ++jsxTagLevel; + sc.SetState(SCE_JSX_TAG); + } else { + sc.SetState(SCE_JS_OPERATOR); + } + } else if (isoperator(sc.ch)) { + sc.SetState(SCE_JS_OPERATOR); + if (!nestedState.empty()) { + if (sc.ch == '{') { + nestedState.push_back(SCE_JS_DEFAULT); + if (enableJsx) { + jsxTagLevels.push_back(jsxTagLevel); + jsxTagLevel = 0; + } + } else if (sc.ch == '}') { + if (enableJsx) { + jsxTagLevel = TryTakeAndPop(jsxTagLevels); + } + const int outerState = TakeAndPop(nestedState); + if (outerState != SCE_JS_DEFAULT) { + sc.ChangeState(SCE_JS_OPERATOR2); + } + sc.ForwardSetState(outerState); + continue; + } + } + } + } + + if (!isspacechar(sc.ch)) { + visibleChars++; + if (!IsSpaceEquiv(sc.state)) { + chPrevNonWhite = sc.ch; + stylePrevNonWhite = sc.state; + } + } + if (sc.atLineEnd) { + int lineState = lineContinuation | lineStateLineType; + if (enableJsx && !(jsxTagLevel == 0 && jsxTagLevels.empty())) { + lineState |= JsLineStateInsideJsxExpression; + } + if (!nestedState.empty()) { + lineState |= PackLineState(nestedState) << 8; + } + styler.SetLineState(sc.currentLine, lineState); + lineStateLineType = 0; + visibleChars = 0; + visibleCharsBefore = 0; + kwType = KeywordType::None; + docTagState = DocTagState::None; + } + sc.Forward(); + } + + sc.Complete(); +} + +struct FoldLineState { + int lineComment; + int packageImport; + int lineContinuation; + constexpr explicit FoldLineState(int lineState) noexcept: + lineComment(lineState & JsLineStateMaskLineComment), + packageImport((lineState >> 1) & 1), + lineContinuation((lineState >> 4) & 1) { + } +}; + +constexpr bool IsStreamCommentStyle(int style) noexcept { + return style == SCE_JS_COMMENTBLOCK + || style == SCE_JS_COMMENTBLOCKDOC + || style == SCE_JS_COMMENTTAGAT + || style == SCE_JS_COMMENTTAGXML + || style == SCE_JS_TASKMARKER; +} + +constexpr bool IsMultilineStringStyle(int style) noexcept { + return style == SCE_JS_STRING_BT + || style == SCE_JS_OPERATOR2 + || style == SCE_JS_ESCAPECHAR; +} + +void FoldJsDoc(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, LexerWordList, Accessor &styler) { + const Sci_PositionU endPos = startPos + lengthDoc; + Sci_Line lineCurrent = styler.GetLine(startPos); + FoldLineState foldPrev(0); + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) { + levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16; + foldPrev = FoldLineState(styler.GetLineState(lineCurrent - 1)); + const Sci_PositionU bracePos = CheckBraceOnNextLine(styler, lineCurrent - 1, SCE_JS_OPERATOR, SCE_JS_TASKMARKER); + if (bracePos) { + startPos = bracePos + 1; // skip the brace + } + } + + int levelNext = levelCurrent; + FoldLineState foldCurrent(styler.GetLineState(lineCurrent)); + Sci_PositionU lineStartNext = styler.LineStart(lineCurrent + 1); + lineStartNext = sci::min(lineStartNext, endPos); + + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + int visibleChars = 0; + + while (startPos < endPos) { + const char ch = chNext; + const int stylePrev = style; + style = styleNext; + chNext = styler[++startPos]; + styleNext = styler.StyleAt(startPos); + + switch (style) { + case SCE_JS_COMMENTBLOCK: + case SCE_JS_COMMENTBLOCKDOC: + if (!IsStreamCommentStyle(stylePrev)) { + levelNext++; + } else if (!IsStreamCommentStyle(styleNext)) { + levelNext--; + } + break; + + case SCE_JS_STRING_BT: + if (!IsMultilineStringStyle(stylePrev)) { + levelNext++; + } else if (!IsMultilineStringStyle(styleNext)) { + levelNext--; + } + break; + + case SCE_JS_OPERATOR: + if (ch == '{' || ch == '[' || ch == '(') { + levelNext++; + } else if (ch == '}' || ch == ']' || ch == ')') { + levelNext--; + } + break; + + case SCE_JSX_TAG: + if (ch == '<') { + if (chNext == '/') { + levelNext--; + startPos++; + chNext = styler[startPos]; + styleNext = styler.StyleAt(startPos); + } else { + levelNext++; + } + } else if (ch == '/' && chNext == '>') { + levelNext--; + } + break; + } + + if (visibleChars == 0 && !IsSpaceEquiv(style)) { + ++visibleChars; + } + if (startPos == lineStartNext) { + const FoldLineState foldNext(styler.GetLineState(lineCurrent + 1)); + if (foldCurrent.lineComment) { + levelNext += foldNext.lineComment - foldPrev.lineComment; + } else if (foldCurrent.packageImport) { + levelNext += foldNext.packageImport - foldPrev.packageImport; + } else if (foldCurrent.lineContinuation | foldPrev.lineContinuation) { + levelNext += foldCurrent.lineContinuation - foldPrev.lineContinuation; + } else if (visibleChars) { + const Sci_PositionU bracePos = CheckBraceOnNextLine(styler, lineCurrent, SCE_JS_OPERATOR, SCE_JS_TASKMARKER); + if (bracePos) { + levelNext++; + startPos = bracePos + 1; // skip the brace + style = SCE_JS_OPERATOR; + chNext = styler[startPos]; + styleNext = styler.StyleAt(startPos); + } + } + + const int levelUse = levelCurrent; + int lev = levelUse | levelNext << 16; + if (levelUse < levelNext) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + + lineCurrent++; + lineStartNext = styler.LineStart(lineCurrent + 1); + lineStartNext = sci::min(lineStartNext, endPos); + levelCurrent = levelNext; + foldPrev = foldCurrent; + foldCurrent = foldNext; + visibleChars = 0; + } + } +} + +} + +LexerModule lmJavaScript(SCLEX_JAVASCRIPT, ColouriseJsDoc, "js", FoldJsDoc); diff --git a/lexilla/lexers_x/orig/zufuliu/LexKotlin.cxx b/lexilla/lexers_x/orig/zufuliu/LexKotlin.cxx new file mode 100644 index 000000000..c23317e9b --- /dev/null +++ b/lexilla/lexers_x/orig/zufuliu/LexKotlin.cxx @@ -0,0 +1,505 @@ +// This file is part of Notepad2. +// See License.txt for details about distribution and modification. +//! Lexer for Kotlin. + +#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 "StringUtils.h" +#include "LexerModule.h" +#include "LexerUtils.h" + +using namespace Lexilla; + +namespace { + +struct EscapeSequence { + int outerState = SCE_KOTLIN_DEFAULT; + int digitsLeft = 0; + + // highlight any character as escape sequence. + bool resetEscapeState(int state, int chNext) noexcept { + if (IsEOLChar(chNext)) { + return false; + } + outerState = state; + digitsLeft = (chNext == 'u') ? 5 : 1; + return true; + } + bool atEscapeEnd(int ch) noexcept { + --digitsLeft; + return digitsLeft <= 0 || !IsHexDigit(ch); + } +}; + +enum { + KotlinLineStateMaskLineComment = 1, // line comment + KotlinLineStateMaskImport = 1 << 1, // import +}; + +//KeywordIndex++Autogenerated -- start of section automatically generated +enum { + KeywordIndex_Keyword = 0, + KeywordIndex_Class = 1, + KeywordIndex_Interface = 2, + KeywordIndex_Enumeration = 3, +}; +//KeywordIndex--Autogenerated -- end of section automatically generated + +enum class KeywordType { + None = SCE_KOTLIN_DEFAULT, + Annotation = SCE_KOTLIN_ANNOTATION, + Class = SCE_KOTLIN_CLASS, + Interface = SCE_KOTLIN_INTERFACE, + Enum = SCE_KOTLIN_ENUM, + Label = SCE_KOTLIN_LABEL, + Return = 0x40, +}; + +static_assert(DefaultNestedStateBaseStyle + 1 == SCE_KOTLIN_STRING); +static_assert(DefaultNestedStateBaseStyle + 2 == SCE_KOTLIN_RAWSTRING); + +constexpr bool IsSpaceEquiv(int state) noexcept { + return state <= SCE_KOTLIN_TASKMARKER; +} + +void ColouriseKotlinDoc(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, LexerWordList keywordLists, Accessor &styler) { + int lineStateLineType = 0; + int commentLevel = 0; // nested block comment level + + KeywordType kwType = KeywordType::None; + int chBeforeIdentifier = 0; + + std::vector nestedState; // string interpolation "${}" + + int visibleChars = 0; + int chBefore = 0; + int visibleCharsBefore = 0; + int chPrevNonWhite = 0; + EscapeSequence escSeq; + + StyleContext sc(startPos, lengthDoc, initStyle, styler); + if (sc.currentLine > 0) { + int lineState = styler.GetLineState(sc.currentLine - 1); + /* + 2: lineStateLineType + 6: commentLevel + 3: nestedState count + 3*4: nestedState + */ + commentLevel = (lineState >> 2) & 0x3f; + lineState >>= 8; + if (lineState) { + UnpackLineState(lineState, nestedState); + } + } else if (startPos == 0 && sc.Match('#', '!')) { + // Shell Shebang at beginning of file + sc.SetState(SCE_KOTLIN_COMMENTLINE); + sc.Forward(); + lineStateLineType = KotlinLineStateMaskLineComment; + } + + while (sc.More()) { + switch (sc.state) { + case SCE_KOTLIN_OPERATOR: + case SCE_KOTLIN_OPERATOR2: + sc.SetState(SCE_KOTLIN_DEFAULT); + break; + + case SCE_KOTLIN_NUMBER: + if (!IsDecimalNumber(sc.chPrev, sc.ch, sc.chNext)) { + sc.SetState(SCE_KOTLIN_DEFAULT); + } + break; + + case SCE_KOTLIN_VARIABLE: + case SCE_KOTLIN_LABEL: + case SCE_KOTLIN_IDENTIFIER: + case SCE_KOTLIN_ANNOTATION: + if (!IsIdentifierCharEx(sc.ch)) { + switch (sc.state) { + case SCE_KOTLIN_VARIABLE: + sc.SetState(escSeq.outerState); + continue; + + case SCE_KOTLIN_ANNOTATION: + if (sc.ch == '.' || sc.ch == ':') { + sc.SetState(SCE_KOTLIN_OPERATOR); + sc.ForwardSetState(SCE_KOTLIN_ANNOTATION); + continue; + } + break; + + case SCE_KOTLIN_IDENTIFIER: { + char s[128]; + sc.GetCurrent(s, sizeof(s)); + if (keywordLists[KeywordIndex_Keyword].InList(s)) { + sc.ChangeState(SCE_KOTLIN_WORD); + if (StrEqual(s, "import")) { + if (visibleChars == sc.LengthCurrent()) { + lineStateLineType = KotlinLineStateMaskImport; + } + } else if (StrEqualsAny(s, "break", "continue", "return", "this", "super")) { + kwType = KeywordType::Label; + } else if (StrEqualsAny(s, "class", "typealias")) { + if (!(kwType == KeywordType::Annotation || kwType == KeywordType::Enum)) { + kwType = KeywordType::Class; + } + } else if (StrEqual(s, "enum")) { + kwType = KeywordType::Enum; + } else if (StrEqual(s, "annotation")) { + kwType = KeywordType::Annotation; + } else if (StrEqual(s, "interface")) { + kwType = KeywordType::Interface; + } else if (StrEqual(s, "return")) { + kwType = KeywordType::Return; + } + if (kwType > KeywordType::None && kwType < KeywordType::Return) { + const int chNext = sc.GetDocNextChar(); + if (!((kwType == KeywordType::Label) ? (chNext == '@') : IsIdentifierStartEx(chNext))) { + kwType = KeywordType::None; + } + } + } else if (sc.ch == '@') { + sc.ChangeState(SCE_KOTLIN_LABEL); + sc.Forward(); + } else if (keywordLists[KeywordIndex_Class].InList(s)) { + sc.ChangeState(SCE_KOTLIN_CLASS); + } else if (keywordLists[KeywordIndex_Interface].InList(s)) { + sc.ChangeState(SCE_KOTLIN_INTERFACE); + } else if (keywordLists[KeywordIndex_Enumeration].InList(s)) { + sc.ChangeState(SCE_KOTLIN_ENUM); + } else if (sc.ch != '.') { + if (kwType > KeywordType::None && kwType < KeywordType::Return) { + sc.ChangeState(static_cast(kwType)); + } else { + const int chNext = sc.GetDocNextChar(sc.ch == '?'); + if (chNext == '(') { + // type function() + // type[] function() + // type function() + if (kwType != KeywordType::Return && (IsIdentifierCharEx(chBefore) || chBefore == ']')) { + sc.ChangeState(SCE_KOTLIN_FUNCTION_DEFINITION); + } else { + sc.ChangeState(SCE_KOTLIN_FUNCTION); + } + } else if (sc.Match(':', ':') + || (chBeforeIdentifier == '<' && (chNext == '>' || chNext == '<'))) { + // type::class + // type + // type + // type> + sc.ChangeState(SCE_KOTLIN_CLASS); + } + } + } + if (sc.state != SCE_KOTLIN_WORD && sc.ch != '.') { + kwType = KeywordType::None; + } + } break; + } + sc.SetState(SCE_KOTLIN_DEFAULT); + } + break; + + case SCE_KOTLIN_COMMENTLINE: + case SCE_KOTLIN_COMMENTLINEDOC: + if (sc.atLineStart) { + sc.SetState(SCE_KOTLIN_DEFAULT); + } else { + HighlightTaskMarker(sc, visibleChars, visibleCharsBefore, SCE_KOTLIN_TASKMARKER); + } + break; + + case SCE_KOTLIN_COMMENTBLOCK: + case SCE_KOTLIN_COMMENTBLOCKDOC: + if (sc.state == SCE_KOTLIN_COMMENTBLOCKDOC && sc.ch == '@' && IsLowerCase(sc.chNext) && IsCommentTagPrev(sc.chPrev)) { + sc.SetState(SCE_KOTLIN_COMMENTDOCWORD); + } else if (sc.Match('*', '/')) { + sc.Forward(); + --commentLevel; + if (commentLevel == 0) { + sc.ForwardSetState(SCE_KOTLIN_DEFAULT); + } + } else if (sc.Match('/', '*')) { + sc.Forward(); + ++commentLevel; + } else if (HighlightTaskMarker(sc, visibleChars, visibleCharsBefore, SCE_KOTLIN_TASKMARKER)) { + continue; + } + break; + + case SCE_KOTLIN_COMMENTDOCWORD: + if (!IsLowerCase(sc.ch)) { + sc.SetState(SCE_KOTLIN_COMMENTBLOCKDOC); + continue; + } + break; + + case SCE_KOTLIN_STRING: + case SCE_KOTLIN_RAWSTRING: + if (sc.state == SCE_KOTLIN_STRING && sc.atLineStart) { + sc.SetState(SCE_KOTLIN_DEFAULT); + } else if (sc.state == SCE_KOTLIN_STRING && sc.ch == '\\') { + if (escSeq.resetEscapeState(sc.state, sc.chNext)) { + sc.SetState(SCE_KOTLIN_ESCAPECHAR); + sc.Forward(); + } + } else if (sc.ch == '$') { + if (sc.chNext == '{') { + nestedState.push_back(sc.state); + sc.SetState(SCE_KOTLIN_OPERATOR2); + sc.Forward(); + } else if (IsIdentifierStartEx(sc.chNext)) { + escSeq.outerState = sc.state; + sc.SetState(SCE_KOTLIN_VARIABLE); + } + } else if (sc.ch == '\"' && (sc.state == SCE_KOTLIN_STRING || sc.MatchNext('"', '"'))) { + if (sc.state == SCE_KOTLIN_RAWSTRING) { + sc.Advance(2); + } + sc.ForwardSetState(SCE_KOTLIN_DEFAULT); + } + break; + + case SCE_KOTLIN_CHARACTER: + if (sc.atLineStart) { + sc.SetState(SCE_KOTLIN_DEFAULT); + } else if (sc.ch == '\\') { + if (escSeq.resetEscapeState(sc.state, sc.chNext)) { + sc.SetState(SCE_KOTLIN_ESCAPECHAR); + sc.Forward(); + } + } else if (sc.ch == '\'') { + sc.ForwardSetState(SCE_KOTLIN_DEFAULT); + } + break; + + case SCE_KOTLIN_ESCAPECHAR: + if (escSeq.atEscapeEnd(sc.ch)) { + sc.SetState(escSeq.outerState); + continue; + } + break; + + case SCE_KOTLIN_BACKTICKS: + if (sc.atLineStart) { + sc.SetState(SCE_KOTLIN_DEFAULT); + } else if (sc.ch == '`') { + sc.ForwardSetState(SCE_KOTLIN_DEFAULT); + } + break; + } + + if (sc.state == SCE_KOTLIN_DEFAULT) { + if (sc.ch == '/' && (sc.chNext == '/' || sc.chNext == '*')) { + visibleCharsBefore = visibleChars; + const int chNext = sc.chNext; + sc.SetState((chNext == '/') ? SCE_KOTLIN_COMMENTLINE : SCE_KOTLIN_COMMENTBLOCK); + sc.Forward(2); + if (sc.ch == '!' || (sc.ch == chNext && sc.chNext != chNext)) { + sc.ChangeState((chNext == '/') ? SCE_KOTLIN_COMMENTLINEDOC : SCE_KOTLIN_COMMENTBLOCKDOC); + } + if (chNext == '/') { + if (visibleChars == 0) { + lineStateLineType = KotlinLineStateMaskLineComment; + } + } else { + commentLevel = 1; + } + continue; + } + if (sc.ch == '\"') { + if (sc.MatchNext('"', '"')) { + sc.SetState(SCE_KOTLIN_RAWSTRING); + sc.Advance(2); + } else { + sc.SetState(SCE_KOTLIN_STRING); + } + } else if (sc.ch == '\'') { + sc.SetState(SCE_KOTLIN_CHARACTER); + } else if (IsNumberStart(sc.ch, sc.chNext)) { + sc.SetState(SCE_KOTLIN_NUMBER); + } else if (sc.ch == '@' && IsIdentifierStartEx(sc.chNext)) { + sc.SetState((kwType == KeywordType::Label) ? SCE_KOTLIN_LABEL : SCE_KOTLIN_ANNOTATION); + kwType = KeywordType::None; + } else if (sc.ch == '`') { + sc.SetState(SCE_KOTLIN_BACKTICKS); + } else if (IsIdentifierStartEx(sc.ch)) { + chBefore = chPrevNonWhite; + if (chPrevNonWhite != '.') { + chBeforeIdentifier = chPrevNonWhite; + } + sc.SetState(SCE_KOTLIN_IDENTIFIER); + } else if (isoperator(sc.ch)) { + sc.SetState(SCE_KOTLIN_OPERATOR); + if (!nestedState.empty()) { + if (sc.ch == '{') { + nestedState.push_back(SCE_KOTLIN_DEFAULT); + } else if (sc.ch == '}') { + const int outerState = TakeAndPop(nestedState); + if (outerState != SCE_KOTLIN_DEFAULT) { + sc.ChangeState(SCE_KOTLIN_OPERATOR2); + } + sc.ForwardSetState(outerState); + continue; + } + } + } + } + + if (!isspacechar(sc.ch)) { + visibleChars++; + if (!IsSpaceEquiv(sc.state)) { + chPrevNonWhite = sc.ch; + } + } + if (sc.atLineEnd) { + int lineState = (commentLevel << 2) | lineStateLineType; + if (!nestedState.empty()) { + lineState |= PackLineState(nestedState) << 8; + } + styler.SetLineState(sc.currentLine, lineState); + lineStateLineType = 0; + visibleChars = 0; + visibleCharsBefore = 0; + kwType = KeywordType::None; + } + sc.Forward(); + } + + sc.Complete(); +} + +struct FoldLineState { + int lineComment; + int packageImport; + constexpr explicit FoldLineState(int lineState) noexcept: + lineComment(lineState & KotlinLineStateMaskLineComment), + packageImport((lineState >> 1) & 1) { + } +}; + +constexpr bool IsMultilineStringStyle(int style) noexcept { + return style == SCE_KOTLIN_RAWSTRING + || style == SCE_KOTLIN_OPERATOR2 + || style == SCE_KOTLIN_ESCAPECHAR + || style == SCE_KOTLIN_VARIABLE; +} + +void FoldKotlinDoc(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, LexerWordList, Accessor &styler) { + const Sci_PositionU endPos = startPos + lengthDoc; + Sci_Line lineCurrent = styler.GetLine(startPos); + FoldLineState foldPrev(0); + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) { + levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16; + foldPrev = FoldLineState(styler.GetLineState(lineCurrent - 1)); + const Sci_PositionU bracePos = CheckBraceOnNextLine(styler, lineCurrent - 1, SCE_KOTLIN_OPERATOR, SCE_KOTLIN_TASKMARKER); + if (bracePos) { + startPos = bracePos + 1; // skip the brace + } + } + + int levelNext = levelCurrent; + FoldLineState foldCurrent(styler.GetLineState(lineCurrent)); + Sci_PositionU lineStartNext = styler.LineStart(lineCurrent + 1); + lineStartNext = sci::min(lineStartNext, endPos); + + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + int visibleChars = 0; + + while (startPos < endPos) { + const char ch = chNext; + const int stylePrev = style; + style = styleNext; + chNext = styler[++startPos]; + styleNext = styler.StyleAt(startPos); + + switch (style) { + case SCE_KOTLIN_COMMENTBLOCK: + case SCE_KOTLIN_COMMENTBLOCKDOC: { + const int level = (ch == '/' && chNext == '*') ? 1 : ((ch == '*' && chNext == '/') ? -1 : 0); + if (level != 0) { + levelNext += level; + startPos++; + chNext = styler[startPos]; + styleNext = styler.StyleAt(startPos); + } + } break; + + case SCE_KOTLIN_RAWSTRING: + if (!IsMultilineStringStyle(stylePrev)) { + levelNext++; + } else if (!IsMultilineStringStyle(styleNext)) { + levelNext--; + } + break; + + case SCE_KOTLIN_OPERATOR: + if (ch == '{' || ch == '[' || ch == '(') { + levelNext++; + } else if (ch == '}' || ch == ']' || ch == ')') { + levelNext--; + } + break; + } + + if (visibleChars == 0 && !IsSpaceEquiv(style)) { + ++visibleChars; + } + if (startPos == lineStartNext) { + const FoldLineState foldNext(styler.GetLineState(lineCurrent + 1)); + if (foldCurrent.lineComment) { + levelNext += foldNext.lineComment - foldPrev.lineComment; + } else if (foldCurrent.packageImport) { + levelNext += foldNext.packageImport - foldPrev.packageImport; + } else if (visibleChars) { + const Sci_PositionU bracePos = CheckBraceOnNextLine(styler, lineCurrent, SCE_KOTLIN_OPERATOR, SCE_KOTLIN_TASKMARKER); + if (bracePos) { + levelNext++; + startPos = bracePos + 1; // skip the brace + style = SCE_KOTLIN_OPERATOR; + chNext = styler[startPos]; + styleNext = styler.StyleAt(startPos); + } + } + + const int levelUse = levelCurrent; + int lev = levelUse | levelNext << 16; + if (levelUse < levelNext) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + + lineCurrent++; + lineStartNext = styler.LineStart(lineCurrent + 1); + lineStartNext = sci::min(lineStartNext, endPos); + levelCurrent = levelNext; + foldPrev = foldCurrent; + foldCurrent = foldNext; + visibleChars = 0; + } + } +} + +} + +LexerModule lmKotlin(SCLEX_KOTLIN, ColouriseKotlinDoc, "kotlin", FoldKotlinDoc); diff --git a/src/Notepad3.c b/src/Notepad3.c index 079345619..f056c2086 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -3919,7 +3919,7 @@ LRESULT MsgFileChangeNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); - + SET_FCT_GUARD(TRUE); InstallFileWatching(false); // terminate @@ -3952,7 +3952,7 @@ LRESULT MsgFileChangeNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) Sci_ScrollSelectionToView(); } - } else { + } else { // file has been deleted if (FileWatching.FileWatchingMode == FWM_MSGBOX) { if (IsYesOkay(InfoBoxLng(MB_YESNO | MB_ICONWARNING, NULL, IDS_MUI_FILECHANGENOTIFY2))) { @@ -3964,6 +3964,7 @@ LRESULT MsgFileChangeNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) // FWM_INDICATORSILENT: nothing todo here SetSaveNeeded(true); } + } InstallFileWatching(true); @@ -12636,7 +12637,9 @@ void InstallFileWatching(const bool bInstall) { HPATHL hdir_pth = Path_Copy(Paths.CurrentFile); Path_RemoveFileSpec(hdir_pth); - bool const bFileDirExists = Path_IsNotEmpty(Paths.CurrentFile) && Path_IsExistingDirectory(hdir_pth); //~ && Path_IsExistingFile(Paths.CurrentFile); + bool const bFileDirExists = Path_IsNotEmpty(Paths.CurrentFile) && Path_IsExistingDirectory(hdir_pth); + bool const bFileExists = bFileDirExists && Path_IsExistingFile(Paths.CurrentFile); + bool const bExclusiveLock = (FileWatching.FileWatchingMode == FWM_EXCLUSIVELOCK); bool const bWatchFile = (FileWatching.FileWatchingMode != FWM_DONT_CARE) && !bExclusiveLock; @@ -12652,16 +12655,16 @@ void InstallFileWatching(const bool bInstall) { BackgroundWorker_Init(&(s_FileChgObsvrData.worker), NULL, NULL); } - bool const bTerminate = !bInstall || !bWatchFile || !bFileDirExists; + bool const bTerminate = !bInstall || !bWatchFile || !bFileDirExists /*~||!bFileExists~*/; // Terminate previous watching if (bTerminate) { KillTimer(Globals.hwndMain, ID_WATCHTIMER); BackgroundWorker_Cancel(&(s_FileChgObsvrData.worker)); - ResetFileObservationData(true); + ResetFileObservationData(false); // (!) false } - if (bInstall) { + if (bInstall && bFileExists) { if (bWatchFile) { @@ -12684,7 +12687,8 @@ void InstallFileWatching(const bool bInstall) { KillTimer(Globals.hwndMain, ID_WATCHTIMER); } - } else if (bExclusiveLock) { + } + else if (bExclusiveLock) { assert(!IS_VALID_HANDLE(_hCurrFileHandle) && "CurrFileHandle not properly closed!"); diff --git a/src/StyleLexers/EditLexer.c b/src/StyleLexers/EditLexer.c index b1d8dee27..49f17a541 100644 --- a/src/StyleLexers/EditLexer.c +++ b/src/StyleLexers/EditLexer.c @@ -248,8 +248,8 @@ void Lexer_SetLexerSpecificProperties(const int lexerId) { break; case SCLEX_JSON: - SciCall_SetProperty("json.allow.comments", "1"); - SciCall_SetProperty("json.escape.sequence", "1"); + SciCall_SetProperty("lexer.json.allow.comments", "1"); + SciCall_SetProperty("lexer.json.escape.sequence", "1"); break; case SCLEX_PYTHON: diff --git a/test/test_files/StyleLexers/styleLexJSON/Sample_JSON (issue #3070).json b/test/test_files/StyleLexers/styleLexJSON/Sample_JSON (issue #3070).json index e8aaa5405..b7fe421ad 100644 --- a/test/test_files/StyleLexers/styleLexJSON/Sample_JSON (issue #3070).json +++ b/test/test_files/StyleLexers/styleLexJSON/Sample_JSON (issue #3070).json @@ -13,7 +13,8 @@ No \\n's!", leadingDecimalPoint: 1234567, .8675309, andTrailing: 8675309., positiveSign: +1, trailingComma: 'in objects', andIn: ['arrays',], - escSeq: "\uAFFE", + escSeq: "\uAEEE", + escSeq2: "\uAFFE", "backwardsCompatible": "with JSON", Infinity: Infinity, positiveInfinity: +Infinity,