1 // Scintilla source code edit control
2 /** @file LexNim.cxx
3 ** Lexer for Nim
4 ** Written by Jad Altahan (github.com/xv)
5 ** Nim manual: https://nim-lang.org/docs/manual.html
6 **/
7 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
8 // The License.txt file describes the conditions under which this software may be distributed.
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <assert.h>
15 #include <ctype.h>
16
17 #include <string>
18 #include <map>
19 #include <algorithm>
20
21 #include "ILexer.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
24
25 #include "StringCopy.h"
26 #include "WordList.h"
27 #include "LexAccessor.h"
28 #include "Accessor.h"
29 #include "StyleContext.h"
30 #include "CharacterSet.h"
31 #include "LexerModule.h"
32 #include "OptionSet.h"
33 #include "DefaultLexer.h"
34
35 using namespace Scintilla;
36
37 namespace {
38 // Use an unnamed namespace to protect the functions and classes from name conflicts
39
40 enum NumType {
41 Binary,
42 Octal,
43 Exponent,
44 Hexadecimal,
45 Decimal,
46 FormatError
47 };
48
GetNumStyle(const int numType)49 int GetNumStyle(const int numType) noexcept {
50 if (numType == NumType::FormatError) {
51 return SCE_NIM_NUMERROR;
52 }
53
54 return SCE_NIM_NUMBER;
55 }
56
IsLetter(const int ch)57 constexpr bool IsLetter(const int ch) noexcept {
58 // 97 to 122 || 65 to 90
59 return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
60 }
61
IsAWordChar(const int ch)62 bool IsAWordChar(const int ch) noexcept {
63 return ch < 0x80 && (isalnum(ch) || ch == '_' || ch == '.');
64 }
65
IsNumHex(const StyleContext & sc)66 int IsNumHex(const StyleContext &sc) noexcept {
67 return sc.chNext == 'x' || sc.chNext == 'X';
68 }
69
IsNumBinary(const StyleContext & sc)70 int IsNumBinary(const StyleContext &sc) noexcept {
71 return sc.chNext == 'b' || sc.chNext == 'B';
72 }
73
IsNumOctal(const StyleContext & sc)74 int IsNumOctal(const StyleContext &sc) {
75 return IsADigit(sc.chNext) || sc.chNext == 'o';
76 }
77
IsNewline(const int ch)78 constexpr bool IsNewline(const int ch) noexcept {
79 return (ch == '\n' || ch == '\r');
80 }
81
IsFuncName(const char * str)82 bool IsFuncName(const char *str) noexcept {
83 const char *identifiers[] = {
84 "proc",
85 "func",
86 "macro",
87 "method",
88 "template",
89 "iterator",
90 "converter"
91 };
92
93 for (const char *id : identifiers) {
94 if (strcmp(str, id) == 0) {
95 return true;
96 }
97 }
98
99 return false;
100 }
101
IsTripleLiteral(const int style)102 constexpr bool IsTripleLiteral(const int style) noexcept {
103 return style == SCE_NIM_TRIPLE || style == SCE_NIM_TRIPLEDOUBLE;
104 }
105
IsLineComment(const int style)106 constexpr bool IsLineComment(const int style) noexcept {
107 return style == SCE_NIM_COMMENTLINE || style == SCE_NIM_COMMENTLINEDOC;
108 }
109
IsStreamComment(const int style)110 constexpr bool IsStreamComment(const int style) noexcept {
111 return style == SCE_NIM_COMMENT || style == SCE_NIM_COMMENTDOC;
112 }
113
114 // Adopted from Accessor.cxx
GetIndent(const Sci_Position line,Accessor & styler)115 int GetIndent(const Sci_Position line, Accessor &styler) {
116 Sci_Position startPos = styler.LineStart(line);
117 const Sci_Position eolPos = styler.LineStart(line + 1) - 1;
118
119 char ch = styler[startPos];
120 int style = styler.StyleAt(startPos);
121
122 int indent = 0;
123 bool inPrevPrefix = line > 0;
124 Sci_Position posPrev = inPrevPrefix ? styler.LineStart(line - 1) : 0;
125
126 // No fold points inside triple literals
127 while ((IsASpaceOrTab(ch) || IsTripleLiteral(style)) && (startPos < eolPos)) {
128 if (inPrevPrefix) {
129 const char chPrev = styler[posPrev++];
130 if (chPrev != ' ' && chPrev != '\t') {
131 inPrevPrefix = false;
132 }
133 }
134
135 if (ch == '\t') {
136 indent = (indent / 8 + 1) * 8;
137 } else {
138 indent++;
139 }
140
141 startPos++;
142 ch = styler[startPos];
143 style = styler.StyleAt(startPos);
144 }
145
146 // Prevent creating fold lines for comments if indented
147 if (!(IsStreamComment(style) || IsLineComment(style)))
148 indent += SC_FOLDLEVELBASE;
149
150 if (styler.LineStart(line) == styler.Length()
151 || IsASpaceOrTab(ch)
152 || IsNewline(ch)
153 || IsStreamComment(style)
154 || IsLineComment(style)) {
155 return indent | SC_FOLDLEVELWHITEFLAG;
156 } else {
157 return indent;
158 }
159 }
160
IndentAmount(const Sci_Position line,Accessor & styler)161 int IndentAmount(const Sci_Position line, Accessor &styler) {
162 const int indent = GetIndent(line, styler);
163 const int indentLevel = indent & SC_FOLDLEVELNUMBERMASK;
164 return indentLevel <= SC_FOLDLEVELBASE ? indent : indentLevel | (indent & ~SC_FOLDLEVELNUMBERMASK);
165 }
166
167 struct OptionsNim {
168 bool fold;
169 bool foldCompact;
170 bool highlightRawStrIdent;
171
OptionsNim__anon5197a16d0111::OptionsNim172 OptionsNim() {
173 fold = true;
174 foldCompact = true;
175 highlightRawStrIdent = false;
176 }
177 };
178
179 static const char *const nimWordListDesc[] = {
180 "Keywords",
181 nullptr
182 };
183
184 struct OptionSetNim : public OptionSet<OptionsNim> {
OptionSetNim__anon5197a16d0111::OptionSetNim185 OptionSetNim() {
186 DefineProperty("lexer.nim.raw.strings.highlight.ident", &OptionsNim::highlightRawStrIdent,
187 "Set to 1 to enable highlighting generalized raw string identifiers. "
188 "Generalized raw string identifiers are anything other than r (or R).");
189
190 DefineProperty("fold", &OptionsNim::fold);
191 DefineProperty("fold.compact", &OptionsNim::foldCompact);
192
193 DefineWordListSets(nimWordListDesc);
194 }
195 };
196
197 LexicalClass lexicalClasses[] = {
198 // Lexer Nim SCLEX_NIM SCE_NIM_:
199 0, "SCE_NIM_DEFAULT", "default", "White space",
200 1, "SCE_NIM_COMMENT", "comment block", "Block comment",
201 2, "SCE_NIM_COMMENTDOC", "comment block doc", "Block doc comment",
202 3, "SCE_NIM_COMMENTLINE", "comment line", "Line comment",
203 4, "SCE_NIM_COMMENTLINEDOC", "comment doc", "Line doc comment",
204 5, "SCE_NIM_NUMBER", "literal numeric", "Number",
205 6, "SCE_NIM_STRING", "literal string", "String",
206 7, "SCE_NIM_CHARACTER", "literal string", "Single quoted string",
207 8, "SCE_NIM_WORD", "keyword", "Keyword",
208 9, "SCE_NIM_TRIPLE", "literal string", "Triple quotes",
209 10, "SCE_NIM_TRIPLEDOUBLE", "literal string", "Triple double quotes",
210 11, "SCE_NIM_BACKTICKS", "operator definition", "Identifiers",
211 12, "SCE_NIM_FUNCNAME", "identifier", "Function name definition",
212 13, "SCE_NIM_STRINGEOL", "error literal string", "String is not closed",
213 14, "SCE_NIM_NUMERROR", "numeric error", "Numeric format error",
214 15, "SCE_NIM_OPERATOR", "operator", "Operators",
215 16, "SCE_NIM_IDENTIFIER", "identifier", "Identifiers",
216 };
217
218 }
219
220 class LexerNim : public DefaultLexer {
221 CharacterSet setWord;
222 WordList keywords;
223 OptionsNim options;
224 OptionSetNim osNim;
225
226 public:
LexerNim()227 LexerNim() :
228 DefaultLexer("nim", SCLEX_NIM, lexicalClasses, ELEMENTS(lexicalClasses)),
229 setWord(CharacterSet::setAlphaNum, "_", 0x80, true) { }
230
~LexerNim()231 virtual ~LexerNim() { }
232
Release()233 void SCI_METHOD Release() noexcept override {
234 delete this;
235 }
236
Version() const237 int SCI_METHOD Version() const noexcept override {
238 return lvRelease5;
239 }
240
PropertyNames()241 const char * SCI_METHOD PropertyNames() override {
242 return osNim.PropertyNames();
243 }
244
PropertyType(const char * name)245 int SCI_METHOD PropertyType(const char *name) override {
246 return osNim.PropertyType(name);
247 }
248
DescribeProperty(const char * name)249 const char * SCI_METHOD DescribeProperty(const char *name) override {
250 return osNim.DescribeProperty(name);
251 }
252
253 Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override;
254
PropertyGet(const char * key)255 const char * SCI_METHOD PropertyGet(const char* key) override {
256 return osNim.PropertyGet(key);
257 }
258
DescribeWordListSets()259 const char * SCI_METHOD DescribeWordListSets() override {
260 return osNim.DescribeWordListSets();
261 }
262
263 Sci_Position SCI_METHOD WordListSet(int n, const char *wl) override;
264
265 void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
266 void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
267
PrivateCall(int,void *)268 void * SCI_METHOD PrivateCall(int, void *) noexcept override {
269 return nullptr;
270 }
271
LineEndTypesSupported()272 int SCI_METHOD LineEndTypesSupported() noexcept override {
273 return SC_LINE_END_TYPE_UNICODE;
274 }
275
PrimaryStyleFromStyle(int style)276 int SCI_METHOD PrimaryStyleFromStyle(int style) noexcept override {
277 return style;
278 }
279
LexerFactoryNim()280 static ILexer5 *LexerFactoryNim() {
281 return new LexerNim();
282 }
283 };
284
PropertySet(const char * key,const char * val)285 Sci_Position SCI_METHOD LexerNim::PropertySet(const char *key, const char *val) {
286 if (osNim.PropertySet(&options, key, val)) {
287 return 0;
288 }
289
290 return -1;
291 }
292
WordListSet(int n,const char * wl)293 Sci_Position SCI_METHOD LexerNim::WordListSet(int n, const char *wl) {
294 WordList *wordListN = nullptr;
295
296 switch (n) {
297 case 0:
298 wordListN = &keywords;
299 break;
300 }
301
302 Sci_Position firstModification = -1;
303
304 if (wordListN) {
305 WordList wlNew;
306 wlNew.Set(wl);
307
308 if (*wordListN != wlNew) {
309 wordListN->Set(wl);
310 firstModification = 0;
311 }
312 }
313
314 return firstModification;
315 }
316
Lex(Sci_PositionU startPos,Sci_Position length,int initStyle,IDocument * pAccess)317 void SCI_METHOD LexerNim::Lex(Sci_PositionU startPos, Sci_Position length,
318 int initStyle, IDocument *pAccess) {
319 // No one likes a leaky string
320 if (initStyle == SCE_NIM_STRINGEOL) {
321 initStyle = SCE_NIM_DEFAULT;
322 }
323
324 Accessor styler(pAccess, nullptr);
325 StyleContext sc(startPos, length, initStyle, styler);
326
327 // Nim supports nested block comments!
328 Sci_Position lineCurrent = styler.GetLine(startPos);
329 int commentNestLevel = lineCurrent > 0 ? styler.GetLineState(lineCurrent - 1) : 0;
330
331 int numType = NumType::Decimal;
332 int decimalCount = 0;
333
334 bool funcNameExists = false;
335 bool isStylingRawString = false;
336 bool isStylingRawStringIdent = false;
337
338 for (; sc.More(); sc.Forward()) {
339 if (sc.atLineStart) {
340 if (sc.state == SCE_NIM_STRING) {
341 sc.SetState(SCE_NIM_STRING);
342 }
343
344 lineCurrent = styler.GetLine(sc.currentPos);
345 styler.SetLineState(lineCurrent, commentNestLevel);
346 }
347
348 // Handle string line continuation
349 if (sc.ch == '\\' && (sc.chNext == '\n' || sc.chNext == '\r') &&
350 (sc.state == SCE_NIM_STRING || sc.state == SCE_NIM_CHARACTER) && !isStylingRawString) {
351 sc.Forward();
352
353 if (sc.ch == '\r' && sc.chNext == '\n') {
354 sc.Forward();
355 }
356
357 continue;
358 }
359
360 switch (sc.state) {
361 case SCE_NIM_OPERATOR:
362 funcNameExists = false;
363 sc.SetState(SCE_NIM_DEFAULT);
364 break;
365 case SCE_NIM_NUMBER:
366 // For a type suffix, such as 0x80'u8
367 if (sc.ch == '\'') {
368 if (sc.chNext == 'i' || sc.chNext == 'I' ||
369 sc.chNext == 'u' || sc.chNext == 'U' ||
370 sc.chNext == 'f' || sc.chNext == 'F' ||
371 sc.chNext == 'd' || sc.chNext == 'D') {
372 sc.Forward(2);
373 }
374 } else if (sc.ch == '.') {
375 if (IsADigit(sc.chNext)) {
376 sc.Forward();
377 } else if (numType <= NumType::Exponent) {
378 sc.SetState(SCE_NIM_OPERATOR);
379 break;
380 } else {
381 decimalCount++;
382
383 if (numType == NumType::Decimal) {
384 if (decimalCount <= 1 && !IsAWordChar(sc.chNext)) {
385 break;
386 }
387 } else if (numType == NumType::Hexadecimal) {
388 if (decimalCount <= 1 && IsADigit(sc.chNext, 16)) {
389 break;
390 }
391
392 sc.SetState(SCE_NIM_OPERATOR);
393 break;
394 }
395 }
396 } else if (sc.ch == '_') {
397 // Accept only one underscore between digits
398 if (IsADigit(sc.chNext)) {
399 sc.Forward();
400 }
401 } else if (numType == NumType::Decimal) {
402 if (sc.chPrev != '\'' && (sc.ch == 'e' || sc.ch == 'E')) {
403 numType = NumType::Exponent;
404
405 if (sc.chNext == '-' || sc.chNext == '+') {
406 sc.Forward();
407 }
408
409 break;
410 }
411
412 if (IsADigit(sc.ch)) {
413 break;
414 }
415 } else if (numType == NumType::Hexadecimal) {
416 if (IsADigit(sc.ch, 16)) {
417 break;
418 }
419 } else if (IsADigit(sc.ch)) {
420 if (numType == NumType::Exponent) {
421 break;
422 }
423
424 if (numType == NumType::Octal) {
425 // Accept only 0-7
426 if (sc.ch <= '7') {
427 break;
428 }
429 } else if (numType == NumType::Binary) {
430 // Accept only 0 and 1
431 if (sc.ch <= '1') {
432 break;
433 }
434 }
435
436 numType = NumType::FormatError;
437 break;
438 }
439
440 sc.ChangeState(GetNumStyle(numType));
441 sc.SetState(SCE_NIM_DEFAULT);
442 break;
443 case SCE_NIM_IDENTIFIER:
444 if (sc.ch == '.' || !IsAWordChar(sc.ch)) {
445 char s[100];
446 sc.GetCurrent(s, sizeof(s));
447 int style = SCE_NIM_IDENTIFIER;
448
449 if (keywords.InList(s) && !funcNameExists) {
450 // Prevent styling keywords if they are sub-identifiers
451 const Sci_Position segStart = styler.GetStartSegment() - 1;
452 if (segStart < 0 || styler.SafeGetCharAt(segStart, '\0') != '.') {
453 style = SCE_NIM_WORD;
454 }
455 } else if (funcNameExists) {
456 style = SCE_NIM_FUNCNAME;
457 }
458
459 sc.ChangeState(style);
460 sc.SetState(SCE_NIM_DEFAULT);
461
462 if (style == SCE_NIM_WORD) {
463 funcNameExists = IsFuncName(s);
464 } else {
465 funcNameExists = false;
466 }
467 }
468
469 if (IsAlphaNumeric(sc.ch) && sc.chNext == '\"') {
470 isStylingRawStringIdent = true;
471
472 if (options.highlightRawStrIdent) {
473 if (styler.SafeGetCharAt(sc.currentPos + 2) == '\"' &&
474 styler.SafeGetCharAt(sc.currentPos + 3) == '\"') {
475 sc.ChangeState(SCE_NIM_TRIPLEDOUBLE);
476 } else {
477 sc.ChangeState(SCE_NIM_STRING);
478 }
479 }
480
481 sc.ForwardSetState(SCE_NIM_DEFAULT);
482 }
483 break;
484 case SCE_NIM_FUNCNAME:
485 if (sc.ch == '`') {
486 funcNameExists = false;
487 sc.ForwardSetState(SCE_NIM_DEFAULT);
488 } else if (sc.atLineEnd) {
489 // Prevent leaking the style to the next line if not closed
490 funcNameExists = false;
491
492 sc.ChangeState(SCE_NIM_STRINGEOL);
493 sc.ForwardSetState(SCE_NIM_DEFAULT);
494 }
495 break;
496 case SCE_NIM_COMMENT:
497 if (sc.Match(']', '#')) {
498 if (commentNestLevel > 0) {
499 commentNestLevel--;
500 }
501
502 lineCurrent = styler.GetLine(sc.currentPos);
503 styler.SetLineState(lineCurrent, commentNestLevel);
504 sc.Forward();
505
506 if (commentNestLevel == 0) {
507 sc.ForwardSetState(SCE_NIM_DEFAULT);
508 }
509 } else if (sc.Match('#', '[')) {
510 commentNestLevel++;
511 lineCurrent = styler.GetLine(sc.currentPos);
512 styler.SetLineState(lineCurrent, commentNestLevel);
513 }
514 break;
515 case SCE_NIM_COMMENTDOC:
516 if (sc.Match("]##")) {
517 if (commentNestLevel > 0) {
518 commentNestLevel--;
519 }
520
521 lineCurrent = styler.GetLine(sc.currentPos);
522 styler.SetLineState(lineCurrent, commentNestLevel);
523 sc.Forward(2);
524
525 if (commentNestLevel == 0) {
526 sc.ForwardSetState(SCE_NIM_DEFAULT);
527 }
528 } else if (sc.Match("##[")) {
529 commentNestLevel++;
530 lineCurrent = styler.GetLine(sc.currentPos);
531 styler.SetLineState(lineCurrent, commentNestLevel);
532 }
533 break;
534 case SCE_NIM_COMMENTLINE:
535 case SCE_NIM_COMMENTLINEDOC:
536 if (sc.atLineStart) {
537 sc.SetState(SCE_NIM_DEFAULT);
538 }
539 break;
540 case SCE_NIM_STRING:
541 if (!isStylingRawStringIdent && !isStylingRawString && sc.ch == '\\') {
542 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
543 sc.Forward();
544 }
545 } else if (isStylingRawString && sc.ch == '\"' && sc.chNext == '\"') {
546 // Forward in situations such as r"a""bc\" so that "bc\" wouldn't be
547 // considered a string of its own
548 sc.Forward();
549 } else if (sc.ch == '\"') {
550 sc.ForwardSetState(SCE_NIM_DEFAULT);
551 } else if (sc.atLineEnd) {
552 sc.ChangeState(SCE_NIM_STRINGEOL);
553 sc.ForwardSetState(SCE_NIM_DEFAULT);
554 }
555 break;
556 case SCE_NIM_CHARACTER:
557 if (sc.ch == '\\') {
558 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
559 sc.Forward();
560 }
561 } else if (sc.ch == '\'') {
562 sc.ForwardSetState(SCE_NIM_DEFAULT);
563 } else if (sc.atLineEnd) {
564 sc.ChangeState(SCE_NIM_STRINGEOL);
565 sc.ForwardSetState(SCE_NIM_DEFAULT);
566 }
567 break;
568 case SCE_NIM_BACKTICKS:
569 if (sc.ch == '`' ) {
570 sc.ForwardSetState(SCE_NIM_DEFAULT);
571 } else if (sc.atLineEnd) {
572 sc.ChangeState(SCE_NIM_STRINGEOL);
573 sc.ForwardSetState(SCE_NIM_DEFAULT);
574 }
575 break;
576 case SCE_NIM_TRIPLEDOUBLE:
577 if (sc.Match(R"(""")")) {
578
579 // Outright forward all " after the closing """ as a triple double
580 //
581 // A valid example where this is needed is: """8 double quotes->""""""""
582 // You can have as many """ at the end as you wish, as long as the actual
583 // closing literal is there
584 while (sc.ch == '"') {
585 sc.Forward();
586 }
587
588 sc.SetState(SCE_NIM_DEFAULT);
589 }
590 break;
591 case SCE_NIM_TRIPLE:
592 if (sc.Match("'''")) {
593 sc.Forward(2);
594 sc.ForwardSetState(SCE_NIM_DEFAULT);
595 }
596 break;
597 }
598
599 if (sc.state == SCE_NIM_DEFAULT) {
600 // Number
601 if (IsADigit(sc.ch)) {
602 sc.SetState(SCE_NIM_NUMBER);
603
604 numType = NumType::Decimal;
605 decimalCount = 0;
606
607 if (sc.ch == '0') {
608 if (IsNumHex(sc)) {
609 numType = NumType::Hexadecimal;
610 } else if (IsNumBinary(sc)) {
611 numType = NumType::Binary;
612 } else if (IsNumOctal(sc)) {
613 numType = NumType::Octal;
614 }
615
616 if (numType != NumType::Decimal) {
617 sc.Forward();
618 }
619 }
620 }
621 // Raw string
622 else if (IsAlphaNumeric(sc.ch) && sc.chNext == '\"') {
623 isStylingRawString = true;
624
625 // Triple doubles can be raw strings too. How sweet
626 if (styler.SafeGetCharAt(sc.currentPos + 2) == '\"' &&
627 styler.SafeGetCharAt(sc.currentPos + 3) == '\"') {
628 sc.SetState(SCE_NIM_TRIPLEDOUBLE);
629 } else {
630 sc.SetState(SCE_NIM_STRING);
631 }
632
633 const int rawStrStyle = options.highlightRawStrIdent ? IsLetter(sc.ch) :
634 (sc.ch == 'r' || sc.ch == 'R');
635
636 if (rawStrStyle) {
637 sc.Forward();
638
639 if (sc.state == SCE_NIM_TRIPLEDOUBLE) {
640 sc.Forward(2);
641 }
642 } else {
643 // Anything other than r/R is considered a general raw string identifier
644 isStylingRawStringIdent = true;
645 sc.SetState(SCE_NIM_IDENTIFIER);
646 }
647 }
648 // String and triple double literal
649 else if (sc.ch == '\"') {
650 isStylingRawString = false;
651
652 if (sc.Match(R"(""")")) {
653 sc.SetState(SCE_NIM_TRIPLEDOUBLE);
654
655 // Keep forwarding until the total opening literal count is 5
656 // A valid example where this is needed is: """""<-5 double quotes"""
657 while (sc.ch == '"') {
658 sc.Forward();
659
660 if (sc.Match(R"(""")")) {
661 sc.Forward();
662 break;
663 }
664 }
665 } else {
666 sc.SetState(SCE_NIM_STRING);
667 }
668 }
669 // Charecter and triple literal
670 else if (sc.ch == '\'') {
671 if (sc.Match("'''")) {
672 sc.SetState(SCE_NIM_TRIPLE);
673 } else {
674 sc.SetState(SCE_NIM_CHARACTER);
675 }
676 }
677 // Operator definition
678 else if (sc.ch == '`') {
679 if (funcNameExists) {
680 sc.SetState(SCE_NIM_FUNCNAME);
681 } else {
682 sc.SetState(SCE_NIM_BACKTICKS);
683 }
684 }
685 // Keyword
686 else if (iswordstart(sc.ch)) {
687 sc.SetState(SCE_NIM_IDENTIFIER);
688 }
689 // Comments
690 else if (sc.ch == '#') {
691 if (sc.Match("##[") || sc.Match("#[")) {
692 commentNestLevel++;
693 lineCurrent = styler.GetLine(sc.currentPos);
694 styler.SetLineState(lineCurrent, commentNestLevel);
695 }
696
697 if (sc.Match("##[")) {
698 sc.SetState(SCE_NIM_COMMENTDOC);
699 sc.Forward();
700 } else if (sc.Match("#[")) {
701 sc.SetState(SCE_NIM_COMMENT);
702 sc.Forward();
703 } else if (sc.Match("##")) {
704 sc.SetState(SCE_NIM_COMMENTLINEDOC);
705 } else {
706 sc.SetState(SCE_NIM_COMMENTLINE);
707 }
708 }
709 // Operators
710 else if (strchr("()[]{}:=;-\\/&%$!+<>|^?,.*~@", sc.ch)) {
711 sc.SetState(SCE_NIM_OPERATOR);
712 }
713 }
714
715 if (sc.atLineEnd) {
716 funcNameExists = false;
717 isStylingRawString = false;
718 isStylingRawStringIdent = false;
719 }
720 }
721
722 sc.Complete();
723 }
724
Fold(Sci_PositionU startPos,Sci_Position length,int,IDocument * pAccess)725 void SCI_METHOD LexerNim::Fold(Sci_PositionU startPos, Sci_Position length, int, IDocument *pAccess) {
726 if (!options.fold) {
727 return;
728 }
729
730 Accessor styler(pAccess, nullptr);
731
732 const Sci_Position docLines = styler.GetLine(styler.Length());
733 const Sci_Position maxPos = startPos + length;
734 const Sci_Position maxLines = styler.GetLine(maxPos == styler.Length() ? maxPos : maxPos - 1);
735
736 Sci_Position lineCurrent = styler.GetLine(startPos);
737 int indentCurrent = IndentAmount(lineCurrent, styler);
738
739 while (lineCurrent > 0) {
740 lineCurrent--;
741 indentCurrent = IndentAmount(lineCurrent, styler);
742
743 if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
744 break;
745 }
746 }
747
748 int indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
749 indentCurrent = indentCurrentLevel | (indentCurrent & ~SC_FOLDLEVELNUMBERMASK);
750
751 while (lineCurrent <= docLines && lineCurrent <= maxLines) {
752 Sci_Position lineNext = lineCurrent + 1;
753 int indentNext = indentCurrent;
754 int lev = indentCurrent;
755
756 if (lineNext <= docLines) {
757 indentNext = IndentAmount(lineNext, styler);
758 }
759
760 if (indentNext & SC_FOLDLEVELWHITEFLAG) {
761 indentNext = SC_FOLDLEVELWHITEFLAG | indentCurrentLevel;
762 }
763
764 while (lineNext < docLines && (indentNext & SC_FOLDLEVELWHITEFLAG)) {
765 lineNext++;
766 indentNext = IndentAmount(lineNext, styler);
767 }
768
769 const int indentNextLevel = indentNext & SC_FOLDLEVELNUMBERMASK;
770 indentNext = indentNextLevel | (indentNext & ~SC_FOLDLEVELNUMBERMASK);
771
772 const int levelBeforeComments = std::max(indentCurrentLevel, indentNextLevel);
773
774 Sci_Position skipLine = lineNext;
775 int skipLevel = indentNextLevel;
776
777 while (--skipLine > lineCurrent) {
778 const int skipLineIndent = IndentAmount(skipLine, styler);
779
780 if (options.foldCompact) {
781 if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > indentNextLevel) {
782 skipLevel = levelBeforeComments;
783 }
784
785 const int whiteFlag = skipLineIndent & SC_FOLDLEVELWHITEFLAG;
786 styler.SetLevel(skipLine, skipLevel | whiteFlag);
787 } else {
788 if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > indentNextLevel &&
789 !(skipLineIndent & SC_FOLDLEVELWHITEFLAG)) {
790 skipLevel = levelBeforeComments;
791 }
792
793 styler.SetLevel(skipLine, skipLevel);
794 }
795 }
796
797 if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
798 if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
799 lev |= SC_FOLDLEVELHEADERFLAG;
800 }
801 }
802
803 styler.SetLevel(lineCurrent, options.foldCompact ? lev : lev & ~SC_FOLDLEVELWHITEFLAG);
804
805 indentCurrent = indentNext;
806 indentCurrentLevel = indentNextLevel;
807 lineCurrent = lineNext;
808 }
809 }
810
811 LexerModule lmNim(SCLEX_NIM, LexerNim::LexerFactoryNim, "nim", nimWordListDesc);