4 ** Copyright (c) 2013 by SiegeLord <slabode@aim.com>
5 ** Converted to lexer object and added further folding features/properties by "Udo Lechner" <dlchnr(at)gmx(dot)net>
7 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
8 // The License.txt file describes the conditions under which this software may be distributed.
21 #include "Scintilla.h"
24 #include "PropSetSimple.h"
26 #include "LexAccessor.h"
28 #include "StyleContext.h"
29 #include "CharacterSet.h"
30 #include "LexerModule.h"
31 #include "OptionSet.h"
32 #include "DefaultLexer.h"
34 using namespace Scintilla
;
36 static const int NUM_RUST_KEYWORD_LISTS
= 7;
37 static const int MAX_RUST_IDENT_CHARS
= 1023;
39 static bool IsStreamCommentStyle(int style
) {
40 return style
== SCE_RUST_COMMENTBLOCK
||
41 style
== SCE_RUST_COMMENTBLOCKDOC
;
44 // Options used for LexerRust
49 bool foldCommentMultiline
;
50 bool foldCommentExplicit
;
51 std::string foldExplicitStart
;
52 std::string foldExplicitEnd
;
53 bool foldExplicitAnywhere
;
59 foldSyntaxBased
= true;
61 foldCommentMultiline
= true;
62 foldCommentExplicit
= true;
63 foldExplicitStart
= "";
65 foldExplicitAnywhere
= false;
72 static const char * const rustWordLists
[NUM_RUST_KEYWORD_LISTS
+ 1] = {
73 "Primary keywords and identifiers",
83 struct OptionSetRust
: public OptionSet
<OptionsRust
> {
85 DefineProperty("fold", &OptionsRust::fold
);
87 DefineProperty("fold.comment", &OptionsRust::foldComment
);
89 DefineProperty("fold.compact", &OptionsRust::foldCompact
);
91 DefineProperty("fold.at.else", &OptionsRust::foldAtElse
);
93 DefineProperty("fold.rust.syntax.based", &OptionsRust::foldSyntaxBased
,
94 "Set this property to 0 to disable syntax based folding.");
96 DefineProperty("fold.rust.comment.multiline", &OptionsRust::foldCommentMultiline
,
97 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
99 DefineProperty("fold.rust.comment.explicit", &OptionsRust::foldCommentExplicit
,
100 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
102 DefineProperty("fold.rust.explicit.start", &OptionsRust::foldExplicitStart
,
103 "The string to use for explicit fold start points, replacing the standard //{.");
105 DefineProperty("fold.rust.explicit.end", &OptionsRust::foldExplicitEnd
,
106 "The string to use for explicit fold end points, replacing the standard //}.");
108 DefineProperty("fold.rust.explicit.anywhere", &OptionsRust::foldExplicitAnywhere
,
109 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
111 DefineProperty("lexer.rust.fold.at.else", &OptionsRust::foldAtElseInt
,
112 "This option enables Rust folding on a \"} else {\" line of an if statement.");
114 DefineWordListSets(rustWordLists
);
118 class LexerRust
: public DefaultLexer
{
119 WordList keywords
[NUM_RUST_KEYWORD_LISTS
];
121 OptionSetRust osRust
;
123 virtual ~LexerRust() {
125 void SCI_METHOD
Release() override
{
128 int SCI_METHOD
Version() const override
{
131 const char * SCI_METHOD
PropertyNames() override
{
132 return osRust
.PropertyNames();
134 int SCI_METHOD
PropertyType(const char *name
) override
{
135 return osRust
.PropertyType(name
);
137 const char * SCI_METHOD
DescribeProperty(const char *name
) override
{
138 return osRust
.DescribeProperty(name
);
140 Sci_Position SCI_METHOD
PropertySet(const char *key
, const char *val
) override
;
141 const char * SCI_METHOD
DescribeWordListSets() override
{
142 return osRust
.DescribeWordListSets();
144 Sci_Position SCI_METHOD
WordListSet(int n
, const char *wl
) override
;
145 void SCI_METHOD
Lex(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) override
;
146 void SCI_METHOD
Fold(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) override
;
147 void * SCI_METHOD
PrivateCall(int, void *) override
{
150 static ILexer4
*LexerFactoryRust() {
151 return new LexerRust();
155 Sci_Position SCI_METHOD
LexerRust::PropertySet(const char *key
, const char *val
) {
156 if (osRust
.PropertySet(&options
, key
, val
)) {
162 Sci_Position SCI_METHOD
LexerRust::WordListSet(int n
, const char *wl
) {
163 Sci_Position firstModification
= -1;
164 if (n
< NUM_RUST_KEYWORD_LISTS
) {
165 WordList
*wordListN
= &keywords
[n
];
168 if (*wordListN
!= wlNew
) {
170 firstModification
= 0;
173 return firstModification
;
176 static bool IsWhitespace(int c
) {
177 return c
== ' ' || c
== '\t' || c
== '\r' || c
== '\n';
180 /* This isn't quite right for Unicode identifiers */
181 static bool IsIdentifierStart(int ch
) {
182 return (IsASCII(ch
) && (isalpha(ch
) || ch
== '_')) || !IsASCII(ch
);
185 /* This isn't quite right for Unicode identifiers */
186 static bool IsIdentifierContinue(int ch
) {
187 return (IsASCII(ch
) && (isalnum(ch
) || ch
== '_')) || !IsASCII(ch
);
190 static void ScanWhitespace(Accessor
& styler
, Sci_Position
& pos
, Sci_Position max
) {
191 while (IsWhitespace(styler
.SafeGetCharAt(pos
, '\0')) && pos
< max
) {
192 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
193 styler
.SetLineState(styler
.GetLine(pos
), 0);
196 styler
.ColourTo(pos
-1, SCE_RUST_DEFAULT
);
199 static void GrabString(char* s
, Accessor
& styler
, Sci_Position start
, Sci_Position len
) {
200 for (Sci_Position ii
= 0; ii
< len
; ii
++)
201 s
[ii
] = styler
[ii
+ start
];
205 static void ScanIdentifier(Accessor
& styler
, Sci_Position
& pos
, WordList
*keywords
) {
206 Sci_Position start
= pos
;
207 while (IsIdentifierContinue(styler
.SafeGetCharAt(pos
, '\0')))
210 if (styler
.SafeGetCharAt(pos
, '\0') == '!') {
212 styler
.ColourTo(pos
- 1, SCE_RUST_MACRO
);
214 char s
[MAX_RUST_IDENT_CHARS
+ 1];
215 Sci_Position len
= pos
- start
;
216 len
= len
> MAX_RUST_IDENT_CHARS
? MAX_RUST_IDENT_CHARS
: len
;
217 GrabString(s
, styler
, start
, len
);
218 bool keyword
= false;
219 for (int ii
= 0; ii
< NUM_RUST_KEYWORD_LISTS
; ii
++) {
220 if (keywords
[ii
].InList(s
)) {
221 styler
.ColourTo(pos
- 1, SCE_RUST_WORD
+ ii
);
227 styler
.ColourTo(pos
- 1, SCE_RUST_IDENTIFIER
);
232 /* Scans a sequence of digits, returning true if it found any. */
233 static bool ScanDigits(Accessor
& styler
, Sci_Position
& pos
, int base
) {
234 Sci_Position old_pos
= pos
;
236 int c
= styler
.SafeGetCharAt(pos
, '\0');
237 if (IsADigit(c
, base
) || c
== '_')
242 return old_pos
!= pos
;
245 /* Scans an integer and floating point literals. */
246 static void ScanNumber(Accessor
& styler
, Sci_Position
& pos
) {
248 int c
= styler
.SafeGetCharAt(pos
, '\0');
249 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
251 /* Scan the prefix, thus determining the base.
252 * 10 is default if there's no prefix. */
253 if (c
== '0' && n
== 'x') {
256 } else if (c
== '0' && n
== 'b') {
259 } else if (c
== '0' && n
== 'o') {
264 /* Scan initial digits. The literal is malformed if there are none. */
265 error
|= !ScanDigits(styler
, pos
, base
);
266 /* See if there's an integer suffix. We mimic the Rust's lexer
267 * and munch it even if there was an error above. */
268 c
= styler
.SafeGetCharAt(pos
, '\0');
269 if (c
== 'u' || c
== 'i') {
271 c
= styler
.SafeGetCharAt(pos
, '\0');
272 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
275 } else if (c
== '1' && n
== '6') {
277 } else if (c
== '3' && n
== '2') {
279 } else if (c
== '6' && n
== '4') {
281 } else if (styler
.Match(pos
, "size")) {
286 /* See if it's a floating point literal. These literals have to be base 10.
289 /* If there's a period, it's a floating point literal unless it's
290 * followed by an identifier (meaning this is a method call, e.g.
291 * `1.foo()`) or another period, in which case it's a range (e.g. 1..2)
293 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
294 if (c
== '.' && !(IsIdentifierStart(n
) || n
== '.')) {
297 /* It's ok to have no digits after the period. */
298 ScanDigits(styler
, pos
, 10);
301 /* Look for the exponentiation. */
302 c
= styler
.SafeGetCharAt(pos
, '\0');
303 if (c
== 'e' || c
== 'E') {
306 c
= styler
.SafeGetCharAt(pos
, '\0');
307 if (c
== '-' || c
== '+')
309 /* It is invalid to have no digits in the exponent. */
310 error
|= !ScanDigits(styler
, pos
, 10);
313 /* Scan the floating point suffix. */
314 c
= styler
.SafeGetCharAt(pos
, '\0');
318 c
= styler
.SafeGetCharAt(pos
, '\0');
319 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
320 if (c
== '3' && n
== '2') {
322 } else if (c
== '6' && n
== '4') {
331 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
333 styler
.ColourTo(pos
- 1, SCE_RUST_NUMBER
);
336 static bool IsOneCharOperator(int c
) {
337 return c
== ';' || c
== ',' || c
== '(' || c
== ')'
338 || c
== '{' || c
== '}' || c
== '[' || c
== ']'
339 || c
== '@' || c
== '#' || c
== '~' || c
== '+'
340 || c
== '*' || c
== '/' || c
== '^' || c
== '%'
341 || c
== '.' || c
== ':' || c
== '!' || c
== '<'
342 || c
== '>' || c
== '=' || c
== '-' || c
== '&'
343 || c
== '|' || c
== '$' || c
== '?';
346 static bool IsTwoCharOperator(int c
, int n
) {
347 return (c
== '.' && n
== '.') || (c
== ':' && n
== ':')
348 || (c
== '!' && n
== '=') || (c
== '<' && n
== '<')
349 || (c
== '<' && n
== '=') || (c
== '>' && n
== '>')
350 || (c
== '>' && n
== '=') || (c
== '=' && n
== '=')
351 || (c
== '=' && n
== '>') || (c
== '-' && n
== '>')
352 || (c
== '&' && n
== '&') || (c
== '|' && n
== '|')
353 || (c
== '-' && n
== '=') || (c
== '&' && n
== '=')
354 || (c
== '|' && n
== '=') || (c
== '+' && n
== '=')
355 || (c
== '*' && n
== '=') || (c
== '/' && n
== '=')
356 || (c
== '^' && n
== '=') || (c
== '%' && n
== '=');
359 static bool IsThreeCharOperator(int c
, int n
, int n2
) {
360 return (c
== '<' && n
== '<' && n2
== '=')
361 || (c
== '>' && n
== '>' && n2
== '=');
364 static bool IsValidCharacterEscape(int c
) {
365 return c
== 'n' || c
== 'r' || c
== 't' || c
== '\\'
366 || c
== '\'' || c
== '"' || c
== '0';
369 static bool IsValidStringEscape(int c
) {
370 return IsValidCharacterEscape(c
) || c
== '\n' || c
== '\r';
373 static bool ScanNumericEscape(Accessor
&styler
, Sci_Position
& pos
, Sci_Position num_digits
, bool stop_asap
) {
375 int c
= styler
.SafeGetCharAt(pos
, '\0');
376 if (!IsADigit(c
, 16))
380 if (num_digits
== 0 && stop_asap
)
383 if (num_digits
== 0) {
390 /* This is overly permissive for character literals in order to accept UTF-8 encoded
391 * character literals. */
392 static void ScanCharacterLiteralOrLifetime(Accessor
&styler
, Sci_Position
& pos
, bool ascii_only
) {
394 int c
= styler
.SafeGetCharAt(pos
, '\0');
395 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
397 bool valid_lifetime
= !ascii_only
&& IsIdentifierStart(c
);
398 bool valid_char
= true;
404 if (IsValidCharacterEscape(n
)) {
406 } else if (n
== 'x') {
408 valid_char
= ScanNumericEscape(styler
, pos
, 2, false);
409 } else if (n
== 'u' && !ascii_only
) {
411 if (styler
.SafeGetCharAt(pos
, '\0') != '{') {
413 valid_char
= ScanNumericEscape(styler
, pos
, 4, false);
416 while (IsADigit(styler
.SafeGetCharAt(++pos
, '\0'), 16) && n_digits
++ < 6) {
418 if (n_digits
> 0 && styler
.SafeGetCharAt(pos
, '\0') == '}')
423 } else if (n
== 'U' && !ascii_only
) {
425 valid_char
= ScanNumericEscape(styler
, pos
, 8, false);
442 if (ascii_only
&& !IsASCII((char)c
)) {
445 } else if (!IsIdentifierContinue(c
) && !first
) {
452 c
= styler
.SafeGetCharAt(pos
, '\0');
453 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
457 if (styler
.SafeGetCharAt(pos
, '\0') == '\'') {
458 valid_lifetime
= false;
462 if (valid_lifetime
) {
463 styler
.ColourTo(pos
- 1, SCE_RUST_LIFETIME
);
464 } else if (valid_char
) {
466 styler
.ColourTo(pos
- 1, ascii_only
? SCE_RUST_BYTECHARACTER
: SCE_RUST_CHARACTER
);
468 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
479 * The rule for block-doc comments is as follows: /xxN and /x! (where x is an asterisk, N is a non-asterisk) start doc comments.
480 * Otherwise it's a regular comment.
482 static void ResumeBlockComment(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, CommentState state
, int level
) {
483 int c
= styler
.SafeGetCharAt(pos
, '\0');
484 bool maybe_doc_comment
= false;
486 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
487 if (n
!= '*' && n
!= '/') {
488 maybe_doc_comment
= true;
490 } else if (c
== '!') {
491 maybe_doc_comment
= true;
495 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
496 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
497 styler
.SetLineState(styler
.GetLine(pos
), level
);
504 styler
.SetLineState(styler
.GetLine(pos
), 0);
505 if (state
== DocComment
|| (state
== UnknownComment
&& maybe_doc_comment
))
506 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCKDOC
);
508 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCK
);
512 } else if (c
== '/') {
523 if (state
== DocComment
|| (state
== UnknownComment
&& maybe_doc_comment
))
524 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCKDOC
);
526 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCK
);
529 c
= styler
.SafeGetCharAt(pos
, '\0');
534 * The rule for line-doc comments is as follows... ///N and //! (where N is a non slash) start doc comments.
535 * Otherwise it's a normal line comment.
537 static void ResumeLineComment(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, CommentState state
) {
538 bool maybe_doc_comment
= false;
539 int c
= styler
.SafeGetCharAt(pos
, '\0');
543 c
= styler
.SafeGetCharAt(pos
, '\0');
545 maybe_doc_comment
= true;
548 } else if (c
== '!') {
549 maybe_doc_comment
= true;
552 while (pos
< max
&& c
!= '\n') {
553 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
554 styler
.SetLineState(styler
.GetLine(pos
), 0);
556 c
= styler
.SafeGetCharAt(pos
, '\0');
559 if (state
== DocComment
|| (state
== UnknownComment
&& maybe_doc_comment
))
560 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTLINEDOC
);
562 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTLINE
);
565 static void ScanComments(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
) {
567 int c
= styler
.SafeGetCharAt(pos
, '\0');
570 ResumeLineComment(styler
, pos
, max
, UnknownComment
);
572 ResumeBlockComment(styler
, pos
, max
, UnknownComment
, 1);
575 static void ResumeString(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, bool ascii_only
) {
576 int c
= styler
.SafeGetCharAt(pos
, '\0');
578 while (c
!= '"' && !error
) {
583 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
584 styler
.SetLineState(styler
.GetLine(pos
), 0);
586 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
587 if (IsValidStringEscape(n
)) {
589 } else if (n
== 'x') {
591 error
= !ScanNumericEscape(styler
, pos
, 2, true);
592 } else if (n
== 'u' && !ascii_only
) {
594 if (styler
.SafeGetCharAt(pos
, '\0') != '{') {
596 error
= !ScanNumericEscape(styler
, pos
, 4, true);
599 while (IsADigit(styler
.SafeGetCharAt(++pos
, '\0'), 16) && n_digits
++ < 6) {
601 if (n_digits
> 0 && styler
.SafeGetCharAt(pos
, '\0') == '}')
606 } else if (n
== 'U' && !ascii_only
) {
608 error
= !ScanNumericEscape(styler
, pos
, 8, true);
614 if (ascii_only
&& !IsASCII((char)c
))
619 c
= styler
.SafeGetCharAt(pos
, '\0');
623 styler
.ColourTo(pos
- 1, ascii_only
? SCE_RUST_BYTESTRING
: SCE_RUST_STRING
);
626 static void ResumeRawString(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, int num_hashes
, bool ascii_only
) {
628 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
629 styler
.SetLineState(styler
.GetLine(pos
), num_hashes
);
631 int c
= styler
.SafeGetCharAt(pos
, '\0');
634 int trailing_num_hashes
= 0;
635 while (styler
.SafeGetCharAt(pos
, '\0') == '#' && trailing_num_hashes
< num_hashes
) {
636 trailing_num_hashes
++;
639 if (trailing_num_hashes
== num_hashes
) {
640 styler
.SetLineState(styler
.GetLine(pos
), 0);
643 } else if (pos
>= max
) {
646 if (ascii_only
&& !IsASCII((char)c
))
651 styler
.ColourTo(pos
- 1, ascii_only
? SCE_RUST_BYTESTRINGR
: SCE_RUST_STRINGR
);
654 static void ScanRawString(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, bool ascii_only
) {
657 while (styler
.SafeGetCharAt(pos
, '\0') == '#') {
661 if (styler
.SafeGetCharAt(pos
, '\0') != '"') {
662 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
665 ResumeRawString(styler
, pos
, max
, num_hashes
, ascii_only
);
669 void SCI_METHOD
LexerRust::Lex(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) {
671 Accessor
styler(pAccess
, &props
);
672 Sci_Position pos
= startPos
;
673 Sci_Position max
= pos
+ length
;
676 styler
.StartSegment(pos
);
678 if (initStyle
== SCE_RUST_COMMENTBLOCK
|| initStyle
== SCE_RUST_COMMENTBLOCKDOC
) {
679 ResumeBlockComment(styler
, pos
, max
, initStyle
== SCE_RUST_COMMENTBLOCKDOC
? DocComment
: NotDocComment
, styler
.GetLineState(styler
.GetLine(pos
) - 1));
680 } else if (initStyle
== SCE_RUST_COMMENTLINE
|| initStyle
== SCE_RUST_COMMENTLINEDOC
) {
681 ResumeLineComment(styler
, pos
, max
, initStyle
== SCE_RUST_COMMENTLINEDOC
? DocComment
: NotDocComment
);
682 } else if (initStyle
== SCE_RUST_STRING
) {
683 ResumeString(styler
, pos
, max
, false);
684 } else if (initStyle
== SCE_RUST_BYTESTRING
) {
685 ResumeString(styler
, pos
, max
, true);
686 } else if (initStyle
== SCE_RUST_STRINGR
) {
687 ResumeRawString(styler
, pos
, max
, styler
.GetLineState(styler
.GetLine(pos
) - 1), false);
688 } else if (initStyle
== SCE_RUST_BYTESTRINGR
) {
689 ResumeRawString(styler
, pos
, max
, styler
.GetLineState(styler
.GetLine(pos
) - 1), true);
693 int c
= styler
.SafeGetCharAt(pos
, '\0');
694 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
695 int n2
= styler
.SafeGetCharAt(pos
+ 2, '\0');
697 if (pos
== 0 && c
== '#' && n
== '!' && n2
!= '[') {
699 ResumeLineComment(styler
, pos
, max
, NotDocComment
);
700 } else if (IsWhitespace(c
)) {
701 ScanWhitespace(styler
, pos
, max
);
702 } else if (c
== '/' && (n
== '/' || n
== '*')) {
703 ScanComments(styler
, pos
, max
);
704 } else if (c
== 'r' && (n
== '#' || n
== '"')) {
705 ScanRawString(styler
, pos
, max
, false);
706 } else if (c
== 'b' && n
== 'r' && (n2
== '#' || n2
== '"')) {
708 ScanRawString(styler
, pos
, max
, true);
709 } else if (c
== 'b' && n
== '"') {
711 ResumeString(styler
, pos
, max
, true);
712 } else if (c
== 'b' && n
== '\'') {
714 ScanCharacterLiteralOrLifetime(styler
, pos
, true);
715 } else if (IsIdentifierStart(c
)) {
716 ScanIdentifier(styler
, pos
, keywords
);
717 } else if (IsADigit(c
)) {
718 ScanNumber(styler
, pos
);
719 } else if (IsThreeCharOperator(c
, n
, n2
)) {
721 styler
.ColourTo(pos
- 1, SCE_RUST_OPERATOR
);
722 } else if (IsTwoCharOperator(c
, n
)) {
724 styler
.ColourTo(pos
- 1, SCE_RUST_OPERATOR
);
725 } else if (IsOneCharOperator(c
)) {
727 styler
.ColourTo(pos
- 1, SCE_RUST_OPERATOR
);
728 } else if (c
== '\'') {
729 ScanCharacterLiteralOrLifetime(styler
, pos
, false);
730 } else if (c
== '"') {
732 ResumeString(styler
, pos
, max
, false);
735 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
738 styler
.ColourTo(pos
- 1, SCE_RUST_DEFAULT
);
742 void SCI_METHOD
LexerRust::Fold(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) {
747 LexAccessor
styler(pAccess
);
749 Sci_PositionU endPos
= startPos
+ length
;
750 int visibleChars
= 0;
751 bool inLineComment
= false;
752 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
753 int levelCurrent
= SC_FOLDLEVELBASE
;
755 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
756 Sci_PositionU lineStartNext
= styler
.LineStart(lineCurrent
+1);
757 int levelMinCurrent
= levelCurrent
;
758 int levelNext
= levelCurrent
;
759 char chNext
= styler
[startPos
];
760 int styleNext
= styler
.StyleAt(startPos
);
761 int style
= initStyle
;
762 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
763 for (Sci_PositionU i
= startPos
; i
< endPos
; i
++) {
765 chNext
= styler
.SafeGetCharAt(i
+ 1);
766 int stylePrev
= style
;
768 styleNext
= styler
.StyleAt(i
+ 1);
769 bool atEOL
= i
== (lineStartNext
-1);
770 if ((style
== SCE_RUST_COMMENTLINE
) || (style
== SCE_RUST_COMMENTLINEDOC
))
771 inLineComment
= true;
772 if (options
.foldComment
&& options
.foldCommentMultiline
&& IsStreamCommentStyle(style
) && !inLineComment
) {
773 if (!IsStreamCommentStyle(stylePrev
)) {
775 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
776 // Comments don't end at end of line and the next character may be unstyled.
780 if (options
.foldComment
&& options
.foldCommentExplicit
&& ((style
== SCE_RUST_COMMENTLINE
) || options
.foldExplicitAnywhere
)) {
781 if (userDefinedFoldMarkers
) {
782 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
784 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
788 if ((ch
== '/') && (chNext
== '/')) {
789 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
790 if (chNext2
== '{') {
792 } else if (chNext2
== '}') {
798 if (options
.foldSyntaxBased
&& (style
== SCE_RUST_OPERATOR
)) {
800 // Measure the minimum before a '{' to allow
801 // folding on "} else {"
802 if (levelMinCurrent
> levelNext
) {
803 levelMinCurrent
= levelNext
;
806 } else if (ch
== '}') {
812 if (atEOL
|| (i
== endPos
-1)) {
813 int levelUse
= levelCurrent
;
814 if (options
.foldSyntaxBased
&& options
.foldAtElse
) {
815 levelUse
= levelMinCurrent
;
817 int lev
= levelUse
| levelNext
<< 16;
818 if (visibleChars
== 0 && options
.foldCompact
)
819 lev
|= SC_FOLDLEVELWHITEFLAG
;
820 if (levelUse
< levelNext
)
821 lev
|= SC_FOLDLEVELHEADERFLAG
;
822 if (lev
!= styler
.LevelAt(lineCurrent
)) {
823 styler
.SetLevel(lineCurrent
, lev
);
826 lineStartNext
= styler
.LineStart(lineCurrent
+1);
827 levelCurrent
= levelNext
;
828 levelMinCurrent
= levelCurrent
;
829 if (atEOL
&& (i
== static_cast<Sci_PositionU
>(styler
.Length()-1))) {
830 // There is an empty line at end of file so give it same level and empty
831 styler
.SetLevel(lineCurrent
, (levelCurrent
| levelCurrent
<< 16) | SC_FOLDLEVELWHITEFLAG
);
834 inLineComment
= false;
839 LexerModule
lmRust(SCLEX_RUST
, LexerRust::LexerFactoryRust
, "rust", rustWordLists
);