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 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
;
326 stylingWithinPreprocessor
= false;
327 identifiersAllowDollars
= true;
328 trackPreprocessor
= true;
329 updatePreprocessor
= true;
330 verbatimStringsAllowEscapes
= false;
331 triplequotedStrings
= false;
332 hashquotedStrings
= false;
333 backQuotedStrings
= false;
334 escapeSequence
= false;
336 foldSyntaxBased
= true;
338 foldCommentMultiline
= true;
339 foldCommentExplicit
= true;
340 foldExplicitStart
= "";
341 foldExplicitEnd
= "";
342 foldExplicitAnywhere
= false;
343 foldPreprocessor
= false;
349 const char *const cppWordLists
[] = {
350 "Primary keywords and identifiers",
351 "Secondary keywords and identifiers",
352 "Documentation comment keywords",
353 "Global classes and typedefs",
354 "Preprocessor definitions",
355 "Task marker and error marker keywords",
359 struct OptionSetCPP
: public OptionSet
<OptionsCPP
> {
361 DefineProperty("styling.within.preprocessor", &OptionsCPP::stylingWithinPreprocessor
,
362 "For C++ code, determines whether all preprocessor code is styled in the "
363 "preprocessor style (0, the default) or only from the initial # to the end "
364 "of the command word(1).");
366 DefineProperty("lexer.cpp.allow.dollars", &OptionsCPP::identifiersAllowDollars
,
367 "Set to 0 to disallow the '$' character in identifiers with the cpp lexer.");
369 DefineProperty("lexer.cpp.track.preprocessor", &OptionsCPP::trackPreprocessor
,
370 "Set to 1 to interpret #if/#else/#endif to grey out code that is not active.");
372 DefineProperty("lexer.cpp.update.preprocessor", &OptionsCPP::updatePreprocessor
,
373 "Set to 1 to update preprocessor definitions when #define found.");
375 DefineProperty("lexer.cpp.verbatim.strings.allow.escapes", &OptionsCPP::verbatimStringsAllowEscapes
,
376 "Set to 1 to allow verbatim strings to contain escape sequences.");
378 DefineProperty("lexer.cpp.triplequoted.strings", &OptionsCPP::triplequotedStrings
,
379 "Set to 1 to enable highlighting of triple-quoted strings.");
381 DefineProperty("lexer.cpp.hashquoted.strings", &OptionsCPP::hashquotedStrings
,
382 "Set to 1 to enable highlighting of hash-quoted strings.");
384 DefineProperty("lexer.cpp.backquoted.strings", &OptionsCPP::backQuotedStrings
,
385 "Set to 1 to enable highlighting of back-quoted raw strings .");
387 DefineProperty("lexer.cpp.escape.sequence", &OptionsCPP::escapeSequence
,
388 "Set to 1 to enable highlighting of escape sequences in strings");
390 DefineProperty("fold", &OptionsCPP::fold
);
392 DefineProperty("fold.cpp.syntax.based", &OptionsCPP::foldSyntaxBased
,
393 "Set this property to 0 to disable syntax based folding.");
395 DefineProperty("fold.comment", &OptionsCPP::foldComment
,
396 "This option enables folding multi-line comments and explicit fold points when using the C++ lexer. "
397 "Explicit fold points allows adding extra folding by placing a //{ comment at the start and a //} "
398 "at the end of a section that should fold.");
400 DefineProperty("fold.cpp.comment.multiline", &OptionsCPP::foldCommentMultiline
,
401 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
403 DefineProperty("fold.cpp.comment.explicit", &OptionsCPP::foldCommentExplicit
,
404 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
406 DefineProperty("fold.cpp.explicit.start", &OptionsCPP::foldExplicitStart
,
407 "The string to use for explicit fold start points, replacing the standard //{.");
409 DefineProperty("fold.cpp.explicit.end", &OptionsCPP::foldExplicitEnd
,
410 "The string to use for explicit fold end points, replacing the standard //}.");
412 DefineProperty("fold.cpp.explicit.anywhere", &OptionsCPP::foldExplicitAnywhere
,
413 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
415 DefineProperty("fold.preprocessor", &OptionsCPP::foldPreprocessor
,
416 "This option enables folding preprocessor directives when using the C++ lexer. "
417 "Includes C#'s explicit #region and #endregion folding directives.");
419 DefineProperty("fold.compact", &OptionsCPP::foldCompact
);
421 DefineProperty("fold.at.else", &OptionsCPP::foldAtElse
,
422 "This option enables C++ folding on a \"} else {\" line of an if statement.");
424 DefineWordListSets(cppWordLists
);
428 const char styleSubable
[] = {SCE_C_IDENTIFIER
, SCE_C_COMMENTDOCKEYWORD
, 0};
432 class LexerCPP
: public ILexerWithSubStyles
{
434 CharacterSet setWord
;
435 CharacterSet setNegationOp
;
436 CharacterSet setArithmethicOp
;
437 CharacterSet setRelOp
;
438 CharacterSet setLogicalOp
;
439 CharacterSet setWordStart
;
441 std::vector
<PPDefinition
> ppDefineHistory
;
446 WordList ppDefinitions
;
450 std::string arguments
;
451 SymbolValue(const std::string
&value_
="", const std::string
&arguments_
="") : value(value_
), arguments(arguments_
) {
453 SymbolValue
&operator = (const std::string
&value_
) {
458 bool IsMacro() const {
459 return !arguments
.empty();
462 typedef std::map
<std::string
, SymbolValue
> SymbolTable
;
463 SymbolTable preprocessorDefinitionsStart
;
466 EscapeSequence escapeSeq
;
467 SparseState
<std::string
> rawStringTerminators
;
468 enum { activeFlag
= 0x40 };
469 enum { ssIdentifier
, ssDocKeyword
};
472 explicit LexerCPP(bool caseSensitive_
) :
473 caseSensitive(caseSensitive_
),
474 setWord(CharacterSet::setAlphaNum
, "._", 0x80, true),
475 setNegationOp(CharacterSet::setNone
, "!"),
476 setArithmethicOp(CharacterSet::setNone
, "+-/*%"),
477 setRelOp(CharacterSet::setNone
, "=!<>"),
478 setLogicalOp(CharacterSet::setNone
, "|&"),
479 subStyles(styleSubable
, 0x80, 0x40, activeFlag
) {
481 virtual ~LexerCPP() {
483 void SCI_METHOD
Release() {
486 int SCI_METHOD
Version() const {
489 const char * SCI_METHOD
PropertyNames() {
490 return osCPP
.PropertyNames();
492 int SCI_METHOD
PropertyType(const char *name
) {
493 return osCPP
.PropertyType(name
);
495 const char * SCI_METHOD
DescribeProperty(const char *name
) {
496 return osCPP
.DescribeProperty(name
);
498 int SCI_METHOD
PropertySet(const char *key
, const char *val
);
499 const char * SCI_METHOD
DescribeWordListSets() {
500 return osCPP
.DescribeWordListSets();
502 int SCI_METHOD
WordListSet(int n
, const char *wl
);
503 void SCI_METHOD
Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
504 void SCI_METHOD
Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
506 void * SCI_METHOD
PrivateCall(int, void *) {
510 int SCI_METHOD
LineEndTypesSupported() {
511 return SC_LINE_END_TYPE_UNICODE
;
514 int SCI_METHOD
AllocateSubStyles(int styleBase
, int numberStyles
) {
515 return subStyles
.Allocate(styleBase
, numberStyles
);
517 int SCI_METHOD
SubStylesStart(int styleBase
) {
518 return subStyles
.Start(styleBase
);
520 int SCI_METHOD
SubStylesLength(int styleBase
) {
521 return subStyles
.Length(styleBase
);
523 int SCI_METHOD
StyleFromSubStyle(int subStyle
) {
524 int styleBase
= subStyles
.BaseStyle(MaskActive(subStyle
));
525 int active
= subStyle
& activeFlag
;
526 return styleBase
| active
;
528 int SCI_METHOD
PrimaryStyleFromStyle(int style
) {
529 return MaskActive(style
);
531 void SCI_METHOD
FreeSubStyles() {
534 void SCI_METHOD
SetIdentifiers(int style
, const char *identifiers
) {
535 subStyles
.SetIdentifiers(style
, identifiers
);
537 int SCI_METHOD
DistanceToSecondaryStyles() {
540 const char * SCI_METHOD
GetSubStyleBases() {
544 static ILexer
*LexerFactoryCPP() {
545 return new LexerCPP(true);
547 static ILexer
*LexerFactoryCPPInsensitive() {
548 return new LexerCPP(false);
550 static int MaskActive(int style
) {
551 return style
& ~activeFlag
;
553 void EvaluateTokens(std::vector
<std::string
> &tokens
, const SymbolTable
&preprocessorDefinitions
);
554 std::vector
<std::string
> Tokenize(const std::string
&expr
) const;
555 bool EvaluateExpression(const std::string
&expr
, const SymbolTable
&preprocessorDefinitions
);
558 int SCI_METHOD
LexerCPP::PropertySet(const char *key
, const char *val
) {
559 if (osCPP
.PropertySet(&options
, key
, val
)) {
560 if (strcmp(key
, "lexer.cpp.allow.dollars") == 0) {
561 setWord
= CharacterSet(CharacterSet::setAlphaNum
, "._", 0x80, true);
562 if (options
.identifiersAllowDollars
) {
571 int SCI_METHOD
LexerCPP::WordListSet(int n
, const char *wl
) {
572 WordList
*wordListN
= 0;
575 wordListN
= &keywords
;
578 wordListN
= &keywords2
;
581 wordListN
= &keywords3
;
584 wordListN
= &keywords4
;
587 wordListN
= &ppDefinitions
;
590 wordListN
= &markerList
;
593 int firstModification
= -1;
597 if (*wordListN
!= wlNew
) {
599 firstModification
= 0;
601 // Rebuild preprocessorDefinitions
602 preprocessorDefinitionsStart
.clear();
603 for (int nDefinition
= 0; nDefinition
< ppDefinitions
.Length(); nDefinition
++) {
604 const char *cpDefinition
= ppDefinitions
.WordAt(nDefinition
);
605 const char *cpEquals
= strchr(cpDefinition
, '=');
607 std::string
name(cpDefinition
, cpEquals
- cpDefinition
);
608 std::string
val(cpEquals
+1);
609 size_t bracket
= name
.find('(');
610 size_t bracketEnd
= name
.find(')');
611 if ((bracket
!= std::string::npos
) && (bracketEnd
!= std::string::npos
)) {
613 std::string args
= name
.substr(bracket
+ 1, bracketEnd
- bracket
- 1);
614 name
= name
.substr(0, bracket
);
615 preprocessorDefinitionsStart
[name
] = SymbolValue(val
, args
);
617 preprocessorDefinitionsStart
[name
] = val
;
620 std::string
name(cpDefinition
);
621 std::string
val("1");
622 preprocessorDefinitionsStart
[name
] = val
;
628 return firstModification
;
631 // Functor used to truncate history
634 explicit After(int line_
) : line(line_
) {}
635 bool operator()(PPDefinition
&p
) const {
636 return p
.line
> line
;
640 void SCI_METHOD
LexerCPP::Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
641 LexAccessor
styler(pAccess
);
643 CharacterSet
setOKBeforeRE(CharacterSet::setNone
, "([{=,:;!%^&*|?~+-");
644 CharacterSet
setCouldBePostOp(CharacterSet::setNone
, "+-");
646 CharacterSet
setDoxygen(CharacterSet::setAlpha
, "$@\\&<>#{}[]");
648 setWordStart
= CharacterSet(CharacterSet::setAlpha
, "_", 0x80, true);
650 CharacterSet
setInvalidRawFirst(CharacterSet::setNone
, " )\\\t\v\f\n");
652 if (options
.identifiersAllowDollars
) {
653 setWordStart
.Add('$');
656 int chPrevNonWhite
= ' ';
657 int visibleChars
= 0;
658 bool lastWordWasUUID
= false;
659 int styleBeforeDCKeyword
= SCE_C_DEFAULT
;
660 int styleBeforeTaskMarker
= SCE_C_DEFAULT
;
661 bool continuationLine
= false;
662 bool isIncludePreprocessor
= false;
663 bool isStringInPreprocessor
= false;
664 bool inRERange
= false;
665 bool seenDocKeyBrace
= false;
667 int lineCurrent
= styler
.GetLine(startPos
);
668 if ((MaskActive(initStyle
) == SCE_C_PREPROCESSOR
) ||
669 (MaskActive(initStyle
) == SCE_C_COMMENTLINE
) ||
670 (MaskActive(initStyle
) == SCE_C_COMMENTLINEDOC
)) {
671 // Set continuationLine if last character of previous line is '\'
672 if (lineCurrent
> 0) {
673 int endLinePrevious
= styler
.LineEnd(lineCurrent
- 1);
674 if (endLinePrevious
> 0) {
675 continuationLine
= styler
.SafeGetCharAt(endLinePrevious
-1) == '\\';
680 // look back to set chPrevNonWhite properly for better regex colouring
683 while (--back
&& IsSpaceEquiv(MaskActive(styler
.StyleAt(back
))))
685 if (MaskActive(styler
.StyleAt(back
)) == SCE_C_OPERATOR
) {
686 chPrevNonWhite
= styler
.SafeGetCharAt(back
);
690 StyleContext
sc(startPos
, length
, initStyle
, styler
, static_cast<unsigned char>(0xff));
691 LinePPState preproc
= vlls
.ForLine(lineCurrent
);
693 bool definitionsChanged
= false;
695 // Truncate ppDefineHistory before current line
697 if (!options
.updatePreprocessor
)
698 ppDefineHistory
.clear();
700 std::vector
<PPDefinition
>::iterator itInvalid
= std::find_if(ppDefineHistory
.begin(), ppDefineHistory
.end(), After(lineCurrent
-1));
701 if (itInvalid
!= ppDefineHistory
.end()) {
702 ppDefineHistory
.erase(itInvalid
, ppDefineHistory
.end());
703 definitionsChanged
= true;
706 SymbolTable preprocessorDefinitions
= preprocessorDefinitionsStart
;
707 for (std::vector
<PPDefinition
>::iterator itDef
= ppDefineHistory
.begin(); itDef
!= ppDefineHistory
.end(); ++itDef
) {
709 preprocessorDefinitions
.erase(itDef
->key
);
711 preprocessorDefinitions
[itDef
->key
] = SymbolValue(itDef
->value
, itDef
->arguments
);
714 std::string rawStringTerminator
= rawStringTerminators
.ValueAt(lineCurrent
-1);
715 SparseState
<std::string
> rawSTNew(lineCurrent
);
717 int activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
719 const WordClassifier
&classifierIdentifiers
= subStyles
.Classifier(SCE_C_IDENTIFIER
);
720 const WordClassifier
&classifierDocKeyWords
= subStyles
.Classifier(SCE_C_COMMENTDOCKEYWORD
);
722 int lineEndNext
= styler
.LineEnd(lineCurrent
);
726 if (sc
.atLineStart
) {
727 // Using MaskActive() is not needed in the following statement.
728 // Inside inactive preprocessor declaration, state will be reset anyway at the end of this block.
729 if ((sc
.state
== SCE_C_STRING
) || (sc
.state
== SCE_C_CHARACTER
)) {
730 // Prevent SCE_C_STRINGEOL from leaking back to previous line which
731 // ends with a line continuation by locking in the state up to this position.
732 sc
.SetState(sc
.state
);
734 if ((MaskActive(sc
.state
) == SCE_C_PREPROCESSOR
) && (!continuationLine
)) {
735 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
737 // Reset states to beginning of colourise so no surprises
738 // if different sets of lines lexed.
740 lastWordWasUUID
= false;
741 isIncludePreprocessor
= false;
743 if (preproc
.IsInactive()) {
744 activitySet
= activeFlag
;
745 sc
.SetState(sc
.state
| activitySet
);
751 lineEndNext
= styler
.LineEnd(lineCurrent
);
752 vlls
.Add(lineCurrent
, preproc
);
753 if (rawStringTerminator
!= "") {
754 rawSTNew
.Set(lineCurrent
-1, rawStringTerminator
);
758 // Handle line continuation generically.
760 if (static_cast<int>((sc
.currentPos
+1)) >= lineEndNext
) {
762 lineEndNext
= styler
.LineEnd(lineCurrent
);
763 vlls
.Add(lineCurrent
, preproc
);
765 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
766 // Even in UTF-8, \r and \n are separate
769 continuationLine
= true;
775 const bool atLineEndBeforeSwitch
= sc
.atLineEnd
;
777 // Determine if the current state should terminate.
778 switch (MaskActive(sc
.state
)) {
780 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
783 // We accept almost anything because of hex. and number suffixes
785 sc
.ChangeState(SCE_C_USERLITERAL
|activitySet
);
786 } else if (!(setWord
.Contains(sc
.ch
)
788 || ((sc
.ch
== '+' || sc
.ch
== '-') && (sc
.chPrev
== 'e' || sc
.chPrev
== 'E' ||
789 sc
.chPrev
== 'p' || sc
.chPrev
== 'P')))) {
790 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
793 case SCE_C_USERLITERAL
:
794 if (!(setWord
.Contains(sc
.ch
)))
795 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
797 case SCE_C_IDENTIFIER
:
798 if (sc
.atLineStart
|| sc
.atLineEnd
|| !setWord
.Contains(sc
.ch
) || (sc
.ch
== '.')) {
801 sc
.GetCurrent(s
, sizeof(s
));
803 sc
.GetCurrentLowered(s
, sizeof(s
));
805 if (keywords
.InList(s
)) {
806 lastWordWasUUID
= strcmp(s
, "uuid") == 0;
807 sc
.ChangeState(SCE_C_WORD
|activitySet
);
808 } else if (keywords2
.InList(s
)) {
809 sc
.ChangeState(SCE_C_WORD2
|activitySet
);
810 } else if (keywords4
.InList(s
)) {
811 sc
.ChangeState(SCE_C_GLOBALCLASS
|activitySet
);
813 int subStyle
= classifierIdentifiers
.ValueFor(s
);
815 sc
.ChangeState(subStyle
|activitySet
);
818 const bool literalString
= sc
.ch
== '\"';
819 if (literalString
|| sc
.ch
== '\'') {
820 size_t lenS
= strlen(s
);
821 const bool raw
= literalString
&& sc
.chPrev
== 'R' && !setInvalidRawFirst
.Contains(sc
.chNext
);
826 ((lenS
== 1) && ((s
[0] == 'L') || (s
[0] == 'u') || (s
[0] == 'U'))) ||
827 ((lenS
== 2) && literalString
&& (s
[0] == 'u') && (s
[1] == '8'));
831 // Set the style of the string prefix to SCE_C_STRINGRAW but then change to
832 // SCE_C_DEFAULT as that allows the raw string start code to run.
833 sc
.ChangeState(SCE_C_STRINGRAW
|activitySet
);
834 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
836 sc
.ChangeState(SCE_C_STRING
|activitySet
);
839 sc
.ChangeState(SCE_C_CHARACTER
|activitySet
);
842 sc
.SetState(SCE_C_DEFAULT
| activitySet
);
845 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
849 case SCE_C_PREPROCESSOR
:
850 if (options
.stylingWithinPreprocessor
) {
851 if (IsASpace(sc
.ch
)) {
852 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
854 } else if (isStringInPreprocessor
&& (sc
.Match('>') || sc
.Match('\"') || sc
.atLineEnd
)) {
855 isStringInPreprocessor
= false;
856 } else if (!isStringInPreprocessor
) {
857 if ((isIncludePreprocessor
&& sc
.Match('<')) || sc
.Match('\"')) {
858 isStringInPreprocessor
= true;
859 } else if (sc
.Match('/', '*')) {
860 if (sc
.Match("/**") || sc
.Match("/*!")) {
861 sc
.SetState(SCE_C_PREPROCESSORCOMMENTDOC
|activitySet
);
863 sc
.SetState(SCE_C_PREPROCESSORCOMMENT
|activitySet
);
865 sc
.Forward(); // Eat the *
866 } else if (sc
.Match('/', '/')) {
867 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
871 case SCE_C_PREPROCESSORCOMMENT
:
872 case SCE_C_PREPROCESSORCOMMENTDOC
:
873 if (sc
.Match('*', '/')) {
875 sc
.ForwardSetState(SCE_C_PREPROCESSOR
|activitySet
);
876 continue; // Without advancing in case of '\'.
880 if (sc
.Match('*', '/')) {
882 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
884 styleBeforeTaskMarker
= SCE_C_COMMENT
;
885 highlightTaskMarker(sc
, styler
, activitySet
, markerList
, caseSensitive
);
888 case SCE_C_COMMENTDOC
:
889 if (sc
.Match('*', '/')) {
891 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
892 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
893 // Verify that we have the conditions to mark a comment-doc-keyword
894 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
895 styleBeforeDCKeyword
= SCE_C_COMMENTDOC
;
896 sc
.SetState(SCE_C_COMMENTDOCKEYWORD
|activitySet
);
900 case SCE_C_COMMENTLINE
:
901 if (sc
.atLineStart
&& !continuationLine
) {
902 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
904 styleBeforeTaskMarker
= SCE_C_COMMENTLINE
;
905 highlightTaskMarker(sc
, styler
, activitySet
, markerList
, caseSensitive
);
908 case SCE_C_COMMENTLINEDOC
:
909 if (sc
.atLineStart
&& !continuationLine
) {
910 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
911 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
912 // Verify that we have the conditions to mark a comment-doc-keyword
913 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '/' || sc
.chPrev
== '!') && (!IsASpace(sc
.chNext
))) {
914 styleBeforeDCKeyword
= SCE_C_COMMENTLINEDOC
;
915 sc
.SetState(SCE_C_COMMENTDOCKEYWORD
|activitySet
);
919 case SCE_C_COMMENTDOCKEYWORD
:
920 if ((styleBeforeDCKeyword
== SCE_C_COMMENTDOC
) && sc
.Match('*', '/')) {
921 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
);
923 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
924 seenDocKeyBrace
= false;
925 } else if (sc
.ch
== '[' || sc
.ch
== '{') {
926 seenDocKeyBrace
= true;
927 } else if (!setDoxygen
.Contains(sc
.ch
)
928 && !(seenDocKeyBrace
&& (sc
.ch
== ',' || sc
.ch
== '.'))) {
931 sc
.GetCurrent(s
, sizeof(s
));
933 sc
.GetCurrentLowered(s
, sizeof(s
));
935 if (!(IsASpace(sc
.ch
) || (sc
.ch
== 0))) {
936 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
|activitySet
);
937 } else if (!keywords3
.InList(s
+ 1)) {
938 int subStyleCDKW
= classifierDocKeyWords
.ValueFor(s
+1);
939 if (subStyleCDKW
>= 0) {
940 sc
.ChangeState(subStyleCDKW
|activitySet
);
942 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
|activitySet
);
945 sc
.SetState(styleBeforeDCKeyword
|activitySet
);
946 seenDocKeyBrace
= false;
951 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
952 } else if (isIncludePreprocessor
) {
954 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
955 isIncludePreprocessor
= false;
957 } else if (sc
.ch
== '\\') {
958 if (options
.escapeSequence
) {
959 sc
.SetState(SCE_C_ESCAPESEQUENCE
|activitySet
);
960 escapeSeq
.resetEscapeState(sc
.chNext
);
962 sc
.Forward(); // Skip all characters after the backslash
963 } else if (sc
.ch
== '\"') {
964 if (sc
.chNext
== '_') {
965 sc
.ChangeState(SCE_C_USERLITERAL
|activitySet
);
967 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
971 case SCE_C_ESCAPESEQUENCE
:
972 escapeSeq
.digitsLeft
--;
973 if (!escapeSeq
.atEscapeEnd(sc
.ch
)) {
977 sc
.SetState(SCE_C_STRING
|activitySet
);
978 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
979 } else if (sc
.ch
== '\\') {
980 escapeSeq
.resetEscapeState(sc
.chNext
);
983 sc
.SetState(SCE_C_STRING
|activitySet
);
985 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
989 case SCE_C_HASHQUOTEDSTRING
:
991 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
994 } else if (sc
.ch
== '\"') {
995 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
998 case SCE_C_STRINGRAW
:
999 if (sc
.Match(rawStringTerminator
.c_str())) {
1000 for (size_t termPos
=rawStringTerminator
.size(); termPos
; termPos
--)
1002 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1003 rawStringTerminator
= "";
1006 case SCE_C_CHARACTER
:
1008 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
1009 } else if (sc
.ch
== '\\') {
1010 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
1013 } else if (sc
.ch
== '\'') {
1014 if (sc
.chNext
== '_') {
1015 sc
.ChangeState(SCE_C_USERLITERAL
|activitySet
);
1017 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
1022 if (sc
.atLineStart
) {
1023 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1024 } else if (! inRERange
&& sc
.ch
== '/') {
1026 while ((sc
.ch
< 0x80) && islower(sc
.ch
))
1027 sc
.Forward(); // gobble regex flags
1028 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1029 } else if (sc
.ch
== '\\' && (static_cast<int>(sc
.currentPos
+1) < lineEndNext
)) {
1030 // Gobble up the escaped character
1032 } else if (sc
.ch
== '[') {
1034 } else if (sc
.ch
== ']') {
1038 case SCE_C_STRINGEOL
:
1039 if (sc
.atLineStart
) {
1040 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1043 case SCE_C_VERBATIM
:
1044 if (options
.verbatimStringsAllowEscapes
&& (sc
.ch
== '\\')) {
1045 sc
.Forward(); // Skip all characters after the backslash
1046 } else if (sc
.ch
== '\"') {
1047 if (sc
.chNext
== '\"') {
1050 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
1054 case SCE_C_TRIPLEVERBATIM
:
1055 if (sc
.Match("\"\"\"")) {
1056 while (sc
.Match('"')) {
1059 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1063 if (sc
.atLineEnd
|| sc
.ch
== ')') {
1064 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1067 case SCE_C_TASKMARKER
:
1068 if (isoperator(sc
.ch
) || IsASpace(sc
.ch
)) {
1069 sc
.SetState(styleBeforeTaskMarker
|activitySet
);
1070 styleBeforeTaskMarker
= SCE_C_DEFAULT
;
1074 if (sc
.atLineEnd
&& !atLineEndBeforeSwitch
) {
1075 // State exit processing consumed characters up to end of line.
1077 lineEndNext
= styler
.LineEnd(lineCurrent
);
1078 vlls
.Add(lineCurrent
, preproc
);
1081 // Determine if a new state should be entered.
1082 if (MaskActive(sc
.state
) == SCE_C_DEFAULT
) {
1083 if (sc
.Match('@', '\"')) {
1084 sc
.SetState(SCE_C_VERBATIM
|activitySet
);
1086 } else if (options
.triplequotedStrings
&& sc
.Match("\"\"\"")) {
1087 sc
.SetState(SCE_C_TRIPLEVERBATIM
|activitySet
);
1089 } else if (options
.hashquotedStrings
&& sc
.Match('#', '\"')) {
1090 sc
.SetState(SCE_C_HASHQUOTEDSTRING
|activitySet
);
1092 } else if (options
.backQuotedStrings
&& sc
.Match('`')) {
1093 sc
.SetState(SCE_C_STRINGRAW
|activitySet
);
1094 rawStringTerminator
= "`";
1095 } else if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
1096 if (lastWordWasUUID
) {
1097 sc
.SetState(SCE_C_UUID
|activitySet
);
1098 lastWordWasUUID
= false;
1100 sc
.SetState(SCE_C_NUMBER
|activitySet
);
1102 } else if (!sc
.atLineEnd
&& (setWordStart
.Contains(sc
.ch
) || (sc
.ch
== '@'))) {
1103 if (lastWordWasUUID
) {
1104 sc
.SetState(SCE_C_UUID
|activitySet
);
1105 lastWordWasUUID
= false;
1107 sc
.SetState(SCE_C_IDENTIFIER
|activitySet
);
1109 } else if (sc
.Match('/', '*')) {
1110 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Qt/Doxygen doc. style
1111 sc
.SetState(SCE_C_COMMENTDOC
|activitySet
);
1113 sc
.SetState(SCE_C_COMMENT
|activitySet
);
1115 sc
.Forward(); // Eat the * so it isn't used for the end of the comment
1116 } else if (sc
.Match('/', '/')) {
1117 if ((sc
.Match("///") && !sc
.Match("////")) || sc
.Match("//!"))
1118 // Support of Qt/Doxygen doc. style
1119 sc
.SetState(SCE_C_COMMENTLINEDOC
|activitySet
);
1121 sc
.SetState(SCE_C_COMMENTLINE
|activitySet
);
1122 } else if (sc
.ch
== '/'
1123 && (setOKBeforeRE
.Contains(chPrevNonWhite
)
1124 || followsReturnKeyword(sc
, styler
))
1125 && (!setCouldBePostOp
.Contains(chPrevNonWhite
)
1126 || !FollowsPostfixOperator(sc
, styler
))) {
1127 sc
.SetState(SCE_C_REGEX
|activitySet
); // JavaScript's RegEx
1129 } else if (sc
.ch
== '\"') {
1130 if (sc
.chPrev
== 'R') {
1132 if (MaskActive(styler
.StyleAt(sc
.currentPos
- 1)) == SCE_C_STRINGRAW
) {
1133 sc
.SetState(SCE_C_STRINGRAW
|activitySet
);
1134 rawStringTerminator
= ")";
1135 for (int termPos
= sc
.currentPos
+ 1;; termPos
++) {
1136 char chTerminator
= styler
.SafeGetCharAt(termPos
, '(');
1137 if (chTerminator
== '(')
1139 rawStringTerminator
+= chTerminator
;
1141 rawStringTerminator
+= '\"';
1143 sc
.SetState(SCE_C_STRING
|activitySet
);
1146 sc
.SetState(SCE_C_STRING
|activitySet
);
1148 isIncludePreprocessor
= false; // ensure that '>' won't end the string
1149 } else if (isIncludePreprocessor
&& sc
.ch
== '<') {
1150 sc
.SetState(SCE_C_STRING
|activitySet
);
1151 } else if (sc
.ch
== '\'') {
1152 sc
.SetState(SCE_C_CHARACTER
|activitySet
);
1153 } else if (sc
.ch
== '#' && visibleChars
== 0) {
1154 // Preprocessor commands are alone on their line
1155 sc
.SetState(SCE_C_PREPROCESSOR
|activitySet
);
1156 // Skip whitespace between # and preprocessor word
1159 } while ((sc
.ch
== ' ' || sc
.ch
== '\t') && sc
.More());
1161 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
1162 } else if (sc
.Match("include")) {
1163 isIncludePreprocessor
= true;
1165 if (options
.trackPreprocessor
) {
1166 if (sc
.Match("ifdef") || sc
.Match("ifndef")) {
1167 bool isIfDef
= sc
.Match("ifdef");
1168 int i
= isIfDef
? 5 : 6;
1169 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ i
+ 1, false);
1170 bool foundDef
= preprocessorDefinitions
.find(restOfLine
) != preprocessorDefinitions
.end();
1171 preproc
.StartSection(isIfDef
== foundDef
);
1172 } else if (sc
.Match("if")) {
1173 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 2, true);
1174 bool ifGood
= EvaluateExpression(restOfLine
, preprocessorDefinitions
);
1175 preproc
.StartSection(ifGood
);
1176 } else if (sc
.Match("else")) {
1177 if (!preproc
.CurrentIfTaken()) {
1178 preproc
.InvertCurrentLevel();
1179 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1181 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1182 } else if (!preproc
.IsInactive()) {
1183 preproc
.InvertCurrentLevel();
1184 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1186 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1188 } else if (sc
.Match("elif")) {
1189 // Ensure only one chosen out of #if .. #elif .. #elif .. #else .. #endif
1190 if (!preproc
.CurrentIfTaken()) {
1192 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 2, true);
1193 bool ifGood
= EvaluateExpression(restOfLine
, preprocessorDefinitions
);
1195 preproc
.InvertCurrentLevel();
1196 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1198 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1200 } else if (!preproc
.IsInactive()) {
1201 preproc
.InvertCurrentLevel();
1202 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1204 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1206 } else if (sc
.Match("endif")) {
1207 preproc
.EndSection();
1208 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
1209 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
1210 } else if (sc
.Match("define")) {
1211 if (options
.updatePreprocessor
&& !preproc
.IsInactive()) {
1212 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 6, true);
1213 size_t startName
= 0;
1214 while ((startName
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[startName
]))
1216 size_t endName
= startName
;
1217 while ((endName
< restOfLine
.length()) && setWord
.Contains(static_cast<unsigned char>(restOfLine
[endName
])))
1219 std::string key
= restOfLine
.substr(startName
, endName
-startName
);
1220 if ((endName
< restOfLine
.length()) && (restOfLine
.at(endName
) == '(')) {
1222 size_t endArgs
= endName
;
1223 while ((endArgs
< restOfLine
.length()) && (restOfLine
[endArgs
] != ')'))
1225 std::string args
= restOfLine
.substr(endName
+ 1, endArgs
- endName
- 1);
1226 size_t startValue
= endArgs
+1;
1227 while ((startValue
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[startValue
]))
1230 if (startValue
< restOfLine
.length())
1231 value
= restOfLine
.substr(startValue
);
1232 preprocessorDefinitions
[key
] = SymbolValue(value
, args
);
1233 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, value
, false, args
));
1234 definitionsChanged
= true;
1237 size_t startValue
= endName
;
1238 while ((startValue
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[startValue
]))
1240 std::string value
= restOfLine
.substr(startValue
);
1241 preprocessorDefinitions
[key
] = value
;
1242 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, value
));
1243 definitionsChanged
= true;
1246 } else if (sc
.Match("undef")) {
1247 if (options
.updatePreprocessor
&& !preproc
.IsInactive()) {
1248 const std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 5, false);
1249 std::vector
<std::string
> tokens
= Tokenize(restOfLine
);
1250 if (tokens
.size() >= 1) {
1251 const std::string key
= tokens
[0];
1252 preprocessorDefinitions
.erase(key
);
1253 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, "", true));
1254 definitionsChanged
= true;
1260 } else if (isoperator(sc
.ch
)) {
1261 sc
.SetState(SCE_C_OPERATOR
|activitySet
);
1265 if (!IsASpace(sc
.ch
) && !IsSpaceEquiv(MaskActive(sc
.state
))) {
1266 chPrevNonWhite
= sc
.ch
;
1269 continuationLine
= false;
1272 const bool rawStringsChanged
= rawStringTerminators
.Merge(rawSTNew
, lineCurrent
);
1273 if (definitionsChanged
|| rawStringsChanged
)
1274 styler
.ChangeLexerState(startPos
, startPos
+ length
);
1278 // Store both the current line's fold level and the next lines in the
1279 // level store to make it easy to pick up with each increment
1280 // and to make it possible to fiddle the current level for "} else {".
1282 void SCI_METHOD
LexerCPP::Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
1287 LexAccessor
styler(pAccess
);
1289 unsigned int endPos
= startPos
+ length
;
1290 int visibleChars
= 0;
1291 bool inLineComment
= false;
1292 int lineCurrent
= styler
.GetLine(startPos
);
1293 int levelCurrent
= SC_FOLDLEVELBASE
;
1294 if (lineCurrent
> 0)
1295 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
1296 unsigned int lineStartNext
= styler
.LineStart(lineCurrent
+1);
1297 int levelMinCurrent
= levelCurrent
;
1298 int levelNext
= levelCurrent
;
1299 char chNext
= styler
[startPos
];
1300 int styleNext
= MaskActive(styler
.StyleAt(startPos
));
1301 int style
= MaskActive(initStyle
);
1302 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
1303 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
1305 chNext
= styler
.SafeGetCharAt(i
+ 1);
1306 int stylePrev
= style
;
1308 styleNext
= MaskActive(styler
.StyleAt(i
+ 1));
1309 bool atEOL
= i
== (lineStartNext
-1);
1310 if ((style
== SCE_C_COMMENTLINE
) || (style
== SCE_C_COMMENTLINEDOC
))
1311 inLineComment
= true;
1312 if (options
.foldComment
&& options
.foldCommentMultiline
&& IsStreamCommentStyle(style
) && !inLineComment
) {
1313 if (!IsStreamCommentStyle(stylePrev
)) {
1315 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
1316 // Comments don't end at end of line and the next character may be unstyled.
1320 if (options
.foldComment
&& options
.foldCommentExplicit
&& ((style
== SCE_C_COMMENTLINE
) || options
.foldExplicitAnywhere
)) {
1321 if (userDefinedFoldMarkers
) {
1322 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
1324 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
1328 if ((ch
== '/') && (chNext
== '/')) {
1329 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
1330 if (chNext2
== '{') {
1332 } else if (chNext2
== '}') {
1338 if (options
.foldPreprocessor
&& (style
== SCE_C_PREPROCESSOR
)) {
1340 unsigned int j
= i
+ 1;
1341 while ((j
< endPos
) && IsASpaceOrTab(styler
.SafeGetCharAt(j
))) {
1344 if (styler
.Match(j
, "region") || styler
.Match(j
, "if")) {
1346 } else if (styler
.Match(j
, "end")) {
1351 if (options
.foldSyntaxBased
&& (style
== SCE_C_OPERATOR
)) {
1352 if (ch
== '{' || ch
== '[') {
1353 // Measure the minimum before a '{' to allow
1354 // folding on "} else {"
1355 if (levelMinCurrent
> levelNext
) {
1356 levelMinCurrent
= levelNext
;
1359 } else if (ch
== '}' || ch
== ']') {
1365 if (atEOL
|| (i
== endPos
-1)) {
1366 int levelUse
= levelCurrent
;
1367 if (options
.foldSyntaxBased
&& options
.foldAtElse
) {
1368 levelUse
= levelMinCurrent
;
1370 int lev
= levelUse
| levelNext
<< 16;
1371 if (visibleChars
== 0 && options
.foldCompact
)
1372 lev
|= SC_FOLDLEVELWHITEFLAG
;
1373 if (levelUse
< levelNext
)
1374 lev
|= SC_FOLDLEVELHEADERFLAG
;
1375 if (lev
!= styler
.LevelAt(lineCurrent
)) {
1376 styler
.SetLevel(lineCurrent
, lev
);
1379 lineStartNext
= styler
.LineStart(lineCurrent
+1);
1380 levelCurrent
= levelNext
;
1381 levelMinCurrent
= levelCurrent
;
1382 if (atEOL
&& (i
== static_cast<unsigned int>(styler
.Length()-1))) {
1383 // There is an empty line at end of file so give it same level and empty
1384 styler
.SetLevel(lineCurrent
, (levelCurrent
| levelCurrent
<< 16) | SC_FOLDLEVELWHITEFLAG
);
1387 inLineComment
= false;
1392 void LexerCPP::EvaluateTokens(std::vector
<std::string
> &tokens
, const SymbolTable
&preprocessorDefinitions
) {
1394 // Remove whitespace tokens
1395 tokens
.erase(std::remove_if(tokens
.begin(), tokens
.end(), OnlySpaceOrTab
), tokens
.end());
1397 // Evaluate defined statements to either 0 or 1
1398 for (size_t i
=0; (i
+1)<tokens
.size();) {
1399 if (tokens
[i
] == "defined") {
1400 const char *val
= "0";
1401 if (tokens
[i
+1] == "(") {
1402 if (((i
+ 2)<tokens
.size()) && (tokens
[i
+ 2] == ")")) {
1404 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 3);
1405 } else if (((i
+3)<tokens
.size()) && (tokens
[i
+3] == ")")) {
1406 // defined(<identifier>)
1407 SymbolTable::const_iterator it
= preprocessorDefinitions
.find(tokens
[i
+2]);
1408 if (it
!= preprocessorDefinitions
.end()) {
1411 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 4);
1413 // Spurious '(' so erase as more likely to result in false
1414 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 2);
1417 // defined <identifier>
1418 SymbolTable::const_iterator it
= preprocessorDefinitions
.find(tokens
[i
+1]);
1419 if (it
!= preprocessorDefinitions
.end()) {
1429 // Evaluate identifiers
1430 const size_t maxIterations
= 100;
1431 size_t iterations
= 0; // Limit number of iterations in case there is a recursive macro.
1432 for (size_t i
= 0; (i
<tokens
.size()) && (iterations
< maxIterations
);) {
1434 if (setWordStart
.Contains(static_cast<unsigned char>(tokens
[i
][0]))) {
1435 SymbolTable::const_iterator it
= preprocessorDefinitions
.find(tokens
[i
]);
1436 if (it
!= preprocessorDefinitions
.end()) {
1438 std::vector
<std::string
> macroTokens
= Tokenize(it
->second
.value
);
1439 if (it
->second
.IsMacro()) {
1440 if ((i
+ 1 < tokens
.size()) && (tokens
.at(i
+ 1) == "(")) {
1441 // Create map of argument name to value
1442 std::vector
<std::string
> argumentNames
= StringSplit(it
->second
.arguments
, ',');
1443 std::map
<std::string
, std::string
> arguments
;
1446 while ((tok
< tokens
.size()) && (arg
< argumentNames
.size()) && (tokens
.at(tok
) != ")")) {
1447 if (tokens
.at(tok
) != ",") {
1448 arguments
[argumentNames
.at(arg
)] = tokens
.at(tok
);
1454 // Remove invocation
1455 tokens
.erase(tokens
.begin() + i
, tokens
.begin() + tok
+ 1);
1457 // Substitute values into macro
1458 macroTokens
.erase(std::remove_if(macroTokens
.begin(), macroTokens
.end(), OnlySpaceOrTab
), macroTokens
.end());
1460 for (size_t iMacro
= 0; iMacro
< macroTokens
.size();) {
1461 if (setWordStart
.Contains(static_cast<unsigned char>(macroTokens
[iMacro
][0]))) {
1462 std::map
<std::string
, std::string
>::const_iterator itFind
= arguments
.find(macroTokens
[iMacro
]);
1463 if (itFind
!= arguments
.end()) {
1464 // TODO: Possible that value will be expression so should insert tokenized form
1465 macroTokens
[iMacro
] = itFind
->second
;
1471 // Insert results back into tokens
1472 tokens
.insert(tokens
.begin() + i
, macroTokens
.begin(), macroTokens
.end());
1478 // Remove invocation
1479 tokens
.erase(tokens
.begin() + i
);
1480 // Insert results back into tokens
1481 tokens
.insert(tokens
.begin() + i
, macroTokens
.begin(), macroTokens
.end());
1484 // Identifier not found
1485 tokens
.erase(tokens
.begin() + i
);
1492 // Find bracketed subexpressions and recurse on them
1493 BracketPair bracketPair
= FindBracketPair(tokens
);
1494 while (bracketPair
.itBracket
!= tokens
.end()) {
1495 std::vector
<std::string
> inBracket(bracketPair
.itBracket
+ 1, bracketPair
.itEndBracket
);
1496 EvaluateTokens(inBracket
, preprocessorDefinitions
);
1498 // The insertion is done before the removal because there were failures with the opposite approach
1499 tokens
.insert(bracketPair
.itBracket
, inBracket
.begin(), inBracket
.end());
1501 bracketPair
= FindBracketPair(tokens
);
1502 tokens
.erase(bracketPair
.itBracket
, bracketPair
.itEndBracket
+ 1);
1504 bracketPair
= FindBracketPair(tokens
);
1507 // Evaluate logical negations
1508 for (size_t j
=0; (j
+1)<tokens
.size();) {
1509 if (setNegationOp
.Contains(tokens
[j
][0])) {
1510 int isTrue
= atoi(tokens
[j
+1].c_str());
1511 if (tokens
[j
] == "!")
1513 std::vector
<std::string
>::iterator itInsert
=
1514 tokens
.erase(tokens
.begin() + j
, tokens
.begin() + j
+ 2);
1515 tokens
.insert(itInsert
, isTrue
? "1" : "0");
1521 // Evaluate expressions in precedence order
1522 enum precedence
{ precArithmetic
, precRelative
, precLogical
};
1523 for (int prec
=precArithmetic
; prec
<= precLogical
; prec
++) {
1524 // Looking at 3 tokens at a time so end at 2 before end
1525 for (size_t k
=0; (k
+2)<tokens
.size();) {
1526 char chOp
= tokens
[k
+1][0];
1528 ((prec
==precArithmetic
) && setArithmethicOp
.Contains(chOp
)) ||
1529 ((prec
==precRelative
) && setRelOp
.Contains(chOp
)) ||
1530 ((prec
==precLogical
) && setLogicalOp
.Contains(chOp
))
1532 int valA
= atoi(tokens
[k
].c_str());
1533 int valB
= atoi(tokens
[k
+2].c_str());
1535 if (tokens
[k
+1] == "+")
1536 result
= valA
+ valB
;
1537 else if (tokens
[k
+1] == "-")
1538 result
= valA
- valB
;
1539 else if (tokens
[k
+1] == "*")
1540 result
= valA
* valB
;
1541 else if (tokens
[k
+1] == "/")
1542 result
= valA
/ (valB
? valB
: 1);
1543 else if (tokens
[k
+1] == "%")
1544 result
= valA
% (valB
? valB
: 1);
1545 else if (tokens
[k
+1] == "<")
1546 result
= valA
< valB
;
1547 else if (tokens
[k
+1] == "<=")
1548 result
= valA
<= valB
;
1549 else 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
;
1557 else if (tokens
[k
+1] == "||")
1558 result
= valA
|| valB
;
1559 else if (tokens
[k
+1] == "&&")
1560 result
= valA
&& valB
;
1562 sprintf(sResult
, "%d", result
);
1563 std::vector
<std::string
>::iterator itInsert
=
1564 tokens
.erase(tokens
.begin() + k
, tokens
.begin() + k
+ 3);
1565 tokens
.insert(itInsert
, sResult
);
1573 std::vector
<std::string
> LexerCPP::Tokenize(const std::string
&expr
) const {
1574 // Break into tokens
1575 std::vector
<std::string
> tokens
;
1576 const char *cp
= expr
.c_str();
1579 if (setWord
.Contains(static_cast<unsigned char>(*cp
))) {
1580 // Identifiers and numbers
1581 while (setWord
.Contains(static_cast<unsigned char>(*cp
))) {
1585 } else if (IsSpaceOrTab(*cp
)) {
1586 while (IsSpaceOrTab(*cp
)) {
1590 } else if (setRelOp
.Contains(static_cast<unsigned char>(*cp
))) {
1593 if (setRelOp
.Contains(static_cast<unsigned char>(*cp
))) {
1597 } else if (setLogicalOp
.Contains(static_cast<unsigned char>(*cp
))) {
1600 if (setLogicalOp
.Contains(static_cast<unsigned char>(*cp
))) {
1605 // Should handle strings, characters, and comments here
1609 tokens
.push_back(word
);
1614 bool LexerCPP::EvaluateExpression(const std::string
&expr
, const SymbolTable
&preprocessorDefinitions
) {
1615 std::vector
<std::string
> tokens
= Tokenize(expr
);
1617 EvaluateTokens(tokens
, preprocessorDefinitions
);
1619 // "0" or "" -> false else true
1620 bool isFalse
= tokens
.empty() ||
1621 ((tokens
.size() == 1) && ((tokens
[0] == "") || tokens
[0] == "0"));
1625 LexerModule
lmCPP(SCLEX_CPP
, LexerCPP::LexerFactoryCPP
, "cpp", cppWordLists
);
1626 LexerModule
lmCPPNoCase(SCLEX_CPPNOCASE
, LexerCPP::LexerFactoryCPPInsensitive
, "cppnocase", cppWordLists
);