1 // Scintilla source code edit control
3 ** Lexer for C++, C, Java, and JavaScript.
4 ** Further folding features and configuration properties added by "Udo Lechner" <dlchnr(at)gmx(dot)net>
6 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
22 #include "Scintilla.h"
26 #include "LexAccessor.h"
28 #include "StyleContext.h"
29 #include "CharacterSet.h"
30 #include "LexerModule.h"
31 #include "OptionSet.h"
32 #include "SparseState.h"
33 #include "SubStyles.h"
36 using namespace Scintilla
;
40 // Use an unnamed namespace to protect the functions and classes from name conflicts
42 bool IsSpaceEquiv(int state
) {
43 return (state
<= SCE_C_COMMENTDOC
) ||
44 // including SCE_C_DEFAULT, SCE_C_COMMENT, SCE_C_COMMENTLINE
45 (state
== SCE_C_COMMENTLINEDOC
) || (state
== SCE_C_COMMENTDOCKEYWORD
) ||
46 (state
== SCE_C_COMMENTDOCKEYWORDERROR
);
49 // Preconditions: sc.currentPos points to a character after '+' or '-'.
50 // The test for pos reaching 0 should be redundant,
51 // and is in only for safety measures.
52 // Limitation: this code will give the incorrect answer for code like
54 // Putting a space between the '++' post-inc operator and the '+' binary op
55 // fixes this, and is highly recommended for readability anyway.
56 bool FollowsPostfixOperator(StyleContext
&sc
, LexAccessor
&styler
) {
57 Sci_Position pos
= (Sci_Position
) sc
.currentPos
;
59 char ch
= styler
[pos
];
60 if (ch
== '+' || ch
== '-') {
61 return styler
[pos
- 1] == ch
;
67 bool followsReturnKeyword(StyleContext
&sc
, LexAccessor
&styler
) {
68 // Don't look at styles, so no need to flush.
69 Sci_Position pos
= (Sci_Position
) sc
.currentPos
;
70 Sci_Position currentLine
= styler
.GetLine(pos
);
71 Sci_Position lineStartPos
= styler
.LineStart(currentLine
);
72 while (--pos
> lineStartPos
) {
73 char ch
= styler
.SafeGetCharAt(pos
);
74 if (ch
!= ' ' && ch
!= '\t') {
78 const char *retBack
= "nruter";
79 const char *s
= retBack
;
81 && pos
>= lineStartPos
82 && styler
.SafeGetCharAt(pos
) == *s
) {
89 bool IsSpaceOrTab(int ch
) {
90 return ch
== ' ' || ch
== '\t';
93 bool OnlySpaceOrTab(const std::string
&s
) {
94 for (std::string::const_iterator it
= s
.begin(); it
!= s
.end(); ++it
) {
95 if (!IsSpaceOrTab(*it
))
101 std::vector
<std::string
> StringSplit(const std::string
&text
, int separator
) {
102 std::vector
<std::string
> vs(text
.empty() ? 0 : 1);
103 for (std::string::const_iterator it
= text
.begin(); it
!= text
.end(); ++it
) {
104 if (*it
== separator
) {
105 vs
.push_back(std::string());
114 std::vector
<std::string
>::iterator itBracket
;
115 std::vector
<std::string
>::iterator itEndBracket
;
118 BracketPair
FindBracketPair(std::vector
<std::string
> &tokens
) {
120 std::vector
<std::string
>::iterator itTok
= std::find(tokens
.begin(), tokens
.end(), "(");
121 bp
.itBracket
= tokens
.end();
122 bp
.itEndBracket
= tokens
.end();
123 if (itTok
!= tokens
.end()) {
124 bp
.itBracket
= itTok
;
126 while (itTok
!= tokens
.end()) {
129 } else if (*itTok
== ")") {
132 bp
.itEndBracket
= itTok
;
139 bp
.itBracket
= tokens
.end();
143 void highlightTaskMarker(StyleContext
&sc
, LexAccessor
&styler
,
144 int activity
, WordList
&markerList
, bool caseSensitive
){
145 if ((isoperator(sc
.chPrev
) || IsASpace(sc
.chPrev
)) && markerList
.Length()) {
146 const int lengthMarker
= 50;
147 char marker
[lengthMarker
+1];
148 Sci_Position currPos
= (Sci_Position
) sc
.currentPos
;
150 while (i
< lengthMarker
) {
151 char ch
= styler
.SafeGetCharAt(currPos
+ i
);
152 if (IsASpace(ch
) || isoperator(ch
)) {
158 marker
[i
] = static_cast<char>(tolower(ch
));
162 if (markerList
.InList(marker
)) {
163 sc
.SetState(SCE_C_TASKMARKER
|activity
);
168 struct EscapeSequence
{
170 CharacterSet setHexDigits
;
171 CharacterSet setOctDigits
;
172 CharacterSet setNoneNumeric
;
173 CharacterSet
*escapeSetValid
;
177 setHexDigits
= CharacterSet(CharacterSet::setDigits
, "ABCDEFabcdef");
178 setOctDigits
= CharacterSet(CharacterSet::setNone
, "01234567");
180 void resetEscapeState(int nextChar
) {
182 escapeSetValid
= &setNoneNumeric
;
183 if (nextChar
== 'U') {
185 escapeSetValid
= &setHexDigits
;
186 } else if (nextChar
== 'u') {
188 escapeSetValid
= &setHexDigits
;
189 } else if (nextChar
== 'x') {
191 escapeSetValid
= &setHexDigits
;
192 } else if (setOctDigits
.Contains(nextChar
)) {
194 escapeSetValid
= &setOctDigits
;
197 bool atEscapeEnd(int currChar
) const {
198 return (digitsLeft
<= 0) || !escapeSetValid
->Contains(currChar
);
202 std::string
GetRestOfLine(LexAccessor
&styler
, Sci_Position start
, bool allowSpace
) {
203 std::string restOfLine
;
205 char ch
= styler
.SafeGetCharAt(start
, '\n');
206 Sci_Position endLine
= styler
.LineEnd(styler
.GetLine(start
));
207 while (((start
+i
) < endLine
) && (ch
!= '\r')) {
208 char chNext
= styler
.SafeGetCharAt(start
+ i
+ 1, '\n');
209 if (ch
== '/' && (chNext
== '/' || chNext
== '*'))
211 if (allowSpace
|| (ch
!= ' '))
219 bool IsStreamCommentStyle(int style
) {
220 return style
== SCE_C_COMMENT
||
221 style
== SCE_C_COMMENTDOC
||
222 style
== SCE_C_COMMENTDOCKEYWORD
||
223 style
== SCE_C_COMMENTDOCKEYWORDERROR
;
226 struct PPDefinition
{
231 std::string arguments
;
232 PPDefinition(Sci_Position line_
, const std::string
&key_
, const std::string
&value_
, bool isUndef_
= false, const std::string
&arguments_
="") :
233 line(line_
), key(key_
), value(value_
), isUndef(isUndef_
), arguments(arguments_
) {
241 bool ValidLevel() const {
242 return level
>= 0 && level
< 32;
244 int maskLevel() const {
248 LinePPState() : state(0), ifTaken(0), level(-1) {
250 bool IsInactive() const {
253 bool CurrentIfTaken() const {
254 return (ifTaken
& maskLevel()) != 0;
256 void StartSection(bool on
) {
260 state
&= ~maskLevel();
261 ifTaken
|= maskLevel();
263 state
|= maskLevel();
264 ifTaken
&= ~maskLevel();
270 state
&= ~maskLevel();
271 ifTaken
&= ~maskLevel();
275 void InvertCurrentLevel() {
277 state
^= maskLevel();
278 ifTaken
|= maskLevel();
283 // Hold the preprocessor state for each line seen.
284 // Currently one entry per line but could become sparse with just one entry per preprocessor line.
286 std::vector
<LinePPState
> vlls
;
288 LinePPState
ForLine(Sci_Position line
) const {
289 if ((line
> 0) && (vlls
.size() > static_cast<size_t>(line
))) {
292 return LinePPState();
295 void Add(Sci_Position line
, LinePPState lls
) {
301 // An individual named option for use in an OptionSet
303 // Options used for LexerCPP
305 bool stylingWithinPreprocessor
;
306 bool identifiersAllowDollars
;
307 bool trackPreprocessor
;
308 bool updatePreprocessor
;
309 bool verbatimStringsAllowEscapes
;
310 bool triplequotedStrings
;
311 bool hashquotedStrings
;
312 bool backQuotedStrings
;
315 bool foldSyntaxBased
;
317 bool foldCommentMultiline
;
318 bool foldCommentExplicit
;
319 std::string foldExplicitStart
;
320 std::string foldExplicitEnd
;
321 bool foldExplicitAnywhere
;
322 bool foldPreprocessor
;
323 bool foldPreprocessorAtElse
;
327 stylingWithinPreprocessor
= false;
328 identifiersAllowDollars
= true;
329 trackPreprocessor
= true;
330 updatePreprocessor
= true;
331 verbatimStringsAllowEscapes
= false;
332 triplequotedStrings
= false;
333 hashquotedStrings
= false;
334 backQuotedStrings
= false;
335 escapeSequence
= false;
337 foldSyntaxBased
= true;
339 foldCommentMultiline
= true;
340 foldCommentExplicit
= true;
341 foldExplicitStart
= "";
342 foldExplicitEnd
= "";
343 foldExplicitAnywhere
= false;
344 foldPreprocessor
= false;
345 foldPreprocessorAtElse
= false;
351 const char *const cppWordLists
[] = {
352 "Primary keywords and identifiers",
353 "Secondary keywords and identifiers",
354 "Documentation comment keywords",
355 "Global classes and typedefs",
356 "Preprocessor definitions",
357 "Task marker and error marker keywords",
361 struct OptionSetCPP
: public OptionSet
<OptionsCPP
> {
363 DefineProperty("styling.within.preprocessor", &OptionsCPP::stylingWithinPreprocessor
,
364 "For C++ code, determines whether all preprocessor code is styled in the "
365 "preprocessor style (0, the default) or only from the initial # to the end "
366 "of the command word(1).");
368 DefineProperty("lexer.cpp.allow.dollars", &OptionsCPP::identifiersAllowDollars
,
369 "Set to 0 to disallow the '$' character in identifiers with the cpp lexer.");
371 DefineProperty("lexer.cpp.track.preprocessor", &OptionsCPP::trackPreprocessor
,
372 "Set to 1 to interpret #if/#else/#endif to grey out code that is not active.");
374 DefineProperty("lexer.cpp.update.preprocessor", &OptionsCPP::updatePreprocessor
,
375 "Set to 1 to update preprocessor definitions when #define found.");
377 DefineProperty("lexer.cpp.verbatim.strings.allow.escapes", &OptionsCPP::verbatimStringsAllowEscapes
,
378 "Set to 1 to allow verbatim strings to contain escape sequences.");
380 DefineProperty("lexer.cpp.triplequoted.strings", &OptionsCPP::triplequotedStrings
,
381 "Set to 1 to enable highlighting of triple-quoted strings.");
383 DefineProperty("lexer.cpp.hashquoted.strings", &OptionsCPP::hashquotedStrings
,
384 "Set to 1 to enable highlighting of hash-quoted strings.");
386 DefineProperty("lexer.cpp.backquoted.strings", &OptionsCPP::backQuotedStrings
,
387 "Set to 1 to enable highlighting of back-quoted raw strings .");
389 DefineProperty("lexer.cpp.escape.sequence", &OptionsCPP::escapeSequence
,
390 "Set to 1 to enable highlighting of escape sequences in strings");
392 DefineProperty("fold", &OptionsCPP::fold
);
394 DefineProperty("fold.cpp.syntax.based", &OptionsCPP::foldSyntaxBased
,
395 "Set this property to 0 to disable syntax based folding.");
397 DefineProperty("fold.comment", &OptionsCPP::foldComment
,
398 "This option enables folding multi-line comments and explicit fold points when using the C++ lexer. "
399 "Explicit fold points allows adding extra folding by placing a //{ comment at the start and a //} "
400 "at the end of a section that should fold.");
402 DefineProperty("fold.cpp.comment.multiline", &OptionsCPP::foldCommentMultiline
,
403 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
405 DefineProperty("fold.cpp.comment.explicit", &OptionsCPP::foldCommentExplicit
,
406 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
408 DefineProperty("fold.cpp.explicit.start", &OptionsCPP::foldExplicitStart
,
409 "The string to use for explicit fold start points, replacing the standard //{.");
411 DefineProperty("fold.cpp.explicit.end", &OptionsCPP::foldExplicitEnd
,
412 "The string to use for explicit fold end points, replacing the standard //}.");
414 DefineProperty("fold.cpp.explicit.anywhere", &OptionsCPP::foldExplicitAnywhere
,
415 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
417 DefineProperty("fold.cpp.preprocessor.at.else", &OptionsCPP::foldPreprocessorAtElse
,
418 "This option enables folding on a preprocessor #else or #endif line of an #if statement.");
420 DefineProperty("fold.preprocessor", &OptionsCPP::foldPreprocessor
,
421 "This option enables folding preprocessor directives when using the C++ lexer. "
422 "Includes C#'s explicit #region and #endregion folding directives.");
424 DefineProperty("fold.compact", &OptionsCPP::foldCompact
);
426 DefineProperty("fold.at.else", &OptionsCPP::foldAtElse
,
427 "This option enables C++ folding on a \"} else {\" line of an if statement.");
429 DefineWordListSets(cppWordLists
);
433 const char styleSubable
[] = {SCE_C_IDENTIFIER
, SCE_C_COMMENTDOCKEYWORD
, 0};
437 class LexerCPP
: public ILexerWithSubStyles
{
439 CharacterSet setWord
;
440 CharacterSet setNegationOp
;
441 CharacterSet setArithmethicOp
;
442 CharacterSet setRelOp
;
443 CharacterSet setLogicalOp
;
444 CharacterSet setWordStart
;
446 std::vector
<PPDefinition
> ppDefineHistory
;
451 WordList ppDefinitions
;
455 std::string arguments
;
456 SymbolValue(const std::string
&value_
="", const std::string
&arguments_
="") : value(value_
), arguments(arguments_
) {
458 SymbolValue
&operator = (const std::string
&value_
) {
463 bool IsMacro() const {
464 return !arguments
.empty();
467 typedef std::map
<std::string
, SymbolValue
> SymbolTable
;
468 SymbolTable preprocessorDefinitionsStart
;
471 EscapeSequence escapeSeq
;
472 SparseState
<std::string
> rawStringTerminators
;
473 enum { activeFlag
= 0x40 };
474 enum { ssIdentifier
, ssDocKeyword
};
477 explicit LexerCPP(bool caseSensitive_
) :
478 caseSensitive(caseSensitive_
),
479 setWord(CharacterSet::setAlphaNum
, "._", 0x80, true),
480 setNegationOp(CharacterSet::setNone
, "!"),
481 setArithmethicOp(CharacterSet::setNone
, "+-/*%"),
482 setRelOp(CharacterSet::setNone
, "=!<>"),
483 setLogicalOp(CharacterSet::setNone
, "|&"),
484 subStyles(styleSubable
, 0x80, 0x40, activeFlag
) {
486 virtual ~LexerCPP() {
488 void SCI_METHOD
Release() {
491 int SCI_METHOD
Version() const {
494 const char * SCI_METHOD
PropertyNames() {
495 return osCPP
.PropertyNames();
497 int SCI_METHOD
PropertyType(const char *name
) {
498 return osCPP
.PropertyType(name
);
500 const char * SCI_METHOD
DescribeProperty(const char *name
) {
501 return osCPP
.DescribeProperty(name
);
503 Sci_Position SCI_METHOD
PropertySet(const char *key
, const char *val
);
504 const char * SCI_METHOD
DescribeWordListSets() {
505 return osCPP
.DescribeWordListSets();
507 Sci_Position SCI_METHOD
WordListSet(int n
, const char *wl
);
508 void SCI_METHOD
Lex(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
);
509 void SCI_METHOD
Fold(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
);
511 void * SCI_METHOD
PrivateCall(int, void *) {
515 int SCI_METHOD
LineEndTypesSupported() {
516 return SC_LINE_END_TYPE_UNICODE
;
519 int SCI_METHOD
AllocateSubStyles(int styleBase
, int numberStyles
) {
520 return subStyles
.Allocate(styleBase
, numberStyles
);
522 int SCI_METHOD
SubStylesStart(int styleBase
) {
523 return subStyles
.Start(styleBase
);
525 int SCI_METHOD
SubStylesLength(int styleBase
) {
526 return subStyles
.Length(styleBase
);
528 int SCI_METHOD
StyleFromSubStyle(int subStyle
) {
529 int styleBase
= subStyles
.BaseStyle(MaskActive(subStyle
));
530 int active
= subStyle
& activeFlag
;
531 return styleBase
| active
;
533 int SCI_METHOD
PrimaryStyleFromStyle(int style
) {
534 return MaskActive(style
);
536 void SCI_METHOD
FreeSubStyles() {
539 void SCI_METHOD
SetIdentifiers(int style
, const char *identifiers
) {
540 subStyles
.SetIdentifiers(style
, identifiers
);
542 int SCI_METHOD
DistanceToSecondaryStyles() {
545 const char * SCI_METHOD
GetSubStyleBases() {
549 static ILexer
*LexerFactoryCPP() {
550 return new LexerCPP(true);
552 static ILexer
*LexerFactoryCPPInsensitive() {
553 return new LexerCPP(false);
555 static int MaskActive(int style
) {
556 return style
& ~activeFlag
;
558 void EvaluateTokens(std::vector
<std::string
> &tokens
, const SymbolTable
&preprocessorDefinitions
);
559 std::vector
<std::string
> Tokenize(const std::string
&expr
) const;
560 bool EvaluateExpression(const std::string
&expr
, const SymbolTable
&preprocessorDefinitions
);
563 Sci_Position SCI_METHOD
LexerCPP::PropertySet(const char *key
, const char *val
) {
564 if (osCPP
.PropertySet(&options
, key
, val
)) {
565 if (strcmp(key
, "lexer.cpp.allow.dollars") == 0) {
566 setWord
= CharacterSet(CharacterSet::setAlphaNum
, "._", 0x80, true);
567 if (options
.identifiersAllowDollars
) {
576 Sci_Position SCI_METHOD
LexerCPP::WordListSet(int n
, const char *wl
) {
577 WordList
*wordListN
= 0;
580 wordListN
= &keywords
;
583 wordListN
= &keywords2
;
586 wordListN
= &keywords3
;
589 wordListN
= &keywords4
;
592 wordListN
= &ppDefinitions
;
595 wordListN
= &markerList
;
598 Sci_Position firstModification
= -1;
602 if (*wordListN
!= wlNew
) {
604 firstModification
= 0;
606 // Rebuild preprocessorDefinitions
607 preprocessorDefinitionsStart
.clear();
608 for (int nDefinition
= 0; nDefinition
< ppDefinitions
.Length(); nDefinition
++) {
609 const char *cpDefinition
= ppDefinitions
.WordAt(nDefinition
);
610 const char *cpEquals
= strchr(cpDefinition
, '=');
612 std::string
name(cpDefinition
, cpEquals
- cpDefinition
);
613 std::string
val(cpEquals
+1);
614 size_t bracket
= name
.find('(');
615 size_t bracketEnd
= name
.find(')');
616 if ((bracket
!= std::string::npos
) && (bracketEnd
!= std::string::npos
)) {
618 std::string args
= name
.substr(bracket
+ 1, bracketEnd
- bracket
- 1);
619 name
= name
.substr(0, bracket
);
620 preprocessorDefinitionsStart
[name
] = SymbolValue(val
, args
);
622 preprocessorDefinitionsStart
[name
] = val
;
625 std::string
name(cpDefinition
);
626 std::string
val("1");
627 preprocessorDefinitionsStart
[name
] = val
;
633 return firstModification
;
636 // Functor used to truncate history
639 explicit After(Sci_Position line_
) : line(line_
) {}
640 bool operator()(PPDefinition
&p
) const {
641 return p
.line
> line
;
645 void SCI_METHOD
LexerCPP::Lex(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) {
646 LexAccessor
styler(pAccess
);
648 CharacterSet
setOKBeforeRE(CharacterSet::setNone
, "([{=,:;!%^&*|?~+-");
649 CharacterSet
setCouldBePostOp(CharacterSet::setNone
, "+-");
651 CharacterSet
setDoxygen(CharacterSet::setAlpha
, "$@\\&<>#{}[]");
653 setWordStart
= CharacterSet(CharacterSet::setAlpha
, "_", 0x80, true);
655 CharacterSet
setInvalidRawFirst(CharacterSet::setNone
, " )\\\t\v\f\n");
657 if (options
.identifiersAllowDollars
) {
658 setWordStart
.Add('$');
661 int chPrevNonWhite
= ' ';
662 int visibleChars
= 0;
663 bool lastWordWasUUID
= false;
664 int styleBeforeDCKeyword
= SCE_C_DEFAULT
;
665 int styleBeforeTaskMarker
= SCE_C_DEFAULT
;
666 bool continuationLine
= false;
667 bool isIncludePreprocessor
= false;
668 bool isStringInPreprocessor
= false;
669 bool inRERange
= false;
670 bool seenDocKeyBrace
= false;
672 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
673 if ((MaskActive(initStyle
) == SCE_C_PREPROCESSOR
) ||
674 (MaskActive(initStyle
) == SCE_C_COMMENTLINE
) ||
675 (MaskActive(initStyle
) == SCE_C_COMMENTLINEDOC
)) {
676 // Set continuationLine if last character of previous line is '\'
677 if (lineCurrent
> 0) {
678 Sci_Position endLinePrevious
= styler
.LineEnd(lineCurrent
- 1);
679 if (endLinePrevious
> 0) {
680 continuationLine
= styler
.SafeGetCharAt(endLinePrevious
-1) == '\\';
685 // look back to set chPrevNonWhite properly for better regex colouring
687 Sci_Position back
= startPos
;
688 while (--back
&& IsSpaceEquiv(MaskActive(styler
.StyleAt(back
))))
690 if (MaskActive(styler
.StyleAt(back
)) == SCE_C_OPERATOR
) {
691 chPrevNonWhite
= styler
.SafeGetCharAt(back
);
695 StyleContext
sc(startPos
, length
, initStyle
, styler
);
696 LinePPState preproc
= vlls
.ForLine(lineCurrent
);
698 bool definitionsChanged
= false;
700 // Truncate ppDefineHistory before current line
702 if (!options
.updatePreprocessor
)
703 ppDefineHistory
.clear();
705 std::vector
<PPDefinition
>::iterator itInvalid
= std::find_if(ppDefineHistory
.begin(), ppDefineHistory
.end(), After(lineCurrent
-1));
706 if (itInvalid
!= ppDefineHistory
.end()) {
707 ppDefineHistory
.erase(itInvalid
, ppDefineHistory
.end());
708 definitionsChanged
= true;
711 SymbolTable preprocessorDefinitions
= preprocessorDefinitionsStart
;
712 for (std::vector
<PPDefinition
>::iterator itDef
= ppDefineHistory
.begin(); itDef
!= ppDefineHistory
.end(); ++itDef
) {
714 preprocessorDefinitions
.erase(itDef
->key
);
716 preprocessorDefinitions
[itDef
->key
] = SymbolValue(itDef
->value
, itDef
->arguments
);
719 std::string rawStringTerminator
= rawStringTerminators
.ValueAt(lineCurrent
-1);
720 SparseState
<std::string
> rawSTNew(lineCurrent
);
722 int activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
724 const WordClassifier
&classifierIdentifiers
= subStyles
.Classifier(SCE_C_IDENTIFIER
);
725 const WordClassifier
&classifierDocKeyWords
= subStyles
.Classifier(SCE_C_COMMENTDOCKEYWORD
);
727 Sci_Position lineEndNext
= styler
.LineEnd(lineCurrent
);
731 if (sc
.atLineStart
) {
732 // Using MaskActive() is not needed in the following statement.
733 // Inside inactive preprocessor declaration, state will be reset anyway at the end of this block.
734 if ((sc
.state
== SCE_C_STRING
) || (sc
.state
== SCE_C_CHARACTER
)) {
735 // Prevent SCE_C_STRINGEOL from leaking back to previous line which
736 // ends with a line continuation by locking in the state up to this position.
737 sc
.SetState(sc
.state
);
739 if ((MaskActive(sc
.state
) == SCE_C_PREPROCESSOR
) && (!continuationLine
)) {
740 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
742 // Reset states to beginning of colourise so no surprises
743 // if different sets of lines lexed.
745 lastWordWasUUID
= false;
746 isIncludePreprocessor
= false;
748 if (preproc
.IsInactive()) {
749 activitySet
= activeFlag
;
750 sc
.SetState(sc
.state
| activitySet
);
756 lineEndNext
= styler
.LineEnd(lineCurrent
);
757 vlls
.Add(lineCurrent
, preproc
);
758 if (rawStringTerminator
!= "") {
759 rawSTNew
.Set(lineCurrent
-1, rawStringTerminator
);
763 // Handle line continuation generically.
765 if (static_cast<Sci_Position
>((sc
.currentPos
+1)) >= lineEndNext
) {
767 lineEndNext
= styler
.LineEnd(lineCurrent
);
768 vlls
.Add(lineCurrent
, preproc
);
769 if (rawStringTerminator
!= "") {
770 rawSTNew
.Set(lineCurrent
-1, rawStringTerminator
);
773 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
774 // Even in UTF-8, \r and \n are separate
777 continuationLine
= true;
783 const bool atLineEndBeforeSwitch
= sc
.atLineEnd
;
785 // Determine if the current state should terminate.
786 switch (MaskActive(sc
.state
)) {
788 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
791 // We accept almost anything because of hex. and number suffixes
793 sc
.ChangeState(SCE_C_USERLITERAL
|activitySet
);
794 } else if (!(setWord
.Contains(sc
.ch
)
796 || ((sc
.ch
== '+' || sc
.ch
== '-') && (sc
.chPrev
== 'e' || sc
.chPrev
== 'E' ||
797 sc
.chPrev
== 'p' || sc
.chPrev
== 'P')))) {
798 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
801 case SCE_C_USERLITERAL
:
802 if (!(setWord
.Contains(sc
.ch
)))
803 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
805 case SCE_C_IDENTIFIER
:
806 if (sc
.atLineStart
|| sc
.atLineEnd
|| !setWord
.Contains(sc
.ch
) || (sc
.ch
== '.')) {
809 sc
.GetCurrent(s
, sizeof(s
));
811 sc
.GetCurrentLowered(s
, sizeof(s
));
813 if (keywords
.InList(s
)) {
814 lastWordWasUUID
= strcmp(s
, "uuid") == 0;
815 sc
.ChangeState(SCE_C_WORD
|activitySet
);
816 } else if (keywords2
.InList(s
)) {
817 sc
.ChangeState(SCE_C_WORD2
|activitySet
);
818 } else if (keywords4
.InList(s
)) {
819 sc
.ChangeState(SCE_C_GLOBALCLASS
|activitySet
);
821 int subStyle
= classifierIdentifiers
.ValueFor(s
);
823 sc
.ChangeState(subStyle
|activitySet
);
826 const bool literalString
= sc
.ch
== '\"';
827 if (literalString
|| sc
.ch
== '\'') {
828 size_t lenS
= strlen(s
);
829 const bool raw
= literalString
&& sc
.chPrev
== 'R' && !setInvalidRawFirst
.Contains(sc
.chNext
);
834 ((lenS
== 1) && ((s
[0] == 'L') || (s
[0] == 'u') || (s
[0] == 'U'))) ||
835 ((lenS
== 2) && literalString
&& (s
[0] == 'u') && (s
[1] == '8'));
839 // Set the style of the string prefix to SCE_C_STRINGRAW but then change to
840 // SCE_C_DEFAULT as that allows the raw string start code to run.
841 sc
.ChangeState(SCE_C_STRINGRAW
|activitySet
);
842 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
844 sc
.ChangeState(SCE_C_STRING
|activitySet
);
847 sc
.ChangeState(SCE_C_CHARACTER
|activitySet
);
850 sc
.SetState(SCE_C_DEFAULT
| activitySet
);
853 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
857 case SCE_C_PREPROCESSOR
:
858 if (options
.stylingWithinPreprocessor
) {
859 if (IsASpace(sc
.ch
)) {
860 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
862 } else if (isStringInPreprocessor
&& (sc
.Match('>') || sc
.Match('\"') || sc
.atLineEnd
)) {
863 isStringInPreprocessor
= false;
864 } else if (!isStringInPreprocessor
) {
865 if ((isIncludePreprocessor
&& sc
.Match('<')) || sc
.Match('\"')) {
866 isStringInPreprocessor
= true;
867 } else if (sc
.Match('/', '*')) {
868 if (sc
.Match("/**") || sc
.Match("/*!")) {
869 sc
.SetState(SCE_C_PREPROCESSORCOMMENTDOC
|activitySet
);
871 sc
.SetState(SCE_C_PREPROCESSORCOMMENT
|activitySet
);
873 sc
.Forward(); // Eat the *
874 } else if (sc
.Match('/', '/')) {
875 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
879 case SCE_C_PREPROCESSORCOMMENT
:
880 case SCE_C_PREPROCESSORCOMMENTDOC
:
881 if (sc
.Match('*', '/')) {
883 sc
.ForwardSetState(SCE_C_PREPROCESSOR
|activitySet
);
884 continue; // Without advancing in case of '\'.
888 if (sc
.Match('*', '/')) {
890 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
892 styleBeforeTaskMarker
= SCE_C_COMMENT
;
893 highlightTaskMarker(sc
, styler
, activitySet
, markerList
, caseSensitive
);
896 case SCE_C_COMMENTDOC
:
897 if (sc
.Match('*', '/')) {
899 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
900 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
901 // Verify that we have the conditions to mark a comment-doc-keyword
902 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
903 styleBeforeDCKeyword
= SCE_C_COMMENTDOC
;
904 sc
.SetState(SCE_C_COMMENTDOCKEYWORD
|activitySet
);
908 case SCE_C_COMMENTLINE
:
909 if (sc
.atLineStart
&& !continuationLine
) {
910 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
912 styleBeforeTaskMarker
= SCE_C_COMMENTLINE
;
913 highlightTaskMarker(sc
, styler
, activitySet
, markerList
, caseSensitive
);
916 case SCE_C_COMMENTLINEDOC
:
917 if (sc
.atLineStart
&& !continuationLine
) {
918 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
919 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
920 // Verify that we have the conditions to mark a comment-doc-keyword
921 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '/' || sc
.chPrev
== '!') && (!IsASpace(sc
.chNext
))) {
922 styleBeforeDCKeyword
= SCE_C_COMMENTLINEDOC
;
923 sc
.SetState(SCE_C_COMMENTDOCKEYWORD
|activitySet
);
927 case SCE_C_COMMENTDOCKEYWORD
:
928 if ((styleBeforeDCKeyword
== SCE_C_COMMENTDOC
) && sc
.Match('*', '/')) {
929 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
);
931 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
932 seenDocKeyBrace
= false;
933 } else if (sc
.ch
== '[' || sc
.ch
== '{') {
934 seenDocKeyBrace
= true;
935 } else if (!setDoxygen
.Contains(sc
.ch
)
936 && !(seenDocKeyBrace
&& (sc
.ch
== ',' || sc
.ch
== '.'))) {
939 sc
.GetCurrent(s
, sizeof(s
));
941 sc
.GetCurrentLowered(s
, sizeof(s
));
943 if (!(IsASpace(sc
.ch
) || (sc
.ch
== 0))) {
944 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
|activitySet
);
945 } else if (!keywords3
.InList(s
+ 1)) {
946 int subStyleCDKW
= classifierDocKeyWords
.ValueFor(s
+1);
947 if (subStyleCDKW
>= 0) {
948 sc
.ChangeState(subStyleCDKW
|activitySet
);
950 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
|activitySet
);
953 sc
.SetState(styleBeforeDCKeyword
|activitySet
);
954 seenDocKeyBrace
= false;
959 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
960 } else if (isIncludePreprocessor
) {
962 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
963 isIncludePreprocessor
= false;
965 } else if (sc
.ch
== '\\') {
966 if (options
.escapeSequence
) {
967 sc
.SetState(SCE_C_ESCAPESEQUENCE
|activitySet
);
968 escapeSeq
.resetEscapeState(sc
.chNext
);
970 sc
.Forward(); // Skip all characters after the backslash
971 } else if (sc
.ch
== '\"') {
972 if (sc
.chNext
== '_') {
973 sc
.ChangeState(SCE_C_USERLITERAL
|activitySet
);
975 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
979 case SCE_C_ESCAPESEQUENCE
:
980 escapeSeq
.digitsLeft
--;
981 if (!escapeSeq
.atEscapeEnd(sc
.ch
)) {
985 sc
.SetState(SCE_C_STRING
|activitySet
);
986 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
987 } else if (sc
.ch
== '\\') {
988 escapeSeq
.resetEscapeState(sc
.chNext
);
991 sc
.SetState(SCE_C_STRING
|activitySet
);
993 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
997 case SCE_C_HASHQUOTEDSTRING
:
999 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
1002 } else if (sc
.ch
== '\"') {
1003 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
1006 case SCE_C_STRINGRAW
:
1007 if (sc
.Match(rawStringTerminator
.c_str())) {
1008 for (size_t termPos
=rawStringTerminator
.size(); termPos
; termPos
--)
1010 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1011 rawStringTerminator
= "";
1014 case SCE_C_CHARACTER
:
1016 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
1017 } else if (sc
.ch
== '\\') {
1018 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
1021 } else if (sc
.ch
== '\'') {
1022 if (sc
.chNext
== '_') {
1023 sc
.ChangeState(SCE_C_USERLITERAL
|activitySet
);
1025 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
1030 if (sc
.atLineStart
) {
1031 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1032 } else if (! inRERange
&& sc
.ch
== '/') {
1034 while ((sc
.ch
< 0x80) && islower(sc
.ch
))
1035 sc
.Forward(); // gobble regex flags
1036 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1037 } else if (sc
.ch
== '\\' && (static_cast<Sci_Position
>(sc
.currentPos
+1) < lineEndNext
)) {
1038 // Gobble up the escaped character
1040 } else if (sc
.ch
== '[') {
1042 } else if (sc
.ch
== ']') {
1046 case SCE_C_STRINGEOL
:
1047 if (sc
.atLineStart
) {
1048 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1051 case SCE_C_VERBATIM
:
1052 if (options
.verbatimStringsAllowEscapes
&& (sc
.ch
== '\\')) {
1053 sc
.Forward(); // Skip all characters after the backslash
1054 } else if (sc
.ch
== '\"') {
1055 if (sc
.chNext
== '\"') {
1058 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
1062 case SCE_C_TRIPLEVERBATIM
:
1063 if (sc
.Match("\"\"\"")) {
1064 while (sc
.Match('"')) {
1067 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1071 if (sc
.atLineEnd
|| sc
.ch
== ')') {
1072 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1075 case SCE_C_TASKMARKER
:
1076 if (isoperator(sc
.ch
) || IsASpace(sc
.ch
)) {
1077 sc
.SetState(styleBeforeTaskMarker
|activitySet
);
1078 styleBeforeTaskMarker
= SCE_C_DEFAULT
;
1082 if (sc
.atLineEnd
&& !atLineEndBeforeSwitch
) {
1083 // State exit processing consumed characters up to end of line.
1085 lineEndNext
= styler
.LineEnd(lineCurrent
);
1086 vlls
.Add(lineCurrent
, preproc
);
1089 // Determine if a new state should be entered.
1090 if (MaskActive(sc
.state
) == SCE_C_DEFAULT
) {
1091 if (sc
.Match('@', '\"')) {
1092 sc
.SetState(SCE_C_VERBATIM
|activitySet
);
1094 } else if (options
.triplequotedStrings
&& sc
.Match("\"\"\"")) {
1095 sc
.SetState(SCE_C_TRIPLEVERBATIM
|activitySet
);
1097 } else if (options
.hashquotedStrings
&& sc
.Match('#', '\"')) {
1098 sc
.SetState(SCE_C_HASHQUOTEDSTRING
|activitySet
);
1100 } else if (options
.backQuotedStrings
&& sc
.Match('`')) {
1101 sc
.SetState(SCE_C_STRINGRAW
|activitySet
);
1102 rawStringTerminator
= "`";
1103 } else if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
1104 if (lastWordWasUUID
) {
1105 sc
.SetState(SCE_C_UUID
|activitySet
);
1106 lastWordWasUUID
= false;
1108 sc
.SetState(SCE_C_NUMBER
|activitySet
);
1110 } else if (!sc
.atLineEnd
&& (setWordStart
.Contains(sc
.ch
) || (sc
.ch
== '@'))) {
1111 if (lastWordWasUUID
) {
1112 sc
.SetState(SCE_C_UUID
|activitySet
);
1113 lastWordWasUUID
= false;
1115 sc
.SetState(SCE_C_IDENTIFIER
|activitySet
);
1117 } else if (sc
.Match('/', '*')) {
1118 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Qt/Doxygen doc. style
1119 sc
.SetState(SCE_C_COMMENTDOC
|activitySet
);
1121 sc
.SetState(SCE_C_COMMENT
|activitySet
);
1123 sc
.Forward(); // Eat the * so it isn't used for the end of the comment
1124 } else if (sc
.Match('/', '/')) {
1125 if ((sc
.Match("///") && !sc
.Match("////")) || sc
.Match("//!"))
1126 // Support of Qt/Doxygen doc. style
1127 sc
.SetState(SCE_C_COMMENTLINEDOC
|activitySet
);
1129 sc
.SetState(SCE_C_COMMENTLINE
|activitySet
);
1130 } else if (sc
.ch
== '/'
1131 && (setOKBeforeRE
.Contains(chPrevNonWhite
)
1132 || followsReturnKeyword(sc
, styler
))
1133 && (!setCouldBePostOp
.Contains(chPrevNonWhite
)
1134 || !FollowsPostfixOperator(sc
, styler
))) {
1135 sc
.SetState(SCE_C_REGEX
|activitySet
); // JavaScript's RegEx
1137 } else if (sc
.ch
== '\"') {
1138 if (sc
.chPrev
== 'R') {
1140 if (MaskActive(styler
.StyleAt(sc
.currentPos
- 1)) == SCE_C_STRINGRAW
) {
1141 sc
.SetState(SCE_C_STRINGRAW
|activitySet
);
1142 rawStringTerminator
= ")";
1143 for (Sci_Position termPos
= sc
.currentPos
+ 1;; termPos
++) {
1144 char chTerminator
= styler
.SafeGetCharAt(termPos
, '(');
1145 if (chTerminator
== '(')
1147 rawStringTerminator
+= chTerminator
;
1149 rawStringTerminator
+= '\"';
1151 sc
.SetState(SCE_C_STRING
|activitySet
);
1154 sc
.SetState(SCE_C_STRING
|activitySet
);
1156 isIncludePreprocessor
= false; // ensure that '>' won't end the string
1157 } else if (isIncludePreprocessor
&& sc
.ch
== '<') {
1158 sc
.SetState(SCE_C_STRING
|activitySet
);
1159 } else if (sc
.ch
== '\'') {
1160 sc
.SetState(SCE_C_CHARACTER
|activitySet
);
1161 } else if (sc
.ch
== '#' && visibleChars
== 0) {
1162 // Preprocessor commands are alone on their line
1163 sc
.SetState(SCE_C_PREPROCESSOR
|activitySet
);
1164 // Skip whitespace between # and preprocessor word
1167 } while ((sc
.ch
== ' ' || sc
.ch
== '\t') && sc
.More());
1169 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1170 } else if (sc
.Match("include")) {
1171 isIncludePreprocessor
= true;
1173 if (options
.trackPreprocessor
) {
1174 if (sc
.Match("ifdef") || sc
.Match("ifndef")) {
1175 bool isIfDef
= sc
.Match("ifdef");
1176 int i
= isIfDef
? 5 : 6;
1177 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ i
+ 1, false);
1178 bool foundDef
= preprocessorDefinitions
.find(restOfLine
) != preprocessorDefinitions
.end();
1179 preproc
.StartSection(isIfDef
== foundDef
);
1180 } else if (sc
.Match("if")) {
1181 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 2, true);
1182 bool ifGood
= EvaluateExpression(restOfLine
, preprocessorDefinitions
);
1183 preproc
.StartSection(ifGood
);
1184 } else if (sc
.Match("else")) {
1185 if (!preproc
.CurrentIfTaken()) {
1186 preproc
.InvertCurrentLevel();
1187 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1189 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1190 } else if (!preproc
.IsInactive()) {
1191 preproc
.InvertCurrentLevel();
1192 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1194 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1196 } else if (sc
.Match("elif")) {
1197 // Ensure only one chosen out of #if .. #elif .. #elif .. #else .. #endif
1198 if (!preproc
.CurrentIfTaken()) {
1200 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 2, true);
1201 bool ifGood
= EvaluateExpression(restOfLine
, preprocessorDefinitions
);
1203 preproc
.InvertCurrentLevel();
1204 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1206 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1208 } else if (!preproc
.IsInactive()) {
1209 preproc
.InvertCurrentLevel();
1210 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1212 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1214 } else if (sc
.Match("endif")) {
1215 preproc
.EndSection();
1216 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1217 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1218 } else if (sc
.Match("define")) {
1219 if (options
.updatePreprocessor
&& !preproc
.IsInactive()) {
1220 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 6, true);
1221 size_t startName
= 0;
1222 while ((startName
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[startName
]))
1224 size_t endName
= startName
;
1225 while ((endName
< restOfLine
.length()) && setWord
.Contains(static_cast<unsigned char>(restOfLine
[endName
])))
1227 std::string key
= restOfLine
.substr(startName
, endName
-startName
);
1228 if ((endName
< restOfLine
.length()) && (restOfLine
.at(endName
) == '(')) {
1230 size_t endArgs
= endName
;
1231 while ((endArgs
< restOfLine
.length()) && (restOfLine
[endArgs
] != ')'))
1233 std::string args
= restOfLine
.substr(endName
+ 1, endArgs
- endName
- 1);
1234 size_t startValue
= endArgs
+1;
1235 while ((startValue
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[startValue
]))
1238 if (startValue
< restOfLine
.length())
1239 value
= restOfLine
.substr(startValue
);
1240 preprocessorDefinitions
[key
] = SymbolValue(value
, args
);
1241 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, value
, false, args
));
1242 definitionsChanged
= true;
1245 size_t startValue
= endName
;
1246 while ((startValue
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[startValue
]))
1248 std::string value
= restOfLine
.substr(startValue
);
1249 preprocessorDefinitions
[key
] = value
;
1250 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, value
));
1251 definitionsChanged
= true;
1254 } else if (sc
.Match("undef")) {
1255 if (options
.updatePreprocessor
&& !preproc
.IsInactive()) {
1256 const std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 5, false);
1257 std::vector
<std::string
> tokens
= Tokenize(restOfLine
);
1258 if (tokens
.size() >= 1) {
1259 const std::string key
= tokens
[0];
1260 preprocessorDefinitions
.erase(key
);
1261 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, "", true));
1262 definitionsChanged
= true;
1268 } else if (isoperator(sc
.ch
)) {
1269 sc
.SetState(SCE_C_OPERATOR
|activitySet
);
1273 if (!IsASpace(sc
.ch
) && !IsSpaceEquiv(MaskActive(sc
.state
))) {
1274 chPrevNonWhite
= sc
.ch
;
1277 continuationLine
= false;
1280 const bool rawStringsChanged
= rawStringTerminators
.Merge(rawSTNew
, lineCurrent
);
1281 if (definitionsChanged
|| rawStringsChanged
)
1282 styler
.ChangeLexerState(startPos
, startPos
+ length
);
1286 // Store both the current line's fold level and the next lines in the
1287 // level store to make it easy to pick up with each increment
1288 // and to make it possible to fiddle the current level for "} else {".
1290 void SCI_METHOD
LexerCPP::Fold(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) {
1295 LexAccessor
styler(pAccess
);
1297 Sci_PositionU endPos
= startPos
+ length
;
1298 int visibleChars
= 0;
1299 bool inLineComment
= false;
1300 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
1301 int levelCurrent
= SC_FOLDLEVELBASE
;
1302 if (lineCurrent
> 0)
1303 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
1304 Sci_PositionU lineStartNext
= styler
.LineStart(lineCurrent
+1);
1305 int levelMinCurrent
= levelCurrent
;
1306 int levelNext
= levelCurrent
;
1307 char chNext
= styler
[startPos
];
1308 int styleNext
= MaskActive(styler
.StyleAt(startPos
));
1309 int style
= MaskActive(initStyle
);
1310 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
1311 for (Sci_PositionU i
= startPos
; i
< endPos
; i
++) {
1313 chNext
= styler
.SafeGetCharAt(i
+ 1);
1314 int stylePrev
= style
;
1316 styleNext
= MaskActive(styler
.StyleAt(i
+ 1));
1317 bool atEOL
= i
== (lineStartNext
-1);
1318 if ((style
== SCE_C_COMMENTLINE
) || (style
== SCE_C_COMMENTLINEDOC
))
1319 inLineComment
= true;
1320 if (options
.foldComment
&& options
.foldCommentMultiline
&& IsStreamCommentStyle(style
) && !inLineComment
) {
1321 if (!IsStreamCommentStyle(stylePrev
)) {
1323 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
1324 // Comments don't end at end of line and the next character may be unstyled.
1328 if (options
.foldComment
&& options
.foldCommentExplicit
&& ((style
== SCE_C_COMMENTLINE
) || options
.foldExplicitAnywhere
)) {
1329 if (userDefinedFoldMarkers
) {
1330 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
1332 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
1336 if ((ch
== '/') && (chNext
== '/')) {
1337 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
1338 if (chNext2
== '{') {
1340 } else if (chNext2
== '}') {
1346 if (options
.foldPreprocessor
&& (style
== SCE_C_PREPROCESSOR
)) {
1348 Sci_PositionU j
= i
+ 1;
1349 while ((j
< endPos
) && IsASpaceOrTab(styler
.SafeGetCharAt(j
))) {
1352 if (styler
.Match(j
, "region") || styler
.Match(j
, "if")) {
1354 } else if (styler
.Match(j
, "end")) {
1358 if (options
.foldPreprocessorAtElse
&& (styler
.Match(j
, "else") || styler
.Match(j
, "elif"))) {
1363 if (options
.foldSyntaxBased
&& (style
== SCE_C_OPERATOR
)) {
1364 if (ch
== '{' || ch
== '[' || ch
== '(') {
1365 // Measure the minimum before a '{' to allow
1366 // folding on "} else {"
1367 if (options
.foldAtElse
&& levelMinCurrent
> levelNext
) {
1368 levelMinCurrent
= levelNext
;
1371 } else if (ch
== '}' || ch
== ']' || ch
== ')') {
1377 if (atEOL
|| (i
== endPos
-1)) {
1378 int levelUse
= levelCurrent
;
1379 if ((options
.foldSyntaxBased
&& options
.foldAtElse
) ||
1380 (options
.foldPreprocessor
&& options
.foldPreprocessorAtElse
)
1382 levelUse
= levelMinCurrent
;
1384 int lev
= levelUse
| levelNext
<< 16;
1385 if (visibleChars
== 0 && options
.foldCompact
)
1386 lev
|= SC_FOLDLEVELWHITEFLAG
;
1387 if (levelUse
< levelNext
)
1388 lev
|= SC_FOLDLEVELHEADERFLAG
;
1389 if (lev
!= styler
.LevelAt(lineCurrent
)) {
1390 styler
.SetLevel(lineCurrent
, lev
);
1393 lineStartNext
= styler
.LineStart(lineCurrent
+1);
1394 levelCurrent
= levelNext
;
1395 levelMinCurrent
= levelCurrent
;
1396 if (atEOL
&& (i
== static_cast<Sci_PositionU
>(styler
.Length()-1))) {
1397 // There is an empty line at end of file so give it same level and empty
1398 styler
.SetLevel(lineCurrent
, (levelCurrent
| levelCurrent
<< 16) | SC_FOLDLEVELWHITEFLAG
);
1401 inLineComment
= false;
1406 void LexerCPP::EvaluateTokens(std::vector
<std::string
> &tokens
, const SymbolTable
&preprocessorDefinitions
) {
1408 // Remove whitespace tokens
1409 tokens
.erase(std::remove_if(tokens
.begin(), tokens
.end(), OnlySpaceOrTab
), tokens
.end());
1411 // Evaluate defined statements to either 0 or 1
1412 for (size_t i
=0; (i
+1)<tokens
.size();) {
1413 if (tokens
[i
] == "defined") {
1414 const char *val
= "0";
1415 if (tokens
[i
+1] == "(") {
1416 if (((i
+ 2)<tokens
.size()) && (tokens
[i
+ 2] == ")")) {
1418 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 3);
1419 } else if (((i
+3)<tokens
.size()) && (tokens
[i
+3] == ")")) {
1420 // defined(<identifier>)
1421 SymbolTable::const_iterator it
= preprocessorDefinitions
.find(tokens
[i
+2]);
1422 if (it
!= preprocessorDefinitions
.end()) {
1425 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 4);
1427 // Spurious '(' so erase as more likely to result in false
1428 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 2);
1431 // defined <identifier>
1432 SymbolTable::const_iterator it
= preprocessorDefinitions
.find(tokens
[i
+1]);
1433 if (it
!= preprocessorDefinitions
.end()) {
1443 // Evaluate identifiers
1444 const size_t maxIterations
= 100;
1445 size_t iterations
= 0; // Limit number of iterations in case there is a recursive macro.
1446 for (size_t i
= 0; (i
<tokens
.size()) && (iterations
< maxIterations
);) {
1448 if (setWordStart
.Contains(static_cast<unsigned char>(tokens
[i
][0]))) {
1449 SymbolTable::const_iterator it
= preprocessorDefinitions
.find(tokens
[i
]);
1450 if (it
!= preprocessorDefinitions
.end()) {
1452 std::vector
<std::string
> macroTokens
= Tokenize(it
->second
.value
);
1453 if (it
->second
.IsMacro()) {
1454 if ((i
+ 1 < tokens
.size()) && (tokens
.at(i
+ 1) == "(")) {
1455 // Create map of argument name to value
1456 std::vector
<std::string
> argumentNames
= StringSplit(it
->second
.arguments
, ',');
1457 std::map
<std::string
, std::string
> arguments
;
1460 while ((tok
< tokens
.size()) && (arg
< argumentNames
.size()) && (tokens
.at(tok
) != ")")) {
1461 if (tokens
.at(tok
) != ",") {
1462 arguments
[argumentNames
.at(arg
)] = tokens
.at(tok
);
1468 // Remove invocation
1469 tokens
.erase(tokens
.begin() + i
, tokens
.begin() + tok
+ 1);
1471 // Substitute values into macro
1472 macroTokens
.erase(std::remove_if(macroTokens
.begin(), macroTokens
.end(), OnlySpaceOrTab
), macroTokens
.end());
1474 for (size_t iMacro
= 0; iMacro
< macroTokens
.size();) {
1475 if (setWordStart
.Contains(static_cast<unsigned char>(macroTokens
[iMacro
][0]))) {
1476 std::map
<std::string
, std::string
>::const_iterator itFind
= arguments
.find(macroTokens
[iMacro
]);
1477 if (itFind
!= arguments
.end()) {
1478 // TODO: Possible that value will be expression so should insert tokenized form
1479 macroTokens
[iMacro
] = itFind
->second
;
1485 // Insert results back into tokens
1486 tokens
.insert(tokens
.begin() + i
, macroTokens
.begin(), macroTokens
.end());
1492 // Remove invocation
1493 tokens
.erase(tokens
.begin() + i
);
1494 // Insert results back into tokens
1495 tokens
.insert(tokens
.begin() + i
, macroTokens
.begin(), macroTokens
.end());
1498 // Identifier not found
1499 tokens
.erase(tokens
.begin() + i
);
1506 // Find bracketed subexpressions and recurse on them
1507 BracketPair bracketPair
= FindBracketPair(tokens
);
1508 while (bracketPair
.itBracket
!= tokens
.end()) {
1509 std::vector
<std::string
> inBracket(bracketPair
.itBracket
+ 1, bracketPair
.itEndBracket
);
1510 EvaluateTokens(inBracket
, preprocessorDefinitions
);
1512 // The insertion is done before the removal because there were failures with the opposite approach
1513 tokens
.insert(bracketPair
.itBracket
, inBracket
.begin(), inBracket
.end());
1515 bracketPair
= FindBracketPair(tokens
);
1516 tokens
.erase(bracketPair
.itBracket
, bracketPair
.itEndBracket
+ 1);
1518 bracketPair
= FindBracketPair(tokens
);
1521 // Evaluate logical negations
1522 for (size_t j
=0; (j
+1)<tokens
.size();) {
1523 if (setNegationOp
.Contains(tokens
[j
][0])) {
1524 int isTrue
= atoi(tokens
[j
+1].c_str());
1525 if (tokens
[j
] == "!")
1527 std::vector
<std::string
>::iterator itInsert
=
1528 tokens
.erase(tokens
.begin() + j
, tokens
.begin() + j
+ 2);
1529 tokens
.insert(itInsert
, isTrue
? "1" : "0");
1535 // Evaluate expressions in precedence order
1536 enum precedence
{ precArithmetic
, precRelative
, precLogical
};
1537 for (int prec
=precArithmetic
; prec
<= precLogical
; prec
++) {
1538 // Looking at 3 tokens at a time so end at 2 before end
1539 for (size_t k
=0; (k
+2)<tokens
.size();) {
1540 char chOp
= tokens
[k
+1][0];
1542 ((prec
==precArithmetic
) && setArithmethicOp
.Contains(chOp
)) ||
1543 ((prec
==precRelative
) && setRelOp
.Contains(chOp
)) ||
1544 ((prec
==precLogical
) && setLogicalOp
.Contains(chOp
))
1546 int valA
= atoi(tokens
[k
].c_str());
1547 int valB
= atoi(tokens
[k
+2].c_str());
1549 if (tokens
[k
+1] == "+")
1550 result
= valA
+ valB
;
1551 else if (tokens
[k
+1] == "-")
1552 result
= valA
- valB
;
1553 else if (tokens
[k
+1] == "*")
1554 result
= valA
* valB
;
1555 else if (tokens
[k
+1] == "/")
1556 result
= valA
/ (valB
? valB
: 1);
1557 else if (tokens
[k
+1] == "%")
1558 result
= valA
% (valB
? valB
: 1);
1559 else if (tokens
[k
+1] == "<")
1560 result
= valA
< valB
;
1561 else if (tokens
[k
+1] == "<=")
1562 result
= valA
<= valB
;
1563 else if (tokens
[k
+1] == ">")
1564 result
= valA
> valB
;
1565 else if (tokens
[k
+1] == ">=")
1566 result
= valA
>= valB
;
1567 else if (tokens
[k
+1] == "==")
1568 result
= valA
== valB
;
1569 else if (tokens
[k
+1] == "!=")
1570 result
= valA
!= valB
;
1571 else if (tokens
[k
+1] == "||")
1572 result
= valA
|| valB
;
1573 else if (tokens
[k
+1] == "&&")
1574 result
= valA
&& valB
;
1576 sprintf(sResult
, "%d", result
);
1577 std::vector
<std::string
>::iterator itInsert
=
1578 tokens
.erase(tokens
.begin() + k
, tokens
.begin() + k
+ 3);
1579 tokens
.insert(itInsert
, sResult
);
1587 std::vector
<std::string
> LexerCPP::Tokenize(const std::string
&expr
) const {
1588 // Break into tokens
1589 std::vector
<std::string
> tokens
;
1590 const char *cp
= expr
.c_str();
1593 if (setWord
.Contains(static_cast<unsigned char>(*cp
))) {
1594 // Identifiers and numbers
1595 while (setWord
.Contains(static_cast<unsigned char>(*cp
))) {
1599 } else if (IsSpaceOrTab(*cp
)) {
1600 while (IsSpaceOrTab(*cp
)) {
1604 } else if (setRelOp
.Contains(static_cast<unsigned char>(*cp
))) {
1607 if (setRelOp
.Contains(static_cast<unsigned char>(*cp
))) {
1611 } else if (setLogicalOp
.Contains(static_cast<unsigned char>(*cp
))) {
1614 if (setLogicalOp
.Contains(static_cast<unsigned char>(*cp
))) {
1619 // Should handle strings, characters, and comments here
1623 tokens
.push_back(word
);
1628 bool LexerCPP::EvaluateExpression(const std::string
&expr
, const SymbolTable
&preprocessorDefinitions
) {
1629 std::vector
<std::string
> tokens
= Tokenize(expr
);
1631 EvaluateTokens(tokens
, preprocessorDefinitions
);
1633 // "0" or "" -> false else true
1634 bool isFalse
= tokens
.empty() ||
1635 ((tokens
.size() == 1) && ((tokens
[0] == "") || tokens
[0] == "0"));
1639 LexerModule
lmCPP(SCLEX_CPP
, LexerCPP::LexerFactoryCPP
, "cpp", cppWordLists
);
1640 LexerModule
lmCPPNoCase(SCLEX_CPPNOCASE
, LexerCPP::LexerFactoryCPPInsensitive
, "cppnocase", cppWordLists
);