You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
814 lines
27 KiB
814 lines
27 KiB
// Scintilla source code edit control |
|
/** @file LexNim.cxx |
|
** Lexer for Nim |
|
** Written by Jad Altahan (github.com/xv) |
|
** Nim manual: https://nim-lang.org/docs/manual.html |
|
**/ |
|
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org> |
|
// The License.txt file describes the conditions under which this software may be distributed. |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdio.h> |
|
#include <stdarg.h> |
|
#include <assert.h> |
|
#include <ctype.h> |
|
|
|
#include <string> |
|
#include <string_view> |
|
#include <map> |
|
#include <algorithm> |
|
#include <functional> |
|
|
|
#include "ILexer.h" |
|
#include "Scintilla.h" |
|
#include "SciLexer.h" |
|
|
|
#include "StringCopy.h" |
|
#include "WordList.h" |
|
#include "LexAccessor.h" |
|
#include "Accessor.h" |
|
#include "StyleContext.h" |
|
#include "CharacterSet.h" |
|
#include "LexerModule.h" |
|
#include "OptionSet.h" |
|
#include "DefaultLexer.h" |
|
|
|
using namespace Scintilla; |
|
using namespace Lexilla; |
|
|
|
namespace { |
|
// Use an unnamed namespace to protect the functions and classes from name conflicts |
|
|
|
enum NumType { |
|
Binary, |
|
Octal, |
|
Exponent, |
|
Hexadecimal, |
|
Decimal, |
|
FormatError |
|
}; |
|
|
|
int GetNumStyle(const int numType) noexcept { |
|
if (numType == NumType::FormatError) { |
|
return SCE_NIM_NUMERROR; |
|
} |
|
|
|
return SCE_NIM_NUMBER; |
|
} |
|
|
|
constexpr bool IsLetter(const int ch) noexcept { |
|
// 97 to 122 || 65 to 90 |
|
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); |
|
} |
|
|
|
bool IsAWordChar(const int ch) noexcept { |
|
return ch < 0x80 && (isalnum(ch) || ch == '_' || ch == '.'); |
|
} |
|
|
|
int IsNumHex(const StyleContext &sc) noexcept { |
|
return sc.chNext == 'x' || sc.chNext == 'X'; |
|
} |
|
|
|
int IsNumBinary(const StyleContext &sc) noexcept { |
|
return sc.chNext == 'b' || sc.chNext == 'B'; |
|
} |
|
|
|
int IsNumOctal(const StyleContext &sc) { |
|
return IsADigit(sc.chNext) || sc.chNext == 'o'; |
|
} |
|
|
|
constexpr bool IsNewline(const int ch) noexcept { |
|
return (ch == '\n' || ch == '\r'); |
|
} |
|
|
|
bool IsFuncName(const char *str) noexcept { |
|
const char *identifiers[] = { |
|
"proc", |
|
"func", |
|
"macro", |
|
"method", |
|
"template", |
|
"iterator", |
|
"converter" |
|
}; |
|
|
|
for (const char *id : identifiers) { |
|
if (strcmp(str, id) == 0) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
constexpr bool IsTripleLiteral(const int style) noexcept { |
|
return style == SCE_NIM_TRIPLE || style == SCE_NIM_TRIPLEDOUBLE; |
|
} |
|
|
|
constexpr bool IsLineComment(const int style) noexcept { |
|
return style == SCE_NIM_COMMENTLINE || style == SCE_NIM_COMMENTLINEDOC; |
|
} |
|
|
|
constexpr bool IsStreamComment(const int style) noexcept { |
|
return style == SCE_NIM_COMMENT || style == SCE_NIM_COMMENTDOC; |
|
} |
|
|
|
// Adopted from Accessor.cxx |
|
int GetIndent(const Sci_Position line, Accessor &styler) { |
|
Sci_Position startPos = styler.LineStart(line); |
|
const Sci_Position eolPos = styler.LineStart(line + 1) - 1; |
|
|
|
char ch = styler[startPos]; |
|
int style = styler.StyleAt(startPos); |
|
|
|
int indent = 0; |
|
bool inPrevPrefix = line > 0; |
|
Sci_Position posPrev = inPrevPrefix ? styler.LineStart(line - 1) : 0; |
|
|
|
// No fold points inside triple literals |
|
while ((IsASpaceOrTab(ch) || IsTripleLiteral(style)) && (startPos < eolPos)) { |
|
if (inPrevPrefix) { |
|
const char chPrev = styler[posPrev++]; |
|
if (chPrev != ' ' && chPrev != '\t') { |
|
inPrevPrefix = false; |
|
} |
|
} |
|
|
|
if (ch == '\t') { |
|
indent = (indent / 8 + 1) * 8; |
|
} else { |
|
indent++; |
|
} |
|
|
|
startPos++; |
|
ch = styler[startPos]; |
|
style = styler.StyleAt(startPos); |
|
} |
|
|
|
// Prevent creating fold lines for comments if indented |
|
if (!(IsStreamComment(style) || IsLineComment(style))) |
|
indent += SC_FOLDLEVELBASE; |
|
|
|
if (styler.LineStart(line) == styler.Length() |
|
|| IsASpaceOrTab(ch) |
|
|| IsNewline(ch) |
|
|| IsStreamComment(style) |
|
|| IsLineComment(style)) { |
|
return indent | SC_FOLDLEVELWHITEFLAG; |
|
} else { |
|
return indent; |
|
} |
|
} |
|
|
|
int IndentAmount(const Sci_Position line, Accessor &styler) { |
|
const int indent = GetIndent(line, styler); |
|
const int indentLevel = indent & SC_FOLDLEVELNUMBERMASK; |
|
return indentLevel <= SC_FOLDLEVELBASE ? indent : indentLevel | (indent & ~SC_FOLDLEVELNUMBERMASK); |
|
} |
|
|
|
struct OptionsNim { |
|
bool fold; |
|
bool foldCompact; |
|
bool highlightRawStrIdent; |
|
|
|
OptionsNim() { |
|
fold = true; |
|
foldCompact = true; |
|
highlightRawStrIdent = false; |
|
} |
|
}; |
|
|
|
static const char *const nimWordListDesc[] = { |
|
"Keywords", |
|
nullptr |
|
}; |
|
|
|
struct OptionSetNim : public OptionSet<OptionsNim> { |
|
OptionSetNim() { |
|
DefineProperty("lexer.nim.raw.strings.highlight.ident", &OptionsNim::highlightRawStrIdent, |
|
"Set to 1 to enable highlighting generalized raw string identifiers. " |
|
"Generalized raw string identifiers are anything other than r (or R)."); |
|
|
|
DefineProperty("fold", &OptionsNim::fold); |
|
DefineProperty("fold.compact", &OptionsNim::foldCompact); |
|
|
|
DefineWordListSets(nimWordListDesc); |
|
} |
|
}; |
|
|
|
LexicalClass lexicalClasses[] = { |
|
// Lexer Nim SCLEX_NIM SCE_NIM_: |
|
0, "SCE_NIM_DEFAULT", "default", "White space", |
|
1, "SCE_NIM_COMMENT", "comment block", "Block comment", |
|
2, "SCE_NIM_COMMENTDOC", "comment block doc", "Block doc comment", |
|
3, "SCE_NIM_COMMENTLINE", "comment line", "Line comment", |
|
4, "SCE_NIM_COMMENTLINEDOC", "comment doc", "Line doc comment", |
|
5, "SCE_NIM_NUMBER", "literal numeric", "Number", |
|
6, "SCE_NIM_STRING", "literal string", "String", |
|
7, "SCE_NIM_CHARACTER", "literal string", "Single quoted string", |
|
8, "SCE_NIM_WORD", "keyword", "Keyword", |
|
9, "SCE_NIM_TRIPLE", "literal string", "Triple quotes", |
|
10, "SCE_NIM_TRIPLEDOUBLE", "literal string", "Triple double quotes", |
|
11, "SCE_NIM_BACKTICKS", "operator definition", "Identifiers", |
|
12, "SCE_NIM_FUNCNAME", "identifier", "Function name definition", |
|
13, "SCE_NIM_STRINGEOL", "error literal string", "String is not closed", |
|
14, "SCE_NIM_NUMERROR", "numeric error", "Numeric format error", |
|
15, "SCE_NIM_OPERATOR", "operator", "Operators", |
|
16, "SCE_NIM_IDENTIFIER", "identifier", "Identifiers", |
|
}; |
|
|
|
} |
|
|
|
class LexerNim : public DefaultLexer { |
|
CharacterSet setWord; |
|
WordList keywords; |
|
OptionsNim options; |
|
OptionSetNim osNim; |
|
|
|
public: |
|
LexerNim() : |
|
DefaultLexer("nim", SCLEX_NIM, lexicalClasses, ELEMENTS(lexicalClasses)), |
|
setWord(CharacterSet::setAlphaNum, "_", 0x80, true) { } |
|
|
|
virtual ~LexerNim() { } |
|
|
|
void SCI_METHOD Release() noexcept override { |
|
delete this; |
|
} |
|
|
|
int SCI_METHOD Version() const noexcept override { |
|
return lvRelease5; |
|
} |
|
|
|
const char * SCI_METHOD PropertyNames() override { |
|
return osNim.PropertyNames(); |
|
} |
|
|
|
int SCI_METHOD PropertyType(const char *name) override { |
|
return osNim.PropertyType(name); |
|
} |
|
|
|
const char * SCI_METHOD DescribeProperty(const char *name) override { |
|
return osNim.DescribeProperty(name); |
|
} |
|
|
|
Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override; |
|
|
|
const char * SCI_METHOD PropertyGet(const char* key) override { |
|
return osNim.PropertyGet(key); |
|
} |
|
|
|
const char * SCI_METHOD DescribeWordListSets() override { |
|
return osNim.DescribeWordListSets(); |
|
} |
|
|
|
Sci_Position SCI_METHOD WordListSet(int n, const char *wl) override; |
|
|
|
void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override; |
|
void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override; |
|
|
|
void * SCI_METHOD PrivateCall(int, void *) noexcept override { |
|
return nullptr; |
|
} |
|
|
|
int SCI_METHOD LineEndTypesSupported() noexcept override { |
|
return SC_LINE_END_TYPE_UNICODE; |
|
} |
|
|
|
int SCI_METHOD PrimaryStyleFromStyle(int style) noexcept override { |
|
return style; |
|
} |
|
|
|
static ILexer5 *LexerFactoryNim() { |
|
return new LexerNim(); |
|
} |
|
}; |
|
|
|
Sci_Position SCI_METHOD LexerNim::PropertySet(const char *key, const char *val) { |
|
if (osNim.PropertySet(&options, key, val)) { |
|
return 0; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
Sci_Position SCI_METHOD LexerNim::WordListSet(int n, const char *wl) { |
|
WordList *wordListN = nullptr; |
|
|
|
switch (n) { |
|
case 0: |
|
wordListN = &keywords; |
|
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 LexerNim::Lex(Sci_PositionU startPos, Sci_Position length, |
|
int initStyle, IDocument *pAccess) { |
|
// No one likes a leaky string |
|
if (initStyle == SCE_NIM_STRINGEOL) { |
|
initStyle = SCE_NIM_DEFAULT; |
|
} |
|
|
|
Accessor styler(pAccess, nullptr); |
|
StyleContext sc(startPos, length, initStyle, styler); |
|
|
|
// Nim supports nested block comments! |
|
Sci_Position lineCurrent = styler.GetLine(startPos); |
|
int commentNestLevel = lineCurrent > 0 ? styler.GetLineState(lineCurrent - 1) : 0; |
|
|
|
int numType = NumType::Decimal; |
|
int decimalCount = 0; |
|
|
|
bool funcNameExists = false; |
|
bool isStylingRawString = false; |
|
bool isStylingRawStringIdent = false; |
|
|
|
for (; sc.More(); sc.Forward()) { |
|
if (sc.atLineStart) { |
|
if (sc.state == SCE_NIM_STRING) { |
|
sc.SetState(SCE_NIM_STRING); |
|
} |
|
|
|
lineCurrent = styler.GetLine(sc.currentPos); |
|
styler.SetLineState(lineCurrent, commentNestLevel); |
|
} |
|
|
|
// Handle string line continuation |
|
if (sc.ch == '\\' && (sc.chNext == '\n' || sc.chNext == '\r') && |
|
(sc.state == SCE_NIM_STRING || sc.state == SCE_NIM_CHARACTER) && !isStylingRawString) { |
|
sc.Forward(); |
|
|
|
if (sc.ch == '\r' && sc.chNext == '\n') { |
|
sc.Forward(); |
|
} |
|
|
|
continue; |
|
} |
|
|
|
switch (sc.state) { |
|
case SCE_NIM_OPERATOR: |
|
funcNameExists = false; |
|
sc.SetState(SCE_NIM_DEFAULT); |
|
break; |
|
case SCE_NIM_NUMBER: |
|
// For a type suffix, such as 0x80'u8 |
|
if (sc.ch == '\'') { |
|
if (sc.chNext == 'i' || sc.chNext == 'I' || |
|
sc.chNext == 'u' || sc.chNext == 'U' || |
|
sc.chNext == 'f' || sc.chNext == 'F' || |
|
sc.chNext == 'd' || sc.chNext == 'D') { |
|
sc.Forward(2); |
|
} |
|
} else if (sc.ch == '.') { |
|
if (IsADigit(sc.chNext)) { |
|
sc.Forward(); |
|
} else if (numType <= NumType::Exponent) { |
|
sc.SetState(SCE_NIM_OPERATOR); |
|
break; |
|
} else { |
|
decimalCount++; |
|
|
|
if (numType == NumType::Decimal) { |
|
if (decimalCount <= 1 && !IsAWordChar(sc.chNext)) { |
|
break; |
|
} |
|
} else if (numType == NumType::Hexadecimal) { |
|
if (decimalCount <= 1 && IsADigit(sc.chNext, 16)) { |
|
break; |
|
} |
|
|
|
sc.SetState(SCE_NIM_OPERATOR); |
|
break; |
|
} |
|
} |
|
} else if (sc.ch == '_') { |
|
// Accept only one underscore between digits |
|
if (IsADigit(sc.chNext)) { |
|
sc.Forward(); |
|
} |
|
} else if (numType == NumType::Decimal) { |
|
if (sc.chPrev != '\'' && (sc.ch == 'e' || sc.ch == 'E')) { |
|
numType = NumType::Exponent; |
|
|
|
if (sc.chNext == '-' || sc.chNext == '+') { |
|
sc.Forward(); |
|
} |
|
|
|
break; |
|
} |
|
|
|
if (IsADigit(sc.ch)) { |
|
break; |
|
} |
|
} else if (numType == NumType::Hexadecimal) { |
|
if (IsADigit(sc.ch, 16)) { |
|
break; |
|
} |
|
} else if (IsADigit(sc.ch)) { |
|
if (numType == NumType::Exponent) { |
|
break; |
|
} |
|
|
|
if (numType == NumType::Octal) { |
|
// Accept only 0-7 |
|
if (sc.ch <= '7') { |
|
break; |
|
} |
|
} else if (numType == NumType::Binary) { |
|
// Accept only 0 and 1 |
|
if (sc.ch <= '1') { |
|
break; |
|
} |
|
} |
|
|
|
numType = NumType::FormatError; |
|
break; |
|
} |
|
|
|
sc.ChangeState(GetNumStyle(numType)); |
|
sc.SetState(SCE_NIM_DEFAULT); |
|
break; |
|
case SCE_NIM_IDENTIFIER: |
|
if (sc.ch == '.' || !IsAWordChar(sc.ch)) { |
|
char s[100]; |
|
sc.GetCurrent(s, sizeof(s)); |
|
int style = SCE_NIM_IDENTIFIER; |
|
|
|
if (keywords.InList(s) && !funcNameExists) { |
|
// Prevent styling keywords if they are sub-identifiers |
|
const Sci_Position segStart = styler.GetStartSegment() - 1; |
|
if (segStart < 0 || styler.SafeGetCharAt(segStart, '\0') != '.') { |
|
style = SCE_NIM_WORD; |
|
} |
|
} else if (funcNameExists) { |
|
style = SCE_NIM_FUNCNAME; |
|
} |
|
|
|
sc.ChangeState(style); |
|
sc.SetState(SCE_NIM_DEFAULT); |
|
|
|
if (style == SCE_NIM_WORD) { |
|
funcNameExists = IsFuncName(s); |
|
} else { |
|
funcNameExists = false; |
|
} |
|
} |
|
|
|
if (IsAlphaNumeric(sc.ch) && sc.chNext == '\"') { |
|
isStylingRawStringIdent = true; |
|
|
|
if (options.highlightRawStrIdent) { |
|
if (styler.SafeGetCharAt(sc.currentPos + 2) == '\"' && |
|
styler.SafeGetCharAt(sc.currentPos + 3) == '\"') { |
|
sc.ChangeState(SCE_NIM_TRIPLEDOUBLE); |
|
} else { |
|
sc.ChangeState(SCE_NIM_STRING); |
|
} |
|
} |
|
|
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} |
|
break; |
|
case SCE_NIM_FUNCNAME: |
|
if (sc.ch == '`') { |
|
funcNameExists = false; |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} else if (sc.atLineEnd) { |
|
// Prevent leaking the style to the next line if not closed |
|
funcNameExists = false; |
|
|
|
sc.ChangeState(SCE_NIM_STRINGEOL); |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} |
|
break; |
|
case SCE_NIM_COMMENT: |
|
if (sc.Match(']', '#')) { |
|
if (commentNestLevel > 0) { |
|
commentNestLevel--; |
|
} |
|
|
|
lineCurrent = styler.GetLine(sc.currentPos); |
|
styler.SetLineState(lineCurrent, commentNestLevel); |
|
sc.Forward(); |
|
|
|
if (commentNestLevel == 0) { |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} |
|
} else if (sc.Match('#', '[')) { |
|
commentNestLevel++; |
|
lineCurrent = styler.GetLine(sc.currentPos); |
|
styler.SetLineState(lineCurrent, commentNestLevel); |
|
} |
|
break; |
|
case SCE_NIM_COMMENTDOC: |
|
if (sc.Match("]##")) { |
|
if (commentNestLevel > 0) { |
|
commentNestLevel--; |
|
} |
|
|
|
lineCurrent = styler.GetLine(sc.currentPos); |
|
styler.SetLineState(lineCurrent, commentNestLevel); |
|
sc.Forward(2); |
|
|
|
if (commentNestLevel == 0) { |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} |
|
} else if (sc.Match("##[")) { |
|
commentNestLevel++; |
|
lineCurrent = styler.GetLine(sc.currentPos); |
|
styler.SetLineState(lineCurrent, commentNestLevel); |
|
} |
|
break; |
|
case SCE_NIM_COMMENTLINE: |
|
case SCE_NIM_COMMENTLINEDOC: |
|
if (sc.atLineStart) { |
|
sc.SetState(SCE_NIM_DEFAULT); |
|
} |
|
break; |
|
case SCE_NIM_STRING: |
|
if (!isStylingRawStringIdent && !isStylingRawString && sc.ch == '\\') { |
|
if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { |
|
sc.Forward(); |
|
} |
|
} else if (isStylingRawString && sc.ch == '\"' && sc.chNext == '\"') { |
|
// Forward in situations such as r"a""bc\" so that "bc\" wouldn't be |
|
// considered a string of its own |
|
sc.Forward(); |
|
} else if (sc.ch == '\"') { |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} else if (sc.atLineEnd) { |
|
sc.ChangeState(SCE_NIM_STRINGEOL); |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} |
|
break; |
|
case SCE_NIM_CHARACTER: |
|
if (sc.ch == '\\') { |
|
if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { |
|
sc.Forward(); |
|
} |
|
} else if (sc.ch == '\'') { |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} else if (sc.atLineEnd) { |
|
sc.ChangeState(SCE_NIM_STRINGEOL); |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} |
|
break; |
|
case SCE_NIM_BACKTICKS: |
|
if (sc.ch == '`' ) { |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} else if (sc.atLineEnd) { |
|
sc.ChangeState(SCE_NIM_STRINGEOL); |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} |
|
break; |
|
case SCE_NIM_TRIPLEDOUBLE: |
|
if (sc.Match(R"(""")")) { |
|
|
|
// Outright forward all " after the closing """ as a triple double |
|
// |
|
// A valid example where this is needed is: """8 double quotes->"""""""" |
|
// You can have as many """ at the end as you wish, as long as the actual |
|
// closing literal is there |
|
while (sc.ch == '"') { |
|
sc.Forward(); |
|
} |
|
|
|
sc.SetState(SCE_NIM_DEFAULT); |
|
} |
|
break; |
|
case SCE_NIM_TRIPLE: |
|
if (sc.Match("'''")) { |
|
sc.Forward(2); |
|
sc.ForwardSetState(SCE_NIM_DEFAULT); |
|
} |
|
break; |
|
} |
|
|
|
if (sc.state == SCE_NIM_DEFAULT) { |
|
// Number |
|
if (IsADigit(sc.ch)) { |
|
sc.SetState(SCE_NIM_NUMBER); |
|
|
|
numType = NumType::Decimal; |
|
decimalCount = 0; |
|
|
|
if (sc.ch == '0') { |
|
if (IsNumHex(sc)) { |
|
numType = NumType::Hexadecimal; |
|
} else if (IsNumBinary(sc)) { |
|
numType = NumType::Binary; |
|
} else if (IsNumOctal(sc)) { |
|
numType = NumType::Octal; |
|
} |
|
|
|
if (numType != NumType::Decimal) { |
|
sc.Forward(); |
|
} |
|
} |
|
} |
|
// Raw string |
|
else if (IsAlphaNumeric(sc.ch) && sc.chNext == '\"') { |
|
isStylingRawString = true; |
|
|
|
// Triple doubles can be raw strings too. How sweet |
|
if (styler.SafeGetCharAt(sc.currentPos + 2) == '\"' && |
|
styler.SafeGetCharAt(sc.currentPos + 3) == '\"') { |
|
sc.SetState(SCE_NIM_TRIPLEDOUBLE); |
|
} else { |
|
sc.SetState(SCE_NIM_STRING); |
|
} |
|
|
|
const int rawStrStyle = options.highlightRawStrIdent ? IsLetter(sc.ch) : |
|
(sc.ch == 'r' || sc.ch == 'R'); |
|
|
|
if (rawStrStyle) { |
|
sc.Forward(); |
|
|
|
if (sc.state == SCE_NIM_TRIPLEDOUBLE) { |
|
sc.Forward(2); |
|
} |
|
} else { |
|
// Anything other than r/R is considered a general raw string identifier |
|
isStylingRawStringIdent = true; |
|
sc.SetState(SCE_NIM_IDENTIFIER); |
|
} |
|
} |
|
// String and triple double literal |
|
else if (sc.ch == '\"') { |
|
isStylingRawString = false; |
|
|
|
if (sc.Match(R"(""")")) { |
|
sc.SetState(SCE_NIM_TRIPLEDOUBLE); |
|
|
|
// Keep forwarding until the total opening literal count is 5 |
|
// A valid example where this is needed is: """""<-5 double quotes""" |
|
while (sc.ch == '"') { |
|
sc.Forward(); |
|
|
|
if (sc.Match(R"(""")")) { |
|
sc.Forward(); |
|
break; |
|
} |
|
} |
|
} else { |
|
sc.SetState(SCE_NIM_STRING); |
|
} |
|
} |
|
// Charecter and triple literal |
|
else if (sc.ch == '\'') { |
|
if (sc.Match("'''")) { |
|
sc.SetState(SCE_NIM_TRIPLE); |
|
} else { |
|
sc.SetState(SCE_NIM_CHARACTER); |
|
} |
|
} |
|
// Operator definition |
|
else if (sc.ch == '`') { |
|
if (funcNameExists) { |
|
sc.SetState(SCE_NIM_FUNCNAME); |
|
} else { |
|
sc.SetState(SCE_NIM_BACKTICKS); |
|
} |
|
} |
|
// Keyword |
|
else if (iswordstart(sc.ch)) { |
|
sc.SetState(SCE_NIM_IDENTIFIER); |
|
} |
|
// Comments |
|
else if (sc.ch == '#') { |
|
if (sc.Match("##[") || sc.Match("#[")) { |
|
commentNestLevel++; |
|
lineCurrent = styler.GetLine(sc.currentPos); |
|
styler.SetLineState(lineCurrent, commentNestLevel); |
|
} |
|
|
|
if (sc.Match("##[")) { |
|
sc.SetState(SCE_NIM_COMMENTDOC); |
|
sc.Forward(); |
|
} else if (sc.Match("#[")) { |
|
sc.SetState(SCE_NIM_COMMENT); |
|
sc.Forward(); |
|
} else if (sc.Match("##")) { |
|
sc.SetState(SCE_NIM_COMMENTLINEDOC); |
|
} else { |
|
sc.SetState(SCE_NIM_COMMENTLINE); |
|
} |
|
} |
|
// Operators |
|
else if (strchr("()[]{}:=;-\\/&%$!+<>|^?,.*~@", sc.ch)) { |
|
sc.SetState(SCE_NIM_OPERATOR); |
|
} |
|
} |
|
|
|
if (sc.atLineEnd) { |
|
funcNameExists = false; |
|
isStylingRawString = false; |
|
isStylingRawStringIdent = false; |
|
} |
|
} |
|
|
|
sc.Complete(); |
|
} |
|
|
|
void SCI_METHOD LexerNim::Fold(Sci_PositionU startPos, Sci_Position length, int, IDocument *pAccess) { |
|
if (!options.fold) { |
|
return; |
|
} |
|
|
|
Accessor styler(pAccess, nullptr); |
|
|
|
const Sci_Position docLines = styler.GetLine(styler.Length()); |
|
const Sci_Position maxPos = startPos + length; |
|
const Sci_Position maxLines = styler.GetLine(maxPos == styler.Length() ? maxPos : maxPos - 1); |
|
|
|
Sci_Position lineCurrent = styler.GetLine(startPos); |
|
int indentCurrent = IndentAmount(lineCurrent, styler); |
|
|
|
while (lineCurrent > 0) { |
|
lineCurrent--; |
|
indentCurrent = IndentAmount(lineCurrent, styler); |
|
|
|
if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { |
|
break; |
|
} |
|
} |
|
|
|
int indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK; |
|
indentCurrent = indentCurrentLevel | (indentCurrent & ~SC_FOLDLEVELNUMBERMASK); |
|
|
|
while (lineCurrent <= docLines && lineCurrent <= maxLines) { |
|
Sci_Position lineNext = lineCurrent + 1; |
|
int indentNext = indentCurrent; |
|
int lev = indentCurrent; |
|
|
|
if (lineNext <= docLines) { |
|
indentNext = IndentAmount(lineNext, styler); |
|
} |
|
|
|
if (indentNext & SC_FOLDLEVELWHITEFLAG) { |
|
indentNext = SC_FOLDLEVELWHITEFLAG | indentCurrentLevel; |
|
} |
|
|
|
while (lineNext < docLines && (indentNext & SC_FOLDLEVELWHITEFLAG)) { |
|
lineNext++; |
|
indentNext = IndentAmount(lineNext, styler); |
|
} |
|
|
|
const int indentNextLevel = indentNext & SC_FOLDLEVELNUMBERMASK; |
|
indentNext = indentNextLevel | (indentNext & ~SC_FOLDLEVELNUMBERMASK); |
|
|
|
const int levelBeforeComments = std::max(indentCurrentLevel, indentNextLevel); |
|
|
|
Sci_Position skipLine = lineNext; |
|
int skipLevel = indentNextLevel; |
|
|
|
while (--skipLine > lineCurrent) { |
|
const int skipLineIndent = IndentAmount(skipLine, styler); |
|
|
|
if (options.foldCompact) { |
|
if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > indentNextLevel) { |
|
skipLevel = levelBeforeComments; |
|
} |
|
|
|
const int whiteFlag = skipLineIndent & SC_FOLDLEVELWHITEFLAG; |
|
styler.SetLevel(skipLine, skipLevel | whiteFlag); |
|
} else { |
|
if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > indentNextLevel && |
|
!(skipLineIndent & SC_FOLDLEVELWHITEFLAG)) { |
|
skipLevel = levelBeforeComments; |
|
} |
|
|
|
styler.SetLevel(skipLine, skipLevel); |
|
} |
|
} |
|
|
|
if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { |
|
if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) { |
|
lev |= SC_FOLDLEVELHEADERFLAG; |
|
} |
|
} |
|
|
|
styler.SetLevel(lineCurrent, options.foldCompact ? lev : lev & ~SC_FOLDLEVELWHITEFLAG); |
|
|
|
indentCurrent = indentNext; |
|
indentCurrentLevel = indentNextLevel; |
|
lineCurrent = lineNext; |
|
} |
|
} |
|
|
|
extern const LexerModule lmNim(SCLEX_NIM, LexerNim::LexerFactoryNim, "nim", nimWordListDesc); |