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