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 int SCI_METHOD
PropertySet(const char *key
, const char *val
);
142 const char * SCI_METHOD
DescribeWordListSets() {
143 return osRust
.DescribeWordListSets();
145 int SCI_METHOD
WordListSet(int n
, const char *wl
);
146 void SCI_METHOD
Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
147 void SCI_METHOD
Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
148 void * SCI_METHOD
PrivateCall(int, void *) {
151 static ILexer
*LexerFactoryRust() {
152 return new LexerRust();
156 int SCI_METHOD
LexerRust::PropertySet(const char *key
, const char *val
) {
157 if (osRust
.PropertySet(&options
, key
, val
)) {
163 int SCI_METHOD
LexerRust::WordListSet(int n
, const char *wl
) {
164 int 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
, int& pos
, int 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
, int start
, int len
) {
201 for (int ii
= 0; ii
< len
; ii
++)
202 s
[ii
] = styler
[ii
+ start
];
206 static void ScanIdentifier(Accessor
& styler
, int& pos
, WordList
*keywords
) {
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
, int& pos
, int base
) {
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
, int& 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');
276 } else if (c
== '1' && n
== '6') {
278 } else if (c
== '3' && n
== '2') {
280 } else if (c
== '6' && n
== '4') {
283 /* See if it's a floating point literal. These literals have to be base 10.
286 /* If there's a period, it's a floating point literal unless it's
287 * followed by an identifier (meaning this is a method call, e.g.
288 * `1.foo()`) or another period, in which case it's a range (e.g. 1..2)
290 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
291 if (c
== '.' && !(IsIdentifierStart(n
) || n
== '.')) {
294 /* It's ok to have no digits after the period. */
295 ScanDigits(styler
, pos
, 10);
298 /* Look for the exponentiation. */
299 c
= styler
.SafeGetCharAt(pos
, '\0');
300 if (c
== 'e' || c
== 'E') {
303 c
= styler
.SafeGetCharAt(pos
, '\0');
304 if (c
== '-' || c
== '+')
306 /* It is invalid to have no digits in the exponent. */
307 error
|= !ScanDigits(styler
, pos
, 10);
310 /* Scan the floating point suffix. */
311 c
= styler
.SafeGetCharAt(pos
, '\0');
315 c
= styler
.SafeGetCharAt(pos
, '\0');
316 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
317 if (c
== '3' && n
== '2') {
319 } else if (c
== '6' && n
== '4') {
328 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
330 styler
.ColourTo(pos
- 1, SCE_RUST_NUMBER
);
333 static bool IsOneCharOperator(int c
) {
334 return c
== ';' || c
== ',' || c
== '(' || c
== ')'
335 || c
== '{' || c
== '}' || c
== '[' || c
== ']'
336 || c
== '@' || c
== '#' || c
== '~' || c
== '+'
337 || c
== '*' || c
== '/' || c
== '^' || c
== '%'
338 || c
== '.' || c
== ':' || c
== '!' || c
== '<'
339 || c
== '>' || c
== '=' || c
== '-' || c
== '&'
340 || c
== '|' || c
== '$';
343 static bool IsTwoCharOperator(int c
, int n
) {
344 return (c
== '.' && n
== '.') || (c
== ':' && n
== ':')
345 || (c
== '!' && n
== '=') || (c
== '<' && n
== '<')
346 || (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
== '=');
356 static bool IsThreeCharOperator(int c
, int n
, int n2
) {
357 return (c
== '<' && n
== '<' && n2
== '=')
358 || (c
== '>' && n
== '>' && n2
== '=');
361 static bool IsValidCharacterEscape(int c
) {
362 return c
== 'n' || c
== 'r' || c
== 't' || c
== '\\'
363 || c
== '\'' || c
== '"' || c
== '0';
366 static bool IsValidStringEscape(int c
) {
367 return IsValidCharacterEscape(c
) || c
== '\n' || c
== '\r';
370 static bool ScanNumericEscape(Accessor
&styler
, int& pos
, int num_digits
, bool stop_asap
) {
372 int c
= styler
.SafeGetCharAt(pos
, '\0');
373 if (!IsADigit(c
, 16))
377 if (num_digits
== 0 && stop_asap
)
380 if (num_digits
== 0) {
387 /* This is overly permissive for character literals in order to accept UTF-8 encoded
388 * character literals. */
389 static void ScanCharacterLiteralOrLifetime(Accessor
&styler
, int& pos
, bool ascii_only
) {
391 int c
= styler
.SafeGetCharAt(pos
, '\0');
392 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
394 bool valid_lifetime
= !ascii_only
&& IsIdentifierStart(c
);
395 bool valid_char
= true;
401 if (IsValidCharacterEscape(n
)) {
403 } else if (n
== 'x') {
405 valid_char
= ScanNumericEscape(styler
, pos
, 2, false);
406 } else if (n
== 'u' && !ascii_only
) {
408 valid_char
= ScanNumericEscape(styler
, pos
, 4, false);
409 } else if (n
== 'U' && !ascii_only
) {
411 valid_char
= ScanNumericEscape(styler
, pos
, 8, false);
428 if (ascii_only
&& !IsASCII((char)c
)) {
431 } else if (!IsIdentifierContinue(c
) && !first
) {
438 c
= styler
.SafeGetCharAt(pos
, '\0');
439 n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
443 if (styler
.SafeGetCharAt(pos
, '\0') == '\'') {
444 valid_lifetime
= false;
448 if (valid_lifetime
) {
449 styler
.ColourTo(pos
- 1, SCE_RUST_LIFETIME
);
450 } else if (valid_char
) {
452 styler
.ColourTo(pos
- 1, ascii_only
? SCE_RUST_BYTECHARACTER
: SCE_RUST_CHARACTER
);
454 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
465 * 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.
466 * Otherwise it's a regular comment.
468 static void ResumeBlockComment(Accessor
&styler
, int& pos
, int max
, CommentState state
, int level
) {
469 int c
= styler
.SafeGetCharAt(pos
, '\0');
470 bool maybe_doc_comment
= false;
472 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
473 if (n
!= '*' && n
!= '/') {
474 maybe_doc_comment
= true;
476 } else if (c
== '!') {
477 maybe_doc_comment
= true;
481 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
482 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
483 styler
.SetLineState(styler
.GetLine(pos
), level
);
490 styler
.SetLineState(styler
.GetLine(pos
), 0);
491 if (state
== DocComment
|| (state
== UnknownComment
&& maybe_doc_comment
))
492 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCKDOC
);
494 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCK
);
498 } else if (c
== '/') {
509 if (state
== DocComment
|| (state
== UnknownComment
&& maybe_doc_comment
))
510 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCKDOC
);
512 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTBLOCK
);
515 c
= styler
.SafeGetCharAt(pos
, '\0');
520 * The rule for line-doc comments is as follows... ///N and //! (where N is a non slash) start doc comments.
521 * Otherwise it's a normal line comment.
523 static void ResumeLineComment(Accessor
&styler
, int& pos
, int max
, CommentState state
) {
524 bool maybe_doc_comment
= false;
525 int c
= styler
.SafeGetCharAt(pos
, '\0');
529 c
= styler
.SafeGetCharAt(pos
, '\0');
531 maybe_doc_comment
= true;
534 } else if (c
== '!') {
535 maybe_doc_comment
= true;
538 while (pos
< max
&& c
!= '\n') {
539 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
540 styler
.SetLineState(styler
.GetLine(pos
), 0);
542 c
= styler
.SafeGetCharAt(pos
, '\0');
545 if (state
== DocComment
|| (state
== UnknownComment
&& maybe_doc_comment
))
546 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTLINEDOC
);
548 styler
.ColourTo(pos
- 1, SCE_RUST_COMMENTLINE
);
551 static void ScanComments(Accessor
&styler
, int& pos
, int max
) {
553 int c
= styler
.SafeGetCharAt(pos
, '\0');
556 ResumeLineComment(styler
, pos
, max
, UnknownComment
);
558 ResumeBlockComment(styler
, pos
, max
, UnknownComment
, 1);
561 static void ResumeString(Accessor
&styler
, int& pos
, int max
, bool ascii_only
) {
562 int c
= styler
.SafeGetCharAt(pos
, '\0');
564 while (c
!= '"' && !error
) {
569 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
570 styler
.SetLineState(styler
.GetLine(pos
), 0);
572 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
573 if (IsValidStringEscape(n
)) {
575 } else if (n
== 'x') {
577 error
= !ScanNumericEscape(styler
, pos
, 2, true);
578 } else if (n
== 'u' && !ascii_only
) {
580 error
= !ScanNumericEscape(styler
, pos
, 4, true);
581 } else if (n
== 'U' && !ascii_only
) {
583 error
= !ScanNumericEscape(styler
, pos
, 8, true);
589 if (ascii_only
&& !IsASCII((char)c
))
594 c
= styler
.SafeGetCharAt(pos
, '\0');
598 styler
.ColourTo(pos
- 1, ascii_only
? SCE_RUST_BYTESTRING
: SCE_RUST_STRING
);
601 static void ResumeRawString(Accessor
&styler
, int& pos
, int max
, int num_hashes
, bool ascii_only
) {
603 if (pos
== styler
.LineEnd(styler
.GetLine(pos
)))
604 styler
.SetLineState(styler
.GetLine(pos
), num_hashes
);
606 int c
= styler
.SafeGetCharAt(pos
, '\0');
609 int trailing_num_hashes
= 0;
610 while (styler
.SafeGetCharAt(pos
, '\0') == '#' && trailing_num_hashes
< num_hashes
) {
611 trailing_num_hashes
++;
614 if (trailing_num_hashes
== num_hashes
) {
615 styler
.SetLineState(styler
.GetLine(pos
), 0);
618 } else if (pos
>= max
) {
621 if (ascii_only
&& !IsASCII((char)c
))
626 styler
.ColourTo(pos
- 1, ascii_only
? SCE_RUST_BYTESTRINGR
: SCE_RUST_STRINGR
);
629 static void ScanRawString(Accessor
&styler
, int& pos
, int max
, bool ascii_only
) {
632 while (styler
.SafeGetCharAt(pos
, '\0') == '#') {
636 if (styler
.SafeGetCharAt(pos
, '\0') != '"') {
637 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
640 ResumeRawString(styler
, pos
, max
, num_hashes
, ascii_only
);
644 void SCI_METHOD
LexerRust::Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
646 Accessor
styler(pAccess
, &props
);
648 int max
= pos
+ length
;
651 styler
.StartSegment(pos
);
653 if (initStyle
== SCE_RUST_COMMENTBLOCK
|| initStyle
== SCE_RUST_COMMENTBLOCKDOC
) {
654 ResumeBlockComment(styler
, pos
, max
, initStyle
== SCE_RUST_COMMENTBLOCKDOC
? DocComment
: NotDocComment
, styler
.GetLineState(styler
.GetLine(pos
) - 1));
655 } else if (initStyle
== SCE_RUST_COMMENTLINE
|| initStyle
== SCE_RUST_COMMENTLINEDOC
) {
656 ResumeLineComment(styler
, pos
, max
, initStyle
== SCE_RUST_COMMENTLINEDOC
? DocComment
: NotDocComment
);
657 } else if (initStyle
== SCE_RUST_STRING
) {
658 ResumeString(styler
, pos
, max
, false);
659 } else if (initStyle
== SCE_RUST_BYTESTRING
) {
660 ResumeString(styler
, pos
, max
, true);
661 } else if (initStyle
== SCE_RUST_STRINGR
) {
662 ResumeRawString(styler
, pos
, max
, styler
.GetLineState(styler
.GetLine(pos
) - 1), false);
663 } else if (initStyle
== SCE_RUST_BYTESTRINGR
) {
664 ResumeRawString(styler
, pos
, max
, styler
.GetLineState(styler
.GetLine(pos
) - 1), true);
668 int c
= styler
.SafeGetCharAt(pos
, '\0');
669 int n
= styler
.SafeGetCharAt(pos
+ 1, '\0');
670 int n2
= styler
.SafeGetCharAt(pos
+ 2, '\0');
672 if (pos
== 0 && c
== '#' && n
== '!' && n2
!= '[') {
674 ResumeLineComment(styler
, pos
, max
, NotDocComment
);
675 } else if (IsWhitespace(c
)) {
676 ScanWhitespace(styler
, pos
, max
);
677 } else if (c
== '/' && (n
== '/' || n
== '*')) {
678 ScanComments(styler
, pos
, max
);
679 } else if (c
== 'r' && (n
== '#' || n
== '"')) {
680 ScanRawString(styler
, pos
, max
, false);
681 } else if (c
== 'b' && n
== 'r' && (n2
== '#' || n2
== '"')) {
683 ScanRawString(styler
, pos
, max
, true);
684 } else if (c
== 'b' && n
== '"') {
686 ResumeString(styler
, pos
, max
, true);
687 } else if (c
== 'b' && n
== '\'') {
689 ScanCharacterLiteralOrLifetime(styler
, pos
, true);
690 } else if (IsIdentifierStart(c
)) {
691 ScanIdentifier(styler
, pos
, keywords
);
692 } else if (IsADigit(c
)) {
693 ScanNumber(styler
, pos
);
694 } else if (IsThreeCharOperator(c
, n
, n2
)) {
696 styler
.ColourTo(pos
- 1, SCE_RUST_OPERATOR
);
697 } else if (IsTwoCharOperator(c
, n
)) {
699 styler
.ColourTo(pos
- 1, SCE_RUST_OPERATOR
);
700 } else if (IsOneCharOperator(c
)) {
702 styler
.ColourTo(pos
- 1, SCE_RUST_OPERATOR
);
703 } else if (c
== '\'') {
704 ScanCharacterLiteralOrLifetime(styler
, pos
, false);
705 } else if (c
== '"') {
707 ResumeString(styler
, pos
, max
, false);
710 styler
.ColourTo(pos
- 1, SCE_RUST_LEXERROR
);
713 styler
.ColourTo(pos
- 1, SCE_RUST_DEFAULT
);
717 void SCI_METHOD
LexerRust::Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
722 LexAccessor
styler(pAccess
);
724 unsigned int endPos
= startPos
+ length
;
725 int visibleChars
= 0;
726 bool inLineComment
= false;
727 int lineCurrent
= styler
.GetLine(startPos
);
728 int levelCurrent
= SC_FOLDLEVELBASE
;
730 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
731 unsigned int lineStartNext
= styler
.LineStart(lineCurrent
+1);
732 int levelMinCurrent
= levelCurrent
;
733 int levelNext
= levelCurrent
;
734 char chNext
= styler
[startPos
];
735 int styleNext
= styler
.StyleAt(startPos
);
736 int style
= initStyle
;
737 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
738 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
740 chNext
= styler
.SafeGetCharAt(i
+ 1);
741 int stylePrev
= style
;
743 styleNext
= styler
.StyleAt(i
+ 1);
744 bool atEOL
= i
== (lineStartNext
-1);
745 if ((style
== SCE_RUST_COMMENTLINE
) || (style
== SCE_RUST_COMMENTLINEDOC
))
746 inLineComment
= true;
747 if (options
.foldComment
&& options
.foldCommentMultiline
&& IsStreamCommentStyle(style
) && !inLineComment
) {
748 if (!IsStreamCommentStyle(stylePrev
)) {
750 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
751 // Comments don't end at end of line and the next character may be unstyled.
755 if (options
.foldComment
&& options
.foldCommentExplicit
&& ((style
== SCE_RUST_COMMENTLINE
) || options
.foldExplicitAnywhere
)) {
756 if (userDefinedFoldMarkers
) {
757 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
759 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
763 if ((ch
== '/') && (chNext
== '/')) {
764 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
765 if (chNext2
== '{') {
767 } else if (chNext2
== '}') {
773 if (options
.foldSyntaxBased
&& (style
== SCE_RUST_OPERATOR
)) {
775 // Measure the minimum before a '{' to allow
776 // folding on "} else {"
777 if (levelMinCurrent
> levelNext
) {
778 levelMinCurrent
= levelNext
;
781 } else if (ch
== '}') {
787 if (atEOL
|| (i
== endPos
-1)) {
788 int levelUse
= levelCurrent
;
789 if (options
.foldSyntaxBased
&& options
.foldAtElse
) {
790 levelUse
= levelMinCurrent
;
792 int lev
= levelUse
| levelNext
<< 16;
793 if (visibleChars
== 0 && options
.foldCompact
)
794 lev
|= SC_FOLDLEVELWHITEFLAG
;
795 if (levelUse
< levelNext
)
796 lev
|= SC_FOLDLEVELHEADERFLAG
;
797 if (lev
!= styler
.LevelAt(lineCurrent
)) {
798 styler
.SetLevel(lineCurrent
, lev
);
801 lineStartNext
= styler
.LineStart(lineCurrent
+1);
802 levelCurrent
= levelNext
;
803 levelMinCurrent
= levelCurrent
;
804 if (atEOL
&& (i
== static_cast<unsigned int>(styler
.Length()-1))) {
805 // There is an empty line at end of file so give it same level and empty
806 styler
.SetLevel(lineCurrent
, (levelCurrent
| levelCurrent
<< 16) | SC_FOLDLEVELWHITEFLAG
);
809 inLineComment
= false;
814 LexerModule
lmRust(SCLEX_RUST
, LexerRust::LexerFactoryRust
, "rust", rustWordLists
);