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 int pos
= (int) 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 int pos
= (int) sc
.currentPos
;
70 int currentLine
= styler
.GetLine(pos
);
71 int 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 int currPos
= (int) 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
, int start
, bool allowSpace
) {
203 std::string restOfLine
;
205 char ch
= styler
.SafeGetCharAt(start
, '\n');
206 int 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(int line_
, const std::string
&key_
, const std::string
&value_
, bool isUndef_
= false, 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(int line
) const {
289 if ((line
> 0) && (vlls
.size() > static_cast<size_t>(line
))) {
292 return LinePPState();
295 void Add(int 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 triplequotedStrings
;
310 bool hashquotedStrings
;
311 bool backQuotedStrings
;
314 bool foldSyntaxBased
;
316 bool foldCommentMultiline
;
317 bool foldCommentExplicit
;
318 std::string foldExplicitStart
;
319 std::string foldExplicitEnd
;
320 bool foldExplicitAnywhere
;
321 bool foldPreprocessor
;
325 stylingWithinPreprocessor
= false;
326 identifiersAllowDollars
= true;
327 trackPreprocessor
= true;
328 updatePreprocessor
= true;
329 triplequotedStrings
= false;
330 hashquotedStrings
= false;
331 backQuotedStrings
= false;
332 escapeSequence
= false;
334 foldSyntaxBased
= true;
336 foldCommentMultiline
= true;
337 foldCommentExplicit
= true;
338 foldExplicitStart
= "";
339 foldExplicitEnd
= "";
340 foldExplicitAnywhere
= false;
341 foldPreprocessor
= false;
347 const char *const cppWordLists
[] = {
348 "Primary keywords and identifiers",
349 "Secondary keywords and identifiers",
350 "Documentation comment keywords",
351 "Global classes and typedefs",
352 "Preprocessor definitions",
353 "Task marker and error marker keywords",
357 struct OptionSetCPP
: public OptionSet
<OptionsCPP
> {
359 DefineProperty("styling.within.preprocessor", &OptionsCPP::stylingWithinPreprocessor
,
360 "For C++ code, determines whether all preprocessor code is styled in the "
361 "preprocessor style (0, the default) or only from the initial # to the end "
362 "of the command word(1).");
364 DefineProperty("lexer.cpp.allow.dollars", &OptionsCPP::identifiersAllowDollars
,
365 "Set to 0 to disallow the '$' character in identifiers with the cpp lexer.");
367 DefineProperty("lexer.cpp.track.preprocessor", &OptionsCPP::trackPreprocessor
,
368 "Set to 1 to interpret #if/#else/#endif to grey out code that is not active.");
370 DefineProperty("lexer.cpp.update.preprocessor", &OptionsCPP::updatePreprocessor
,
371 "Set to 1 to update preprocessor definitions when #define found.");
373 DefineProperty("lexer.cpp.triplequoted.strings", &OptionsCPP::triplequotedStrings
,
374 "Set to 1 to enable highlighting of triple-quoted strings.");
376 DefineProperty("lexer.cpp.hashquoted.strings", &OptionsCPP::hashquotedStrings
,
377 "Set to 1 to enable highlighting of hash-quoted strings.");
379 DefineProperty("lexer.cpp.backquoted.strings", &OptionsCPP::backQuotedStrings
,
380 "Set to 1 to enable highlighting of back-quoted raw strings .");
382 DefineProperty("lexer.cpp.escape.sequence", &OptionsCPP::escapeSequence
,
383 "Set to 1 to enable highlighting of escape sequences in strings");
385 DefineProperty("fold", &OptionsCPP::fold
);
387 DefineProperty("fold.cpp.syntax.based", &OptionsCPP::foldSyntaxBased
,
388 "Set this property to 0 to disable syntax based folding.");
390 DefineProperty("fold.comment", &OptionsCPP::foldComment
,
391 "This option enables folding multi-line comments and explicit fold points when using the C++ lexer. "
392 "Explicit fold points allows adding extra folding by placing a //{ comment at the start and a //} "
393 "at the end of a section that should fold.");
395 DefineProperty("fold.cpp.comment.multiline", &OptionsCPP::foldCommentMultiline
,
396 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
398 DefineProperty("fold.cpp.comment.explicit", &OptionsCPP::foldCommentExplicit
,
399 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
401 DefineProperty("fold.cpp.explicit.start", &OptionsCPP::foldExplicitStart
,
402 "The string to use for explicit fold start points, replacing the standard //{.");
404 DefineProperty("fold.cpp.explicit.end", &OptionsCPP::foldExplicitEnd
,
405 "The string to use for explicit fold end points, replacing the standard //}.");
407 DefineProperty("fold.cpp.explicit.anywhere", &OptionsCPP::foldExplicitAnywhere
,
408 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
410 DefineProperty("fold.preprocessor", &OptionsCPP::foldPreprocessor
,
411 "This option enables folding preprocessor directives when using the C++ lexer. "
412 "Includes C#'s explicit #region and #endregion folding directives.");
414 DefineProperty("fold.compact", &OptionsCPP::foldCompact
);
416 DefineProperty("fold.at.else", &OptionsCPP::foldAtElse
,
417 "This option enables C++ folding on a \"} else {\" line of an if statement.");
419 DefineWordListSets(cppWordLists
);
423 const char styleSubable
[] = {SCE_C_IDENTIFIER
, SCE_C_COMMENTDOCKEYWORD
, 0};
427 class LexerCPP
: public ILexerWithSubStyles
{
429 CharacterSet setWord
;
430 CharacterSet setNegationOp
;
431 CharacterSet setArithmethicOp
;
432 CharacterSet setRelOp
;
433 CharacterSet setLogicalOp
;
434 CharacterSet setWordStart
;
436 std::vector
<PPDefinition
> ppDefineHistory
;
441 WordList ppDefinitions
;
445 std::string arguments
;
446 SymbolValue(const std::string
&value_
="", const std::string
&arguments_
="") : value(value_
), arguments(arguments_
) {
448 SymbolValue
&operator = (const std::string
&value_
) {
453 bool IsMacro() const {
454 return !arguments
.empty();
457 typedef std::map
<std::string
, SymbolValue
> SymbolTable
;
458 SymbolTable preprocessorDefinitionsStart
;
461 EscapeSequence escapeSeq
;
462 SparseState
<std::string
> rawStringTerminators
;
463 enum { activeFlag
= 0x40 };
464 enum { ssIdentifier
, ssDocKeyword
};
467 explicit LexerCPP(bool caseSensitive_
) :
468 caseSensitive(caseSensitive_
),
469 setWord(CharacterSet::setAlphaNum
, "._", 0x80, true),
470 setNegationOp(CharacterSet::setNone
, "!"),
471 setArithmethicOp(CharacterSet::setNone
, "+-/*%"),
472 setRelOp(CharacterSet::setNone
, "=!<>"),
473 setLogicalOp(CharacterSet::setNone
, "|&"),
474 subStyles(styleSubable
, 0x80, 0x40, activeFlag
) {
476 virtual ~LexerCPP() {
478 void SCI_METHOD
Release() {
481 int SCI_METHOD
Version() const {
484 const char * SCI_METHOD
PropertyNames() {
485 return osCPP
.PropertyNames();
487 int SCI_METHOD
PropertyType(const char *name
) {
488 return osCPP
.PropertyType(name
);
490 const char * SCI_METHOD
DescribeProperty(const char *name
) {
491 return osCPP
.DescribeProperty(name
);
493 int SCI_METHOD
PropertySet(const char *key
, const char *val
);
494 const char * SCI_METHOD
DescribeWordListSets() {
495 return osCPP
.DescribeWordListSets();
497 int SCI_METHOD
WordListSet(int n
, const char *wl
);
498 void SCI_METHOD
Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
499 void SCI_METHOD
Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
501 void * SCI_METHOD
PrivateCall(int, void *) {
505 int SCI_METHOD
LineEndTypesSupported() {
506 return SC_LINE_END_TYPE_UNICODE
;
509 int SCI_METHOD
AllocateSubStyles(int styleBase
, int numberStyles
) {
510 return subStyles
.Allocate(styleBase
, numberStyles
);
512 int SCI_METHOD
SubStylesStart(int styleBase
) {
513 return subStyles
.Start(styleBase
);
515 int SCI_METHOD
SubStylesLength(int styleBase
) {
516 return subStyles
.Length(styleBase
);
518 int SCI_METHOD
StyleFromSubStyle(int subStyle
) {
519 int styleBase
= subStyles
.BaseStyle(MaskActive(subStyle
));
520 int active
= subStyle
& activeFlag
;
521 return styleBase
| active
;
523 int SCI_METHOD
PrimaryStyleFromStyle(int style
) {
524 return MaskActive(style
);
526 void SCI_METHOD
FreeSubStyles() {
529 void SCI_METHOD
SetIdentifiers(int style
, const char *identifiers
) {
530 subStyles
.SetIdentifiers(style
, identifiers
);
532 int SCI_METHOD
DistanceToSecondaryStyles() {
535 const char * SCI_METHOD
GetSubStyleBases() {
539 static ILexer
*LexerFactoryCPP() {
540 return new LexerCPP(true);
542 static ILexer
*LexerFactoryCPPInsensitive() {
543 return new LexerCPP(false);
545 static int MaskActive(int style
) {
546 return style
& ~activeFlag
;
548 void EvaluateTokens(std::vector
<std::string
> &tokens
, const SymbolTable
&preprocessorDefinitions
);
549 std::vector
<std::string
> Tokenize(const std::string
&expr
) const;
550 bool EvaluateExpression(const std::string
&expr
, const SymbolTable
&preprocessorDefinitions
);
553 int SCI_METHOD
LexerCPP::PropertySet(const char *key
, const char *val
) {
554 if (osCPP
.PropertySet(&options
, key
, val
)) {
555 if (strcmp(key
, "lexer.cpp.allow.dollars") == 0) {
556 setWord
= CharacterSet(CharacterSet::setAlphaNum
, "._", 0x80, true);
557 if (options
.identifiersAllowDollars
) {
566 int SCI_METHOD
LexerCPP::WordListSet(int n
, const char *wl
) {
567 WordList
*wordListN
= 0;
570 wordListN
= &keywords
;
573 wordListN
= &keywords2
;
576 wordListN
= &keywords3
;
579 wordListN
= &keywords4
;
582 wordListN
= &ppDefinitions
;
585 wordListN
= &markerList
;
588 int firstModification
= -1;
592 if (*wordListN
!= wlNew
) {
594 firstModification
= 0;
596 // Rebuild preprocessorDefinitions
597 preprocessorDefinitionsStart
.clear();
598 for (int nDefinition
= 0; nDefinition
< ppDefinitions
.Length(); nDefinition
++) {
599 const char *cpDefinition
= ppDefinitions
.WordAt(nDefinition
);
600 const char *cpEquals
= strchr(cpDefinition
, '=');
602 std::string
name(cpDefinition
, cpEquals
- cpDefinition
);
603 std::string
val(cpEquals
+1);
604 size_t bracket
= name
.find('(');
605 size_t bracketEnd
= name
.find(')');
606 if ((bracket
!= std::string::npos
) && (bracketEnd
!= std::string::npos
)) {
608 std::string args
= name
.substr(bracket
+ 1, bracketEnd
- bracket
- 1);
609 name
= name
.substr(0, bracket
);
610 preprocessorDefinitionsStart
[name
] = SymbolValue(val
, args
);
612 preprocessorDefinitionsStart
[name
] = val
;
615 std::string
name(cpDefinition
);
616 std::string
val("1");
617 preprocessorDefinitionsStart
[name
] = val
;
623 return firstModification
;
626 // Functor used to truncate history
629 explicit After(int line_
) : line(line_
) {}
630 bool operator()(PPDefinition
&p
) const {
631 return p
.line
> line
;
635 void SCI_METHOD
LexerCPP::Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
636 LexAccessor
styler(pAccess
);
638 CharacterSet
setOKBeforeRE(CharacterSet::setNone
, "([{=,:;!%^&*|?~+-");
639 CharacterSet
setCouldBePostOp(CharacterSet::setNone
, "+-");
641 CharacterSet
setDoxygen(CharacterSet::setAlpha
, "$@\\&<>#{}[]");
643 setWordStart
= CharacterSet(CharacterSet::setAlpha
, "_", 0x80, true);
645 CharacterSet
setInvalidRawFirst(CharacterSet::setNone
, " )\\\t\v\f\n");
647 if (options
.identifiersAllowDollars
) {
648 setWordStart
.Add('$');
651 int chPrevNonWhite
= ' ';
652 int visibleChars
= 0;
653 bool lastWordWasUUID
= false;
654 int styleBeforeDCKeyword
= SCE_C_DEFAULT
;
655 int styleBeforeTaskMarker
= SCE_C_DEFAULT
;
656 bool continuationLine
= false;
657 bool isIncludePreprocessor
= false;
658 bool isStringInPreprocessor
= false;
659 bool inRERange
= false;
660 bool seenDocKeyBrace
= false;
662 int lineCurrent
= styler
.GetLine(startPos
);
663 if ((MaskActive(initStyle
) == SCE_C_PREPROCESSOR
) ||
664 (MaskActive(initStyle
) == SCE_C_COMMENTLINE
) ||
665 (MaskActive(initStyle
) == SCE_C_COMMENTLINEDOC
)) {
666 // Set continuationLine if last character of previous line is '\'
667 if (lineCurrent
> 0) {
668 int endLinePrevious
= styler
.LineEnd(lineCurrent
- 1);
669 if (endLinePrevious
> 0) {
670 continuationLine
= styler
.SafeGetCharAt(endLinePrevious
-1) == '\\';
675 // look back to set chPrevNonWhite properly for better regex colouring
678 while (--back
&& IsSpaceEquiv(MaskActive(styler
.StyleAt(back
))))
680 if (MaskActive(styler
.StyleAt(back
)) == SCE_C_OPERATOR
) {
681 chPrevNonWhite
= styler
.SafeGetCharAt(back
);
685 StyleContext
sc(startPos
, length
, initStyle
, styler
, static_cast<unsigned char>(0xff));
686 LinePPState preproc
= vlls
.ForLine(lineCurrent
);
688 bool definitionsChanged
= false;
690 // Truncate ppDefineHistory before current line
692 if (!options
.updatePreprocessor
)
693 ppDefineHistory
.clear();
695 std::vector
<PPDefinition
>::iterator itInvalid
= std::find_if(ppDefineHistory
.begin(), ppDefineHistory
.end(), After(lineCurrent
-1));
696 if (itInvalid
!= ppDefineHistory
.end()) {
697 ppDefineHistory
.erase(itInvalid
, ppDefineHistory
.end());
698 definitionsChanged
= true;
701 SymbolTable preprocessorDefinitions
= preprocessorDefinitionsStart
;
702 for (std::vector
<PPDefinition
>::iterator itDef
= ppDefineHistory
.begin(); itDef
!= ppDefineHistory
.end(); ++itDef
) {
704 preprocessorDefinitions
.erase(itDef
->key
);
706 preprocessorDefinitions
[itDef
->key
] = SymbolValue(itDef
->value
, itDef
->arguments
);
709 std::string rawStringTerminator
= rawStringTerminators
.ValueAt(lineCurrent
-1);
710 SparseState
<std::string
> rawSTNew(lineCurrent
);
712 int activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
714 const WordClassifier
&classifierIdentifiers
= subStyles
.Classifier(SCE_C_IDENTIFIER
);
715 const WordClassifier
&classifierDocKeyWords
= subStyles
.Classifier(SCE_C_COMMENTDOCKEYWORD
);
717 int lineEndNext
= styler
.LineEnd(lineCurrent
);
721 if (sc
.atLineStart
) {
722 // Using MaskActive() is not needed in the following statement.
723 // Inside inactive preprocessor declaration, state will be reset anyway at the end of this block.
724 if ((sc
.state
== SCE_C_STRING
) || (sc
.state
== SCE_C_CHARACTER
)) {
725 // Prevent SCE_C_STRINGEOL from leaking back to previous line which
726 // ends with a line continuation by locking in the state up to this position.
727 sc
.SetState(sc
.state
);
729 if ((MaskActive(sc
.state
) == SCE_C_PREPROCESSOR
) && (!continuationLine
)) {
730 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
732 // Reset states to beginning of colourise so no surprises
733 // if different sets of lines lexed.
735 lastWordWasUUID
= false;
736 isIncludePreprocessor
= false;
738 if (preproc
.IsInactive()) {
739 activitySet
= activeFlag
;
740 sc
.SetState(sc
.state
| activitySet
);
746 lineEndNext
= styler
.LineEnd(lineCurrent
);
747 vlls
.Add(lineCurrent
, preproc
);
748 if (rawStringTerminator
!= "") {
749 rawSTNew
.Set(lineCurrent
-1, rawStringTerminator
);
753 // Handle line continuation generically.
755 if (static_cast<int>((sc
.currentPos
+1)) >= lineEndNext
) {
757 lineEndNext
= styler
.LineEnd(lineCurrent
);
758 vlls
.Add(lineCurrent
, preproc
);
760 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
761 // Even in UTF-8, \r and \n are separate
764 continuationLine
= true;
770 const bool atLineEndBeforeSwitch
= sc
.atLineEnd
;
772 // Determine if the current state should terminate.
773 switch (MaskActive(sc
.state
)) {
775 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
778 // We accept almost anything because of hex. and number suffixes
780 sc
.ChangeState(SCE_C_USERLITERAL
|activitySet
);
781 } else if (!(setWord
.Contains(sc
.ch
)
783 || ((sc
.ch
== '+' || sc
.ch
== '-') && (sc
.chPrev
== 'e' || sc
.chPrev
== 'E' ||
784 sc
.chPrev
== 'p' || sc
.chPrev
== 'P')))) {
785 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
788 case SCE_C_USERLITERAL
:
789 if (!(setWord
.Contains(sc
.ch
)))
790 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
792 case SCE_C_IDENTIFIER
:
793 if (sc
.atLineStart
|| sc
.atLineEnd
|| !setWord
.Contains(sc
.ch
) || (sc
.ch
== '.')) {
796 sc
.GetCurrent(s
, sizeof(s
));
798 sc
.GetCurrentLowered(s
, sizeof(s
));
800 if (keywords
.InList(s
)) {
801 lastWordWasUUID
= strcmp(s
, "uuid") == 0;
802 sc
.ChangeState(SCE_C_WORD
|activitySet
);
803 } else if (keywords2
.InList(s
)) {
804 sc
.ChangeState(SCE_C_WORD2
|activitySet
);
805 } else if (keywords4
.InList(s
)) {
806 sc
.ChangeState(SCE_C_GLOBALCLASS
|activitySet
);
808 int subStyle
= classifierIdentifiers
.ValueFor(s
);
810 sc
.ChangeState(subStyle
|activitySet
);
813 const bool literalString
= sc
.ch
== '\"';
814 if (literalString
|| sc
.ch
== '\'') {
815 size_t lenS
= strlen(s
);
816 const bool raw
= literalString
&& sc
.chPrev
== 'R' && !setInvalidRawFirst
.Contains(sc
.chNext
);
821 ((lenS
== 1) && ((s
[0] == 'L') || (s
[0] == 'u') || (s
[0] == 'U'))) ||
822 ((lenS
== 2) && literalString
&& (s
[0] == 'u') && (s
[1] == '8'));
826 // Set the style of the string prefix to SCE_C_STRINGRAW but then change to
827 // SCE_C_DEFAULT as that allows the raw string start code to run.
828 sc
.ChangeState(SCE_C_STRINGRAW
|activitySet
);
829 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
831 sc
.ChangeState(SCE_C_STRING
|activitySet
);
834 sc
.ChangeState(SCE_C_CHARACTER
|activitySet
);
837 sc
.SetState(SCE_C_DEFAULT
| activitySet
);
840 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
844 case SCE_C_PREPROCESSOR
:
845 if (options
.stylingWithinPreprocessor
) {
846 if (IsASpace(sc
.ch
)) {
847 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
849 } else if (isStringInPreprocessor
&& (sc
.Match('>') || sc
.Match('\"') || sc
.atLineEnd
)) {
850 isStringInPreprocessor
= false;
851 } else if (!isStringInPreprocessor
) {
852 if ((isIncludePreprocessor
&& sc
.Match('<')) || sc
.Match('\"')) {
853 isStringInPreprocessor
= true;
854 } else if (sc
.Match('/', '*')) {
855 if (sc
.Match("/**") || sc
.Match("/*!")) {
856 sc
.SetState(SCE_C_PREPROCESSORCOMMENTDOC
|activitySet
);
858 sc
.SetState(SCE_C_PREPROCESSORCOMMENT
|activitySet
);
860 sc
.Forward(); // Eat the *
861 } else if (sc
.Match('/', '/')) {
862 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
866 case SCE_C_PREPROCESSORCOMMENT
:
867 case SCE_C_PREPROCESSORCOMMENTDOC
:
868 if (sc
.Match('*', '/')) {
870 sc
.ForwardSetState(SCE_C_PREPROCESSOR
|activitySet
);
871 continue; // Without advancing in case of '\'.
875 if (sc
.Match('*', '/')) {
877 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
879 styleBeforeTaskMarker
= SCE_C_COMMENT
;
880 highlightTaskMarker(sc
, styler
, activitySet
, markerList
, caseSensitive
);
883 case SCE_C_COMMENTDOC
:
884 if (sc
.Match('*', '/')) {
886 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
887 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
888 // Verify that we have the conditions to mark a comment-doc-keyword
889 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
890 styleBeforeDCKeyword
= SCE_C_COMMENTDOC
;
891 sc
.SetState(SCE_C_COMMENTDOCKEYWORD
|activitySet
);
895 case SCE_C_COMMENTLINE
:
896 if (sc
.atLineStart
&& !continuationLine
) {
897 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
899 styleBeforeTaskMarker
= SCE_C_COMMENTLINE
;
900 highlightTaskMarker(sc
, styler
, activitySet
, markerList
, caseSensitive
);
903 case SCE_C_COMMENTLINEDOC
:
904 if (sc
.atLineStart
&& !continuationLine
) {
905 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
906 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
907 // Verify that we have the conditions to mark a comment-doc-keyword
908 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '/' || sc
.chPrev
== '!') && (!IsASpace(sc
.chNext
))) {
909 styleBeforeDCKeyword
= SCE_C_COMMENTLINEDOC
;
910 sc
.SetState(SCE_C_COMMENTDOCKEYWORD
|activitySet
);
914 case SCE_C_COMMENTDOCKEYWORD
:
915 if ((styleBeforeDCKeyword
== SCE_C_COMMENTDOC
) && sc
.Match('*', '/')) {
916 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
);
918 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
919 seenDocKeyBrace
= false;
920 } else if (sc
.ch
== '[' || sc
.ch
== '{') {
921 seenDocKeyBrace
= true;
922 } else if (!setDoxygen
.Contains(sc
.ch
)
923 && !(seenDocKeyBrace
&& (sc
.ch
== ',' || sc
.ch
== '.'))) {
926 sc
.GetCurrent(s
, sizeof(s
));
928 sc
.GetCurrentLowered(s
, sizeof(s
));
930 if (!(IsASpace(sc
.ch
) || (sc
.ch
== 0))) {
931 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
|activitySet
);
932 } else if (!keywords3
.InList(s
+ 1)) {
933 int subStyleCDKW
= classifierDocKeyWords
.ValueFor(s
+1);
934 if (subStyleCDKW
>= 0) {
935 sc
.ChangeState(subStyleCDKW
|activitySet
);
937 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
|activitySet
);
940 sc
.SetState(styleBeforeDCKeyword
|activitySet
);
941 seenDocKeyBrace
= false;
946 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
947 } else if (isIncludePreprocessor
) {
949 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
950 isIncludePreprocessor
= false;
952 } else if (sc
.ch
== '\\') {
953 if (options
.escapeSequence
) {
954 sc
.SetState(SCE_C_ESCAPESEQUENCE
|activitySet
);
955 escapeSeq
.resetEscapeState(sc
.chNext
);
957 sc
.Forward(); // Skip all characters after the backslash
958 } else if (sc
.ch
== '\"') {
959 if (sc
.chNext
== '_') {
960 sc
.ChangeState(SCE_C_USERLITERAL
|activitySet
);
962 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
966 case SCE_C_ESCAPESEQUENCE
:
967 escapeSeq
.digitsLeft
--;
968 if (!escapeSeq
.atEscapeEnd(sc
.ch
)) {
972 sc
.SetState(SCE_C_STRING
|activitySet
);
973 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
974 } else if (sc
.ch
== '\\') {
975 escapeSeq
.resetEscapeState(sc
.chNext
);
978 sc
.SetState(SCE_C_STRING
|activitySet
);
980 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
984 case SCE_C_HASHQUOTEDSTRING
:
986 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
989 } else if (sc
.ch
== '\"') {
990 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
993 case SCE_C_STRINGRAW
:
994 if (sc
.Match(rawStringTerminator
.c_str())) {
995 for (size_t termPos
=rawStringTerminator
.size(); termPos
; termPos
--)
997 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
998 rawStringTerminator
= "";
1001 case SCE_C_CHARACTER
:
1003 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
1004 } else if (sc
.ch
== '\\') {
1005 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
1008 } else if (sc
.ch
== '\'') {
1009 if (sc
.chNext
== '_') {
1010 sc
.ChangeState(SCE_C_USERLITERAL
|activitySet
);
1012 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
1017 if (sc
.atLineStart
) {
1018 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1019 } else if (! inRERange
&& sc
.ch
== '/') {
1021 while ((sc
.ch
< 0x80) && islower(sc
.ch
))
1022 sc
.Forward(); // gobble regex flags
1023 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1024 } else if (sc
.ch
== '\\' && (static_cast<int>(sc
.currentPos
+1) < lineEndNext
)) {
1025 // Gobble up the escaped character
1027 } else if (sc
.ch
== '[') {
1029 } else if (sc
.ch
== ']') {
1033 case SCE_C_STRINGEOL
:
1034 if (sc
.atLineStart
) {
1035 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1038 case SCE_C_VERBATIM
:
1039 if (sc
.ch
== '\"') {
1040 if (sc
.chNext
== '\"') {
1043 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
1047 case SCE_C_TRIPLEVERBATIM
:
1048 if (sc
.Match("\"\"\"")) {
1049 while (sc
.Match('"')) {
1052 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1056 if (sc
.atLineEnd
|| sc
.ch
== ')') {
1057 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1060 case SCE_C_TASKMARKER
:
1061 if (isoperator(sc
.ch
) || IsASpace(sc
.ch
)) {
1062 sc
.SetState(styleBeforeTaskMarker
|activitySet
);
1063 styleBeforeTaskMarker
= SCE_C_DEFAULT
;
1067 if (sc
.atLineEnd
&& !atLineEndBeforeSwitch
) {
1068 // State exit processing consumed characters up to end of line.
1070 lineEndNext
= styler
.LineEnd(lineCurrent
);
1071 vlls
.Add(lineCurrent
, preproc
);
1074 // Determine if a new state should be entered.
1075 if (MaskActive(sc
.state
) == SCE_C_DEFAULT
) {
1076 if (sc
.Match('@', '\"')) {
1077 sc
.SetState(SCE_C_VERBATIM
|activitySet
);
1079 } else if (options
.triplequotedStrings
&& sc
.Match("\"\"\"")) {
1080 sc
.SetState(SCE_C_TRIPLEVERBATIM
|activitySet
);
1082 } else if (options
.hashquotedStrings
&& sc
.Match('#', '\"')) {
1083 sc
.SetState(SCE_C_HASHQUOTEDSTRING
|activitySet
);
1085 } else if (options
.backQuotedStrings
&& sc
.Match('`')) {
1086 sc
.SetState(SCE_C_STRINGRAW
|activitySet
);
1087 rawStringTerminator
= "`";
1089 } else if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
1090 if (lastWordWasUUID
) {
1091 sc
.SetState(SCE_C_UUID
|activitySet
);
1092 lastWordWasUUID
= false;
1094 sc
.SetState(SCE_C_NUMBER
|activitySet
);
1096 } else if (!sc
.atLineEnd
&& (setWordStart
.Contains(sc
.ch
) || (sc
.ch
== '@'))) {
1097 if (lastWordWasUUID
) {
1098 sc
.SetState(SCE_C_UUID
|activitySet
);
1099 lastWordWasUUID
= false;
1101 sc
.SetState(SCE_C_IDENTIFIER
|activitySet
);
1103 } else if (sc
.Match('/', '*')) {
1104 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Qt/Doxygen doc. style
1105 sc
.SetState(SCE_C_COMMENTDOC
|activitySet
);
1107 sc
.SetState(SCE_C_COMMENT
|activitySet
);
1109 sc
.Forward(); // Eat the * so it isn't used for the end of the comment
1110 } else if (sc
.Match('/', '/')) {
1111 if ((sc
.Match("///") && !sc
.Match("////")) || sc
.Match("//!"))
1112 // Support of Qt/Doxygen doc. style
1113 sc
.SetState(SCE_C_COMMENTLINEDOC
|activitySet
);
1115 sc
.SetState(SCE_C_COMMENTLINE
|activitySet
);
1116 } else if (sc
.ch
== '/'
1117 && (setOKBeforeRE
.Contains(chPrevNonWhite
)
1118 || followsReturnKeyword(sc
, styler
))
1119 && (!setCouldBePostOp
.Contains(chPrevNonWhite
)
1120 || !FollowsPostfixOperator(sc
, styler
))) {
1121 sc
.SetState(SCE_C_REGEX
|activitySet
); // JavaScript's RegEx
1123 } else if (sc
.ch
== '\"') {
1124 if (sc
.chPrev
== 'R') {
1126 if (MaskActive(styler
.StyleAt(sc
.currentPos
- 1)) == SCE_C_STRINGRAW
) {
1127 sc
.SetState(SCE_C_STRINGRAW
|activitySet
);
1128 rawStringTerminator
= ")";
1129 for (int termPos
= sc
.currentPos
+ 1;; termPos
++) {
1130 char chTerminator
= styler
.SafeGetCharAt(termPos
, '(');
1131 if (chTerminator
== '(')
1133 rawStringTerminator
+= chTerminator
;
1135 rawStringTerminator
+= '\"';
1137 sc
.SetState(SCE_C_STRING
|activitySet
);
1140 sc
.SetState(SCE_C_STRING
|activitySet
);
1142 isIncludePreprocessor
= false; // ensure that '>' won't end the string
1143 } else if (isIncludePreprocessor
&& sc
.ch
== '<') {
1144 sc
.SetState(SCE_C_STRING
|activitySet
);
1145 } else if (sc
.ch
== '\'') {
1146 sc
.SetState(SCE_C_CHARACTER
|activitySet
);
1147 } else if (sc
.ch
== '#' && visibleChars
== 0) {
1148 // Preprocessor commands are alone on their line
1149 sc
.SetState(SCE_C_PREPROCESSOR
|activitySet
);
1150 // Skip whitespace between # and preprocessor word
1153 } while ((sc
.ch
== ' ' || sc
.ch
== '\t') && sc
.More());
1155 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1156 } else if (sc
.Match("include")) {
1157 isIncludePreprocessor
= true;
1159 if (options
.trackPreprocessor
) {
1160 if (sc
.Match("ifdef") || sc
.Match("ifndef")) {
1161 bool isIfDef
= sc
.Match("ifdef");
1162 int i
= isIfDef
? 5 : 6;
1163 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ i
+ 1, false);
1164 bool foundDef
= preprocessorDefinitions
.find(restOfLine
) != preprocessorDefinitions
.end();
1165 preproc
.StartSection(isIfDef
== foundDef
);
1166 } else if (sc
.Match("if")) {
1167 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 2, true);
1168 bool ifGood
= EvaluateExpression(restOfLine
, preprocessorDefinitions
);
1169 preproc
.StartSection(ifGood
);
1170 } else if (sc
.Match("else")) {
1171 if (!preproc
.CurrentIfTaken()) {
1172 preproc
.InvertCurrentLevel();
1173 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1175 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1176 } else if (!preproc
.IsInactive()) {
1177 preproc
.InvertCurrentLevel();
1178 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1180 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1182 } else if (sc
.Match("elif")) {
1183 // Ensure only one chosen out of #if .. #elif .. #elif .. #else .. #endif
1184 if (!preproc
.CurrentIfTaken()) {
1186 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 2, true);
1187 bool ifGood
= EvaluateExpression(restOfLine
, preprocessorDefinitions
);
1189 preproc
.InvertCurrentLevel();
1190 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1192 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1194 } else if (!preproc
.IsInactive()) {
1195 preproc
.InvertCurrentLevel();
1196 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1198 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1200 } else if (sc
.Match("endif")) {
1201 preproc
.EndSection();
1202 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1203 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1204 } else if (sc
.Match("define")) {
1205 if (options
.updatePreprocessor
&& !preproc
.IsInactive()) {
1206 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 6, true);
1207 size_t startName
= 0;
1208 while ((startName
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[startName
]))
1210 size_t endName
= startName
;
1211 while ((endName
< restOfLine
.length()) && setWord
.Contains(static_cast<unsigned char>(restOfLine
[endName
])))
1213 std::string key
= restOfLine
.substr(startName
, endName
-startName
);
1214 if ((endName
< restOfLine
.length()) && (restOfLine
.at(endName
) == '(')) {
1216 size_t endArgs
= endName
;
1217 while ((endArgs
< restOfLine
.length()) && (restOfLine
[endArgs
] != ')'))
1219 std::string args
= restOfLine
.substr(endName
+ 1, endArgs
- endName
- 1);
1220 size_t startValue
= endArgs
+1;
1221 while ((startValue
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[startValue
]))
1224 if (startValue
< restOfLine
.length())
1225 value
= restOfLine
.substr(startValue
);
1226 preprocessorDefinitions
[key
] = SymbolValue(value
, args
);
1227 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, value
, false, args
));
1228 definitionsChanged
= true;
1231 size_t startValue
= endName
;
1232 while ((startValue
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[startValue
]))
1234 std::string value
= restOfLine
.substr(startValue
);
1235 preprocessorDefinitions
[key
] = value
;
1236 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, value
));
1237 definitionsChanged
= true;
1240 } else if (sc
.Match("undef")) {
1241 if (options
.updatePreprocessor
&& !preproc
.IsInactive()) {
1242 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 5, true);
1243 std::vector
<std::string
> tokens
= Tokenize(restOfLine
);
1245 if (tokens
.size() >= 1) {
1247 preprocessorDefinitions
.erase(key
);
1248 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, "", true));
1249 definitionsChanged
= true;
1255 } else if (isoperator(sc
.ch
)) {
1256 sc
.SetState(SCE_C_OPERATOR
|activitySet
);
1260 if (!IsASpace(sc
.ch
) && !IsSpaceEquiv(MaskActive(sc
.state
))) {
1261 chPrevNonWhite
= sc
.ch
;
1264 continuationLine
= false;
1267 const bool rawStringsChanged
= rawStringTerminators
.Merge(rawSTNew
, lineCurrent
);
1268 if (definitionsChanged
|| rawStringsChanged
)
1269 styler
.ChangeLexerState(startPos
, startPos
+ length
);
1273 // Store both the current line's fold level and the next lines in the
1274 // level store to make it easy to pick up with each increment
1275 // and to make it possible to fiddle the current level for "} else {".
1277 void SCI_METHOD
LexerCPP::Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
1282 LexAccessor
styler(pAccess
);
1284 unsigned int endPos
= startPos
+ length
;
1285 int visibleChars
= 0;
1286 bool inLineComment
= false;
1287 int lineCurrent
= styler
.GetLine(startPos
);
1288 int levelCurrent
= SC_FOLDLEVELBASE
;
1289 if (lineCurrent
> 0)
1290 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
1291 unsigned int lineStartNext
= styler
.LineStart(lineCurrent
+1);
1292 int levelMinCurrent
= levelCurrent
;
1293 int levelNext
= levelCurrent
;
1294 char chNext
= styler
[startPos
];
1295 int styleNext
= MaskActive(styler
.StyleAt(startPos
));
1296 int style
= MaskActive(initStyle
);
1297 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
1298 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
1300 chNext
= styler
.SafeGetCharAt(i
+ 1);
1301 int stylePrev
= style
;
1303 styleNext
= MaskActive(styler
.StyleAt(i
+ 1));
1304 bool atEOL
= i
== (lineStartNext
-1);
1305 if ((style
== SCE_C_COMMENTLINE
) || (style
== SCE_C_COMMENTLINEDOC
))
1306 inLineComment
= true;
1307 if (options
.foldComment
&& options
.foldCommentMultiline
&& IsStreamCommentStyle(style
) && !inLineComment
) {
1308 if (!IsStreamCommentStyle(stylePrev
)) {
1310 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
1311 // Comments don't end at end of line and the next character may be unstyled.
1315 if (options
.foldComment
&& options
.foldCommentExplicit
&& ((style
== SCE_C_COMMENTLINE
) || options
.foldExplicitAnywhere
)) {
1316 if (userDefinedFoldMarkers
) {
1317 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
1319 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
1323 if ((ch
== '/') && (chNext
== '/')) {
1324 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
1325 if (chNext2
== '{') {
1327 } else if (chNext2
== '}') {
1333 if (options
.foldPreprocessor
&& (style
== SCE_C_PREPROCESSOR
)) {
1335 unsigned int j
= i
+ 1;
1336 while ((j
< endPos
) && IsASpaceOrTab(styler
.SafeGetCharAt(j
))) {
1339 if (styler
.Match(j
, "region") || styler
.Match(j
, "if")) {
1341 } else if (styler
.Match(j
, "end")) {
1346 if (options
.foldSyntaxBased
&& (style
== SCE_C_OPERATOR
)) {
1348 // Measure the minimum before a '{' to allow
1349 // folding on "} else {"
1350 if (levelMinCurrent
> levelNext
) {
1351 levelMinCurrent
= levelNext
;
1354 } else if (ch
== '}') {
1360 if (atEOL
|| (i
== endPos
-1)) {
1361 int levelUse
= levelCurrent
;
1362 if (options
.foldSyntaxBased
&& options
.foldAtElse
) {
1363 levelUse
= levelMinCurrent
;
1365 int lev
= levelUse
| levelNext
<< 16;
1366 if (visibleChars
== 0 && options
.foldCompact
)
1367 lev
|= SC_FOLDLEVELWHITEFLAG
;
1368 if (levelUse
< levelNext
)
1369 lev
|= SC_FOLDLEVELHEADERFLAG
;
1370 if (lev
!= styler
.LevelAt(lineCurrent
)) {
1371 styler
.SetLevel(lineCurrent
, lev
);
1374 lineStartNext
= styler
.LineStart(lineCurrent
+1);
1375 levelCurrent
= levelNext
;
1376 levelMinCurrent
= levelCurrent
;
1377 if (atEOL
&& (i
== static_cast<unsigned int>(styler
.Length()-1))) {
1378 // There is an empty line at end of file so give it same level and empty
1379 styler
.SetLevel(lineCurrent
, (levelCurrent
| levelCurrent
<< 16) | SC_FOLDLEVELWHITEFLAG
);
1382 inLineComment
= false;
1387 void LexerCPP::EvaluateTokens(std::vector
<std::string
> &tokens
, const SymbolTable
&preprocessorDefinitions
) {
1389 // Remove whitespace tokens
1390 tokens
.erase(std::remove_if(tokens
.begin(), tokens
.end(), OnlySpaceOrTab
), tokens
.end());
1392 // Evaluate defined statements to either 0 or 1
1393 for (size_t i
=0; (i
+1)<tokens
.size();) {
1394 if (tokens
[i
] == "defined") {
1395 const char *val
= "0";
1396 if (tokens
[i
+1] == "(") {
1397 if (((i
+ 2)<tokens
.size()) && (tokens
[i
+ 2] == ")")) {
1399 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 3);
1400 } else if (((i
+3)<tokens
.size()) && (tokens
[i
+3] == ")")) {
1401 // defined(<identifier>)
1402 SymbolTable::const_iterator it
= preprocessorDefinitions
.find(tokens
[i
+2]);
1403 if (it
!= preprocessorDefinitions
.end()) {
1406 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 4);
1408 // Spurious '(' so erase as more likely to result in false
1409 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 2);
1412 // defined <identifier>
1413 SymbolTable::const_iterator it
= preprocessorDefinitions
.find(tokens
[i
+1]);
1414 if (it
!= preprocessorDefinitions
.end()) {
1424 // Evaluate identifiers
1425 const size_t maxIterations
= 100;
1426 size_t iterations
= 0; // Limit number of iterations in case there is a recursive macro.
1427 for (size_t i
= 0; (i
<tokens
.size()) && (iterations
< maxIterations
);) {
1429 if (setWordStart
.Contains(static_cast<unsigned char>(tokens
[i
][0]))) {
1430 SymbolTable::const_iterator it
= preprocessorDefinitions
.find(tokens
[i
]);
1431 if (it
!= preprocessorDefinitions
.end()) {
1433 std::vector
<std::string
> macroTokens
= Tokenize(it
->second
.value
);
1434 if (it
->second
.IsMacro()) {
1435 if ((i
+ 1 < tokens
.size()) && (tokens
.at(i
+ 1) == "(")) {
1436 // Create map of argument name to value
1437 std::vector
<std::string
> argumentNames
= StringSplit(it
->second
.arguments
, ',');
1438 std::map
<std::string
, std::string
> arguments
;
1441 while ((tok
< tokens
.size()) && (arg
< argumentNames
.size()) && (tokens
.at(tok
) != ")")) {
1442 if (tokens
.at(tok
) != ",") {
1443 arguments
[argumentNames
.at(arg
)] = tokens
.at(tok
);
1449 // Remove invocation
1450 tokens
.erase(tokens
.begin() + i
, tokens
.begin() + tok
+ 1);
1452 // Substitute values into macro
1453 macroTokens
.erase(std::remove_if(macroTokens
.begin(), macroTokens
.end(), OnlySpaceOrTab
), macroTokens
.end());
1455 for (size_t iMacro
= 0; iMacro
< macroTokens
.size();) {
1456 if (setWordStart
.Contains(static_cast<unsigned char>(macroTokens
[iMacro
][0]))) {
1457 std::map
<std::string
, std::string
>::const_iterator itFind
= arguments
.find(macroTokens
[iMacro
]);
1458 if (itFind
!= arguments
.end()) {
1459 // TODO: Possible that value will be expression so should insert tokenized form
1460 macroTokens
[iMacro
] = itFind
->second
;
1466 // Insert results back into tokens
1467 tokens
.insert(tokens
.begin() + i
, macroTokens
.begin(), macroTokens
.end());
1473 // Remove invocation
1474 tokens
.erase(tokens
.begin() + i
);
1475 // Insert results back into tokens
1476 tokens
.insert(tokens
.begin() + i
, macroTokens
.begin(), macroTokens
.end());
1479 // Identifier not found
1480 tokens
.erase(tokens
.begin() + i
);
1487 // Find bracketed subexpressions and recurse on them
1488 BracketPair bracketPair
= FindBracketPair(tokens
);
1489 while (bracketPair
.itBracket
!= tokens
.end()) {
1490 std::vector
<std::string
> inBracket(bracketPair
.itBracket
+ 1, bracketPair
.itEndBracket
);
1491 EvaluateTokens(inBracket
, preprocessorDefinitions
);
1493 // The insertion is done before the removal because there were failures with the opposite approach
1494 tokens
.insert(bracketPair
.itBracket
, inBracket
.begin(), inBracket
.end());
1496 bracketPair
= FindBracketPair(tokens
);
1497 tokens
.erase(bracketPair
.itBracket
, bracketPair
.itEndBracket
+ 1);
1499 bracketPair
= FindBracketPair(tokens
);
1502 // Evaluate logical negations
1503 for (size_t j
=0; (j
+1)<tokens
.size();) {
1504 if (setNegationOp
.Contains(tokens
[j
][0])) {
1505 int isTrue
= atoi(tokens
[j
+1].c_str());
1506 if (tokens
[j
] == "!")
1508 std::vector
<std::string
>::iterator itInsert
=
1509 tokens
.erase(tokens
.begin() + j
, tokens
.begin() + j
+ 2);
1510 tokens
.insert(itInsert
, isTrue
? "1" : "0");
1516 // Evaluate expressions in precedence order
1517 enum precedence
{ precArithmetic
, precRelative
, precLogical
};
1518 for (int prec
=precArithmetic
; prec
<= precLogical
; prec
++) {
1519 // Looking at 3 tokens at a time so end at 2 before end
1520 for (size_t k
=0; (k
+2)<tokens
.size();) {
1521 char chOp
= tokens
[k
+1][0];
1523 ((prec
==precArithmetic
) && setArithmethicOp
.Contains(chOp
)) ||
1524 ((prec
==precRelative
) && setRelOp
.Contains(chOp
)) ||
1525 ((prec
==precLogical
) && setLogicalOp
.Contains(chOp
))
1527 int valA
= atoi(tokens
[k
].c_str());
1528 int valB
= atoi(tokens
[k
+2].c_str());
1530 if (tokens
[k
+1] == "+")
1531 result
= valA
+ valB
;
1532 else if (tokens
[k
+1] == "-")
1533 result
= valA
- valB
;
1534 else if (tokens
[k
+1] == "*")
1535 result
= valA
* valB
;
1536 else if (tokens
[k
+1] == "/")
1537 result
= valA
/ (valB
? valB
: 1);
1538 else if (tokens
[k
+1] == "%")
1539 result
= valA
% (valB
? valB
: 1);
1540 else if (tokens
[k
+1] == "<")
1541 result
= valA
< valB
;
1542 else if (tokens
[k
+1] == "<=")
1543 result
= valA
<= valB
;
1544 else if (tokens
[k
+1] == ">")
1545 result
= valA
> valB
;
1546 else if (tokens
[k
+1] == ">=")
1547 result
= valA
>= valB
;
1548 else if (tokens
[k
+1] == "==")
1549 result
= valA
== valB
;
1550 else if (tokens
[k
+1] == "!=")
1551 result
= valA
!= valB
;
1552 else if (tokens
[k
+1] == "||")
1553 result
= valA
|| valB
;
1554 else if (tokens
[k
+1] == "&&")
1555 result
= valA
&& valB
;
1557 sprintf(sResult
, "%d", result
);
1558 std::vector
<std::string
>::iterator itInsert
=
1559 tokens
.erase(tokens
.begin() + k
, tokens
.begin() + k
+ 3);
1560 tokens
.insert(itInsert
, sResult
);
1568 std::vector
<std::string
> LexerCPP::Tokenize(const std::string
&expr
) const {
1569 // Break into tokens
1570 std::vector
<std::string
> tokens
;
1571 const char *cp
= expr
.c_str();
1574 if (setWord
.Contains(static_cast<unsigned char>(*cp
))) {
1575 // Identifiers and numbers
1576 while (setWord
.Contains(static_cast<unsigned char>(*cp
))) {
1580 } else if (IsSpaceOrTab(*cp
)) {
1581 while (IsSpaceOrTab(*cp
)) {
1585 } else if (setRelOp
.Contains(static_cast<unsigned char>(*cp
))) {
1588 if (setRelOp
.Contains(static_cast<unsigned char>(*cp
))) {
1592 } else if (setLogicalOp
.Contains(static_cast<unsigned char>(*cp
))) {
1595 if (setLogicalOp
.Contains(static_cast<unsigned char>(*cp
))) {
1600 // Should handle strings, characters, and comments here
1604 tokens
.push_back(word
);
1609 bool LexerCPP::EvaluateExpression(const std::string
&expr
, const SymbolTable
&preprocessorDefinitions
) {
1610 std::vector
<std::string
> tokens
= Tokenize(expr
);
1612 EvaluateTokens(tokens
, preprocessorDefinitions
);
1614 // "0" or "" -> false else true
1615 bool isFalse
= tokens
.empty() ||
1616 ((tokens
.size() == 1) && ((tokens
[0] == "") || tokens
[0] == "0"));
1620 LexerModule
lmCPP(SCLEX_CPP
, LexerCPP::LexerFactoryCPP
, "cpp", cppWordLists
);
1621 LexerModule
lmCPPNoCase(SCLEX_CPPNOCASE
, LexerCPP::LexerFactoryCPPInsensitive
, "cppnocase", cppWordLists
);