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"
34 using namespace Scintilla
;
37 static const int NUM_RUST_KEYWORD_LISTS
= 7;
38 static const int MAX_RUST_IDENT_CHARS
= 1023;
40 static bool IsStreamCommentStyle(int style
) {
41 return style
== SCE_RUST_COMMENTBLOCK
||
42 style
== SCE_RUST_COMMENTBLOCKDOC
;
45 // Options used for LexerRust
50 bool foldCommentMultiline
;
51 bool foldCommentExplicit
;
52 std::string foldExplicitStart
;
53 std::string foldExplicitEnd
;
54 bool foldExplicitAnywhere
;
60 foldSyntaxBased
= true;
62 foldCommentMultiline
= true;
63 foldCommentExplicit
= true;
64 foldExplicitStart
= "";
66 foldExplicitAnywhere
= false;
73 static const char * const rustWordLists
[NUM_RUST_KEYWORD_LISTS
+ 1] = {
74 "Primary keywords and identifiers",
84 struct OptionSetRust
: public OptionSet
<OptionsRust
> {
86 DefineProperty("fold", &OptionsRust::fold
);
88 DefineProperty("fold.comment", &OptionsRust::foldComment
);
90 DefineProperty("fold.compact", &OptionsRust::foldCompact
);
92 DefineProperty("fold.at.else", &OptionsRust::foldAtElse
);
94 DefineProperty("fold.rust.syntax.based", &OptionsRust::foldSyntaxBased
,
95 "Set this property to 0 to disable syntax based folding.");
97 DefineProperty("fold.rust.comment.multiline", &OptionsRust::foldCommentMultiline
,
98 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
100 DefineProperty("fold.rust.comment.explicit", &OptionsRust::foldCommentExplicit
,
101 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
103 DefineProperty("fold.rust.explicit.start", &OptionsRust::foldExplicitStart
,
104 "The string to use for explicit fold start points, replacing the standard //{.");
106 DefineProperty("fold.rust.explicit.end", &OptionsRust::foldExplicitEnd
,
107 "The string to use for explicit fold end points, replacing the standard //}.");
109 DefineProperty("fold.rust.explicit.anywhere", &OptionsRust::foldExplicitAnywhere
,
110 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
112 DefineProperty("lexer.rust.fold.at.else", &OptionsRust::foldAtElseInt
,
113 "This option enables Rust folding on a \"} else {\" line of an if statement.");
115 DefineWordListSets(rustWordLists
);
119 class LexerRust
: public ILexer
{
120 WordList keywords
[NUM_RUST_KEYWORD_LISTS
];
122 OptionSetRust osRust
;
124 virtual ~LexerRust() {
126 void SCI_METHOD
Release() {
129 int SCI_METHOD
Version() const {
132 const char * SCI_METHOD
PropertyNames() {
133 return osRust
.PropertyNames();
135 int SCI_METHOD
PropertyType(const char *name
) {
136 return osRust
.PropertyType(name
);
138 const char * SCI_METHOD
DescribeProperty(const char *name
) {
139 return osRust
.DescribeProperty(name
);
141 Sci_Position SCI_METHOD
PropertySet(const char *key
, const char *val
);
142 const char * SCI_METHOD
DescribeWordListSets() {
143 return osRust
.DescribeWordListSets();
145 Sci_Position SCI_METHOD
WordListSet(int n
, const char *wl
);
146 void SCI_METHOD
Lex(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
);
147 void SCI_METHOD
Fold(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
);
148 void * SCI_METHOD
PrivateCall(int, void *) {
151 static ILexer
*LexerFactoryRust() {
152 return new LexerRust();
156 Sci_Position SCI_METHOD
LexerRust::PropertySet(const char *key
, const char *val
) {
157 if (osRust
.PropertySet(&options
, key
, val
)) {
163 Sci_Position SCI_METHOD
LexerRust::WordListSet(int n
, const char *wl
) {
164 Sci_Position firstModification
= -1;
165 if (n
< NUM_RUST_KEYWORD_LISTS
) {
166 WordList
*wordListN
= &keywords
[n
];
169 if (*wordListN
!= wlNew
) {
171 firstModification
= 0;
174 return firstModification
;
177 static bool IsWhitespace(int c
) {
178 return c
== ' ' || c
== '\t' || c
== '\r' || c
== '\n';
181 /* This isn't quite right for Unicode identifiers */
182 static bool IsIdentifierStart(int ch
) {
183 return (IsASCII(ch
) && (isalpha(ch
) || ch
== '_')) || !IsASCII(ch
);
186 /* This isn't quite right for Unicode identifiers */
187 static bool IsIdentifierContinue(int ch
) {
188 return (IsASCII(ch
) && (isalnum(ch
) || ch
== '_')) || !IsASCII(ch
);
191 static void ScanWhitespace(Accessor
& styler
, Sci_Position
& pos
, Sci_Position max
) {
192 while (IsWhitespace(styler
.SafeGetCharAt(pos
, '\0')) && pos
< max
) {
193 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
194 styler
.SetLineState(styler
.GetLine(pos
), 0);
197 styler
.ColourTo(pos
-1, SCE_RUST_DEFAULT
);
200 static void GrabString(char* s
, Accessor
& styler
, Sci_Position start
, Sci_Position len
) {
201 for (Sci_Position ii
= 0; ii
< len
; ii
++)
202 s
[ii
] = styler
[ii
+ start
];
206 static void ScanIdentifier(Accessor
& styler
, Sci_Position
& pos
, WordList
*keywords
) {
207 Sci_Position start
= pos
;
208 while (IsIdentifierContinue(styler
.SafeGetCharAt(pos
, '\0')))
211 if (styler
.SafeGetCharAt(pos
, '\0') == '!') {
213 styler
.ColourTo(pos
- 1, SCE_RUST_MACRO
);
215 char s
[MAX_RUST_IDENT_CHARS
+ 1];
216 int len
= pos
- start
;
217 len
= len
> MAX_RUST_IDENT_CHARS
? MAX_RUST_IDENT_CHARS
: len
;
218 GrabString(s
, styler
, start
, len
);
219 bool keyword
= false;
220 for (int ii
= 0; ii
< NUM_RUST_KEYWORD_LISTS
; ii
++) {
221 if (keywords
[ii
].InList(s
)) {
222 styler
.ColourTo(pos
- 1, SCE_RUST_WORD
+ ii
);
228 styler
.ColourTo(pos
- 1, SCE_RUST_IDENTIFIER
);
233 /* Scans a sequence of digits, returning true if it found any. */
234 static bool ScanDigits(Accessor
& styler
, Sci_Position
& pos
, int base
) {
235 Sci_Position old_pos
= pos
;
237 int c
= styler
.SafeGetCharAt(pos
, '\0');
238 if (IsADigit(c
, base
) || c
== '_')
243 return old_pos
!= pos
;
246 /* Scans an integer and floating point literals. */
247 static void ScanNumber(Accessor
& styler
, Sci_Position
& pos
) {
249 int c
= styler
.SafeGetCharAt(pos
, '\0');
250 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
252 /* Scan the prefix, thus determining the base.
253 * 10 is default if there's no prefix. */
254 if (c
== '0' && n
== 'x') {
257 } else if (c
== '0' && n
== 'b') {
260 } else if (c
== '0' && n
== 'o') {
265 /* Scan initial digits. The literal is malformed if there are none. */
266 error
|= !ScanDigits(styler
, pos
, base
);
267 /* See if there's an integer suffix. We mimic the Rust's lexer
268 * and munch it even if there was an error above. */
269 c
= styler
.SafeGetCharAt(pos
, '\0');
270 if (c
== 'u' || c
== 'i') {
272 c
= styler
.SafeGetCharAt(pos
, '\0');
273 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
274 if (c
== '8' || c
== 's') {
276 } else if (c
== '1' && n
== '6') {
278 } else if (c
== '3' && n
== '2') {
280 } else if (c
== '6' && n
== '4') {
285 /* See if it's a floating point literal. These literals have to be base 10.
288 /* If there's a period, it's a floating point literal unless it's
289 * followed by an identifier (meaning this is a method call, e.g.
290 * `1.foo()`) or another period, in which case it's a range (e.g. 1..2)
292 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
293 if (c
== '.' && !(IsIdentifierStart(n
) || n
== '.')) {
296 /* It's ok to have no digits after the period. */
297 ScanDigits(styler
, pos
, 10);
300 /* Look for the exponentiation. */
301 c
= styler
.SafeGetCharAt(pos
, '\0');
302 if (c
== 'e' || c
== 'E') {
305 c
= styler
.SafeGetCharAt(pos
, '\0');
306 if (c
== '-' || c
== '+')
308 /* It is invalid to have no digits in the exponent. */
309 error
|= !ScanDigits(styler
, pos
, 10);
312 /* Scan the floating point suffix. */
313 c
= styler
.SafeGetCharAt(pos
, '\0');
317 c
= styler
.SafeGetCharAt(pos
, '\0');
318 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
319 if (c
== '3' && n
== '2') {
321 } else if (c
== '6' && n
== '4') {
330 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
332 styler
.ColourTo(pos
- 1, SCE_RUST_NUMBER
);
335 static bool IsOneCharOperator(int c
) {
336 return c
== ';' || c
== ',' || c
== '(' || c
== ')'
337 || 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
== '$';
345 static bool IsTwoCharOperator(int c
, int n
) {
346 return (c
== '.' && n
== '.') || (c
== ':' && n
== ':')
347 || (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
== '=');
358 static bool IsThreeCharOperator(int c
, int n
, int n2
) {
359 return (c
== '<' && n
== '<' && n2
== '=')
360 || (c
== '>' && n
== '>' && n2
== '=');
363 static bool IsValidCharacterEscape(int c
) {
364 return c
== 'n' || c
== 'r' || c
== 't' || c
== '\\'
365 || c
== '\'' || c
== '"' || c
== '0';
368 static bool IsValidStringEscape(int c
) {
369 return IsValidCharacterEscape(c
) || c
== '\n' || c
== '\r';
372 static bool ScanNumericEscape(Accessor
&styler
, Sci_Position
& pos
, Sci_Position num_digits
, bool stop_asap
) {
374 int c
= styler
.SafeGetCharAt(pos
, '\0');
375 if (!IsADigit(c
, 16))
379 if (num_digits
== 0 && stop_asap
)
382 if (num_digits
== 0) {
389 /* This is overly permissive for character literals in order to accept UTF-8 encoded
390 * character literals. */
391 static void ScanCharacterLiteralOrLifetime(Accessor
&styler
, Sci_Position
& pos
, bool ascii_only
) {
393 int c
= styler
.SafeGetCharAt(pos
, '\0');
394 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
396 bool valid_lifetime
= !ascii_only
&& IsIdentifierStart(c
);
397 bool valid_char
= true;
403 if (IsValidCharacterEscape(n
)) {
405 } else if (n
== 'x') {
407 valid_char
= ScanNumericEscape(styler
, pos
, 2, false);
408 } else if (n
== 'u' && !ascii_only
) {
410 valid_char
= ScanNumericEscape(styler
, pos
, 4, false);
411 } else if (n
== 'U' && !ascii_only
) {
413 valid_char
= ScanNumericEscape(styler
, pos
, 8, false);
430 if (ascii_only
&& !IsASCII((char)c
)) {
433 } else if (!IsIdentifierContinue(c
) && !first
) {
440 c
= styler
.SafeGetCharAt(pos
, '\0');
441 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
445 if (styler
.SafeGetCharAt(pos
, '\0') == '\'') {
446 valid_lifetime
= false;
450 if (valid_lifetime
) {
451 styler
.ColourTo(pos
- 1, SCE_RUST_LIFETIME
);
452 } else if (valid_char
) {
454 styler
.ColourTo(pos
- 1, ascii_only
? SCE_RUST_BYTECHARACTER
: SCE_RUST_CHARACTER
);
456 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
467 * 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.
468 * Otherwise it's a regular comment.
470 static void ResumeBlockComment(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, CommentState state
, int level
) {
471 int c
= styler
.SafeGetCharAt(pos
, '\0');
472 bool maybe_doc_comment
= false;
474 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
475 if (n
!= '*' && n
!= '/') {
476 maybe_doc_comment
= true;
478 } else if (c
== '!') {
479 maybe_doc_comment
= true;
483 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
484 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
485 styler
.SetLineState(styler
.GetLine(pos
), level
);
492 styler
.SetLineState(styler
.GetLine(pos
), 0);
493 if (state
== DocComment
|| (state
== UnknownComment
&& maybe_doc_comment
))
494 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCKDOC
);
496 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCK
);
500 } else if (c
== '/') {
511 if (state
== DocComment
|| (state
== UnknownComment
&& maybe_doc_comment
))
512 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCKDOC
);
514 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCK
);
517 c
= styler
.SafeGetCharAt(pos
, '\0');
522 * The rule for line-doc comments is as follows... ///N and //! (where N is a non slash) start doc comments.
523 * Otherwise it's a normal line comment.
525 static void ResumeLineComment(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, CommentState state
) {
526 bool maybe_doc_comment
= false;
527 int c
= styler
.SafeGetCharAt(pos
, '\0');
531 c
= styler
.SafeGetCharAt(pos
, '\0');
533 maybe_doc_comment
= true;
536 } else if (c
== '!') {
537 maybe_doc_comment
= true;
540 while (pos
< max
&& c
!= '\n') {
541 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
542 styler
.SetLineState(styler
.GetLine(pos
), 0);
544 c
= styler
.SafeGetCharAt(pos
, '\0');
547 if (state
== DocComment
|| (state
== UnknownComment
&& maybe_doc_comment
))
548 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTLINEDOC
);
550 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTLINE
);
553 static void ScanComments(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
) {
555 int c
= styler
.SafeGetCharAt(pos
, '\0');
558 ResumeLineComment(styler
, pos
, max
, UnknownComment
);
560 ResumeBlockComment(styler
, pos
, max
, UnknownComment
, 1);
563 static void ResumeString(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, bool ascii_only
) {
564 int c
= styler
.SafeGetCharAt(pos
, '\0');
566 while (c
!= '"' && !error
) {
571 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
572 styler
.SetLineState(styler
.GetLine(pos
), 0);
574 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
575 if (IsValidStringEscape(n
)) {
577 } else if (n
== 'x') {
579 error
= !ScanNumericEscape(styler
, pos
, 2, true);
580 } else if (n
== 'u' && !ascii_only
) {
582 error
= !ScanNumericEscape(styler
, pos
, 4, true);
583 } else if (n
== 'U' && !ascii_only
) {
585 error
= !ScanNumericEscape(styler
, pos
, 8, true);
591 if (ascii_only
&& !IsASCII((char)c
))
596 c
= styler
.SafeGetCharAt(pos
, '\0');
600 styler
.ColourTo(pos
- 1, ascii_only
? SCE_RUST_BYTESTRING
: SCE_RUST_STRING
);
603 static void ResumeRawString(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, int num_hashes
, bool ascii_only
) {
605 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
606 styler
.SetLineState(styler
.GetLine(pos
), num_hashes
);
608 int c
= styler
.SafeGetCharAt(pos
, '\0');
611 int trailing_num_hashes
= 0;
612 while (styler
.SafeGetCharAt(pos
, '\0') == '#' && trailing_num_hashes
< num_hashes
) {
613 trailing_num_hashes
++;
616 if (trailing_num_hashes
== num_hashes
) {
617 styler
.SetLineState(styler
.GetLine(pos
), 0);
620 } else if (pos
>= max
) {
623 if (ascii_only
&& !IsASCII((char)c
))
628 styler
.ColourTo(pos
- 1, ascii_only
? SCE_RUST_BYTESTRINGR
: SCE_RUST_STRINGR
);
631 static void ScanRawString(Accessor
&styler
, Sci_Position
& pos
, Sci_Position max
, bool ascii_only
) {
634 while (styler
.SafeGetCharAt(pos
, '\0') == '#') {
638 if (styler
.SafeGetCharAt(pos
, '\0') != '"') {
639 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
642 ResumeRawString(styler
, pos
, max
, num_hashes
, ascii_only
);
646 void SCI_METHOD
LexerRust::Lex(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) {
648 Accessor
styler(pAccess
, &props
);
649 Sci_Position pos
= startPos
;
650 Sci_Position max
= pos
+ length
;
653 styler
.StartSegment(pos
);
655 if (initStyle
== SCE_RUST_COMMENTBLOCK
|| initStyle
== SCE_RUST_COMMENTBLOCKDOC
) {
656 ResumeBlockComment(styler
, pos
, max
, initStyle
== SCE_RUST_COMMENTBLOCKDOC
? DocComment
: NotDocComment
, styler
.GetLineState(styler
.GetLine(pos
) - 1));
657 } else if (initStyle
== SCE_RUST_COMMENTLINE
|| initStyle
== SCE_RUST_COMMENTLINEDOC
) {
658 ResumeLineComment(styler
, pos
, max
, initStyle
== SCE_RUST_COMMENTLINEDOC
? DocComment
: NotDocComment
);
659 } else if (initStyle
== SCE_RUST_STRING
) {
660 ResumeString(styler
, pos
, max
, false);
661 } else if (initStyle
== SCE_RUST_BYTESTRING
) {
662 ResumeString(styler
, pos
, max
, true);
663 } else if (initStyle
== SCE_RUST_STRINGR
) {
664 ResumeRawString(styler
, pos
, max
, styler
.GetLineState(styler
.GetLine(pos
) - 1), false);
665 } else if (initStyle
== SCE_RUST_BYTESTRINGR
) {
666 ResumeRawString(styler
, pos
, max
, styler
.GetLineState(styler
.GetLine(pos
) - 1), true);
670 int c
= styler
.SafeGetCharAt(pos
, '\0');
671 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
672 int n2
= styler
.SafeGetCharAt(pos
+ 2, '\0');
674 if (pos
== 0 && c
== '#' && n
== '!' && n2
!= '[') {
676 ResumeLineComment(styler
, pos
, max
, NotDocComment
);
677 } else if (IsWhitespace(c
)) {
678 ScanWhitespace(styler
, pos
, max
);
679 } else if (c
== '/' && (n
== '/' || n
== '*')) {
680 ScanComments(styler
, pos
, max
);
681 } else if (c
== 'r' && (n
== '#' || n
== '"')) {
682 ScanRawString(styler
, pos
, max
, false);
683 } else if (c
== 'b' && n
== 'r' && (n2
== '#' || n2
== '"')) {
685 ScanRawString(styler
, pos
, max
, true);
686 } else if (c
== 'b' && n
== '"') {
688 ResumeString(styler
, pos
, max
, true);
689 } else if (c
== 'b' && n
== '\'') {
691 ScanCharacterLiteralOrLifetime(styler
, pos
, true);
692 } else if (IsIdentifierStart(c
)) {
693 ScanIdentifier(styler
, pos
, keywords
);
694 } else if (IsADigit(c
)) {
695 ScanNumber(styler
, pos
);
696 } else if (IsThreeCharOperator(c
, n
, n2
)) {
698 styler
.ColourTo(pos
- 1, SCE_RUST_OPERATOR
);
699 } else if (IsTwoCharOperator(c
, n
)) {
701 styler
.ColourTo(pos
- 1, SCE_RUST_OPERATOR
);
702 } else if (IsOneCharOperator(c
)) {
704 styler
.ColourTo(pos
- 1, SCE_RUST_OPERATOR
);
705 } else if (c
== '\'') {
706 ScanCharacterLiteralOrLifetime(styler
, pos
, false);
707 } else if (c
== '"') {
709 ResumeString(styler
, pos
, max
, false);
712 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
715 styler
.ColourTo(pos
- 1, SCE_RUST_DEFAULT
);
719 void SCI_METHOD
LexerRust::Fold(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) {
724 LexAccessor
styler(pAccess
);
726 Sci_PositionU endPos
= startPos
+ length
;
727 int visibleChars
= 0;
728 bool inLineComment
= false;
729 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
730 int levelCurrent
= SC_FOLDLEVELBASE
;
732 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
733 Sci_PositionU lineStartNext
= styler
.LineStart(lineCurrent
+1);
734 int levelMinCurrent
= levelCurrent
;
735 int levelNext
= levelCurrent
;
736 char chNext
= styler
[startPos
];
737 int styleNext
= styler
.StyleAt(startPos
);
738 int style
= initStyle
;
739 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
740 for (Sci_PositionU i
= startPos
; i
< endPos
; i
++) {
742 chNext
= styler
.SafeGetCharAt(i
+ 1);
743 int stylePrev
= style
;
745 styleNext
= styler
.StyleAt(i
+ 1);
746 bool atEOL
= i
== (lineStartNext
-1);
747 if ((style
== SCE_RUST_COMMENTLINE
) || (style
== SCE_RUST_COMMENTLINEDOC
))
748 inLineComment
= true;
749 if (options
.foldComment
&& options
.foldCommentMultiline
&& IsStreamCommentStyle(style
) && !inLineComment
) {
750 if (!IsStreamCommentStyle(stylePrev
)) {
752 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
753 // Comments don't end at end of line and the next character may be unstyled.
757 if (options
.foldComment
&& options
.foldCommentExplicit
&& ((style
== SCE_RUST_COMMENTLINE
) || options
.foldExplicitAnywhere
)) {
758 if (userDefinedFoldMarkers
) {
759 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
761 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
765 if ((ch
== '/') && (chNext
== '/')) {
766 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
767 if (chNext2
== '{') {
769 } else if (chNext2
== '}') {
775 if (options
.foldSyntaxBased
&& (style
== SCE_RUST_OPERATOR
)) {
777 // Measure the minimum before a '{' to allow
778 // folding on "} else {"
779 if (levelMinCurrent
> levelNext
) {
780 levelMinCurrent
= levelNext
;
783 } else if (ch
== '}') {
789 if (atEOL
|| (i
== endPos
-1)) {
790 int levelUse
= levelCurrent
;
791 if (options
.foldSyntaxBased
&& options
.foldAtElse
) {
792 levelUse
= levelMinCurrent
;
794 int lev
= levelUse
| levelNext
<< 16;
795 if (visibleChars
== 0 && options
.foldCompact
)
796 lev
|= SC_FOLDLEVELWHITEFLAG
;
797 if (levelUse
< levelNext
)
798 lev
|= SC_FOLDLEVELHEADERFLAG
;
799 if (lev
!= styler
.LevelAt(lineCurrent
)) {
800 styler
.SetLevel(lineCurrent
, lev
);
803 lineStartNext
= styler
.LineStart(lineCurrent
+1);
804 levelCurrent
= levelNext
;
805 levelMinCurrent
= levelCurrent
;
806 if (atEOL
&& (i
== static_cast<Sci_PositionU
>(styler
.Length()-1))) {
807 // There is an empty line at end of file so give it same level and empty
808 styler
.SetLevel(lineCurrent
, (levelCurrent
| levelCurrent
<< 16) | SC_FOLDLEVELWHITEFLAG
);
811 inLineComment
= false;
816 LexerModule
lmRust(SCLEX_RUST
, LexerRust::LexerFactoryRust
, "rust", rustWordLists
);