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.
17 #pragma warning(disable: 4786)
26 #include "Scintilla.h"
30 #include "LexAccessor.h"
32 #include "StyleContext.h"
33 #include "CharacterSet.h"
34 #include "LexerModule.h"
35 #include "OptionSet.h"
36 #include "SparseState.h"
39 using namespace Scintilla
;
42 static 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 static 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 static 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
);
73 while (--pos
> lineStartPos
) {
74 ch
= styler
.SafeGetCharAt(pos
);
75 if (ch
!= ' ' && ch
!= '\t') {
79 const char *retBack
= "nruter";
80 const char *s
= retBack
;
82 && pos
>= lineStartPos
83 && styler
.SafeGetCharAt(pos
) == *s
) {
90 static std::string
GetRestOfLine(LexAccessor
&styler
, int start
, bool allowSpace
) {
91 std::string restOfLine
;
93 char ch
= styler
.SafeGetCharAt(start
+ i
, '\n');
94 while ((ch
!= '\r') && (ch
!= '\n')) {
95 if (allowSpace
|| (ch
!= ' '))
98 ch
= styler
.SafeGetCharAt(start
+ i
, '\n');
103 static bool IsStreamCommentStyle(int style
) {
104 return style
== SCE_C_COMMENT
||
105 style
== SCE_C_COMMENTDOC
||
106 style
== SCE_C_COMMENTDOCKEYWORD
||
107 style
== SCE_C_COMMENTDOCKEYWORDERROR
;
110 static std::vector
<std::string
> Tokenize(const std::string
&s
) {
111 // Break into space separated tokens
113 std::vector
<std::string
> tokens
;
114 for (const char *cp
= s
.c_str(); *cp
; cp
++) {
115 if ((*cp
== ' ') || (*cp
== '\t')) {
117 tokens
.push_back(word
);
125 tokens
.push_back(word
);
130 struct PPDefinition
{
134 PPDefinition(int line_
, const std::string
&key_
, const std::string
&value_
) :
135 line(line_
), key(key_
), value(value_
) {
143 bool ValidLevel() const {
144 return level
>= 0 && level
< 32;
146 int maskLevel() const {
150 LinePPState() : state(0), ifTaken(0), level(-1) {
152 bool IsInactive() const {
155 bool CurrentIfTaken() {
156 return (ifTaken
& maskLevel()) != 0;
158 void StartSection(bool on
) {
162 state
&= ~maskLevel();
163 ifTaken
|= maskLevel();
165 state
|= maskLevel();
166 ifTaken
&= ~maskLevel();
172 state
&= ~maskLevel();
173 ifTaken
&= ~maskLevel();
177 void InvertCurrentLevel() {
179 state
^= maskLevel();
180 ifTaken
|= maskLevel();
185 // Hold the preprocessor state for each line seen.
186 // Currently one entry per line but could become sparse with just one entry per preprocessor line.
188 std::vector
<LinePPState
> vlls
;
190 LinePPState
ForLine(int line
) {
191 if ((line
> 0) && (vlls
.size() > static_cast<size_t>(line
))) {
194 return LinePPState();
197 void Add(int line
, LinePPState lls
) {
203 // An individual named option for use in an OptionSet
205 // Options used for LexerCPP
207 bool stylingWithinPreprocessor
;
208 bool identifiersAllowDollars
;
209 bool trackPreprocessor
;
210 bool updatePreprocessor
;
211 bool triplequotedStrings
;
213 bool foldSyntaxBased
;
215 bool foldCommentMultiline
;
216 bool foldCommentExplicit
;
217 std::string foldExplicitStart
;
218 std::string foldExplicitEnd
;
219 bool foldExplicitAnywhere
;
220 bool foldPreprocessor
;
224 stylingWithinPreprocessor
= false;
225 identifiersAllowDollars
= true;
226 trackPreprocessor
= true;
227 updatePreprocessor
= true;
228 triplequotedStrings
= false;
230 foldSyntaxBased
= true;
232 foldCommentMultiline
= true;
233 foldCommentExplicit
= true;
234 foldExplicitStart
= "";
235 foldExplicitEnd
= "";
236 foldExplicitAnywhere
= false;
237 foldPreprocessor
= false;
243 static const char *const cppWordLists
[] = {
244 "Primary keywords and identifiers",
245 "Secondary keywords and identifiers",
246 "Documentation comment keywords",
247 "Global classes and typedefs",
248 "Preprocessor definitions",
252 struct OptionSetCPP
: public OptionSet
<OptionsCPP
> {
254 DefineProperty("styling.within.preprocessor", &OptionsCPP::stylingWithinPreprocessor
,
255 "For C++ code, determines whether all preprocessor code is styled in the "
256 "preprocessor style (0, the default) or only from the initial # to the end "
257 "of the command word(1).");
259 DefineProperty("lexer.cpp.allow.dollars", &OptionsCPP::identifiersAllowDollars
,
260 "Set to 0 to disallow the '$' character in identifiers with the cpp lexer.");
262 DefineProperty("lexer.cpp.track.preprocessor", &OptionsCPP::trackPreprocessor
,
263 "Set to 1 to interpret #if/#else/#endif to grey out code that is not active.");
265 DefineProperty("lexer.cpp.update.preprocessor", &OptionsCPP::updatePreprocessor
,
266 "Set to 1 to update preprocessor definitions when #define found.");
268 DefineProperty("lexer.cpp.triplequoted.strings", &OptionsCPP::triplequotedStrings
,
269 "Set to 1 to enable highlighting of triple-quoted strings.");
271 DefineProperty("fold", &OptionsCPP::fold
);
273 DefineProperty("fold.cpp.syntax.based", &OptionsCPP::foldSyntaxBased
,
274 "Set this property to 0 to disable syntax based folding.");
276 DefineProperty("fold.comment", &OptionsCPP::foldComment
,
277 "This option enables folding multi-line comments and explicit fold points when using the C++ lexer. "
278 "Explicit fold points allows adding extra folding by placing a //{ comment at the start and a //} "
279 "at the end of a section that should fold.");
281 DefineProperty("fold.cpp.comment.multiline", &OptionsCPP::foldCommentMultiline
,
282 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
284 DefineProperty("fold.cpp.comment.explicit", &OptionsCPP::foldCommentExplicit
,
285 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
287 DefineProperty("fold.cpp.explicit.start", &OptionsCPP::foldExplicitStart
,
288 "The string to use for explicit fold start points, replacing the standard //{.");
290 DefineProperty("fold.cpp.explicit.end", &OptionsCPP::foldExplicitEnd
,
291 "The string to use for explicit fold end points, replacing the standard //}.");
293 DefineProperty("fold.cpp.explicit.anywhere", &OptionsCPP::foldExplicitAnywhere
,
294 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
296 DefineProperty("fold.preprocessor", &OptionsCPP::foldPreprocessor
,
297 "This option enables folding preprocessor directives when using the C++ lexer. "
298 "Includes C#'s explicit #region and #endregion folding directives.");
300 DefineProperty("fold.compact", &OptionsCPP::foldCompact
);
302 DefineProperty("fold.at.else", &OptionsCPP::foldAtElse
,
303 "This option enables C++ folding on a \"} else {\" line of an if statement.");
305 DefineWordListSets(cppWordLists
);
309 class LexerCPP
: public ILexer
{
311 CharacterSet setWord
;
312 CharacterSet setNegationOp
;
313 CharacterSet setArithmethicOp
;
314 CharacterSet setRelOp
;
315 CharacterSet setLogicalOp
;
317 std::vector
<PPDefinition
> ppDefineHistory
;
322 WordList ppDefinitions
;
323 std::map
<std::string
, std::string
> preprocessorDefinitionsStart
;
326 SparseState
<std::string
> rawStringTerminators
;
327 enum { activeFlag
= 0x40 };
329 LexerCPP(bool caseSensitive_
) :
330 caseSensitive(caseSensitive_
),
331 setWord(CharacterSet::setAlphaNum
, "._", 0x80, true),
332 setNegationOp(CharacterSet::setNone
, "!"),
333 setArithmethicOp(CharacterSet::setNone
, "+-/*%"),
334 setRelOp(CharacterSet::setNone
, "=!<>"),
335 setLogicalOp(CharacterSet::setNone
, "|&") {
339 void SCI_METHOD
Release() {
342 int SCI_METHOD
Version() const {
345 const char * SCI_METHOD
PropertyNames() {
346 return osCPP
.PropertyNames();
348 int SCI_METHOD
PropertyType(const char *name
) {
349 return osCPP
.PropertyType(name
);
351 const char * SCI_METHOD
DescribeProperty(const char *name
) {
352 return osCPP
.DescribeProperty(name
);
354 int SCI_METHOD
PropertySet(const char *key
, const char *val
);
355 const char * SCI_METHOD
DescribeWordListSets() {
356 return osCPP
.DescribeWordListSets();
358 int SCI_METHOD
WordListSet(int n
, const char *wl
);
359 void SCI_METHOD
Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
360 void SCI_METHOD
Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
362 void * SCI_METHOD
PrivateCall(int, void *) {
366 static ILexer
*LexerFactoryCPP() {
367 return new LexerCPP(true);
369 static ILexer
*LexerFactoryCPPInsensitive() {
370 return new LexerCPP(false);
372 static int MaskActive(int style
) {
373 return style
& ~activeFlag
;
375 void EvaluateTokens(std::vector
<std::string
> &tokens
);
376 bool EvaluateExpression(const std::string
&expr
, const std::map
<std::string
, std::string
> &preprocessorDefinitions
);
379 int SCI_METHOD
LexerCPP::PropertySet(const char *key
, const char *val
) {
380 if (osCPP
.PropertySet(&options
, key
, val
)) {
386 int SCI_METHOD
LexerCPP::WordListSet(int n
, const char *wl
) {
387 WordList
*wordListN
= 0;
390 wordListN
= &keywords
;
393 wordListN
= &keywords2
;
396 wordListN
= &keywords3
;
399 wordListN
= &keywords4
;
402 wordListN
= &ppDefinitions
;
405 int firstModification
= -1;
409 if (*wordListN
!= wlNew
) {
411 firstModification
= 0;
413 // Rebuild preprocessorDefinitions
414 preprocessorDefinitionsStart
.clear();
415 for (int nDefinition
= 0; nDefinition
< ppDefinitions
.len
; nDefinition
++) {
416 char *cpDefinition
= ppDefinitions
.words
[nDefinition
];
417 char *cpEquals
= strchr(cpDefinition
, '=');
419 std::string
name(cpDefinition
, cpEquals
- cpDefinition
);
420 std::string
val(cpEquals
+1);
421 preprocessorDefinitionsStart
[name
] = val
;
423 std::string
name(cpDefinition
);
424 std::string
val("1");
425 preprocessorDefinitionsStart
[name
] = val
;
431 return firstModification
;
434 // Functor used to truncate history
437 After(int line_
) : line(line_
) {}
438 bool operator()(PPDefinition
&p
) const {
439 return p
.line
> line
;
443 void SCI_METHOD
LexerCPP::Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
444 LexAccessor
styler(pAccess
);
446 CharacterSet
setOKBeforeRE(CharacterSet::setNone
, "([{=,:;!%^&*|?~+-");
447 CharacterSet
setCouldBePostOp(CharacterSet::setNone
, "+-");
449 CharacterSet
setDoxygen(CharacterSet::setAlpha
, "$@\\&<>#{}[]");
451 CharacterSet
setWordStart(CharacterSet::setAlpha
, "_", 0x80, true);
453 if (options
.identifiersAllowDollars
) {
454 setWordStart
.Add('$');
458 int chPrevNonWhite
= ' ';
459 int visibleChars
= 0;
460 bool lastWordWasUUID
= false;
461 int styleBeforeDCKeyword
= SCE_C_DEFAULT
;
462 bool continuationLine
= false;
463 bool isIncludePreprocessor
= false;
465 int lineCurrent
= styler
.GetLine(startPos
);
466 if ((initStyle
== SCE_C_PREPROCESSOR
) ||
467 (initStyle
== SCE_C_COMMENTLINE
) ||
468 (initStyle
== SCE_C_COMMENTLINEDOC
)) {
469 // Set continuationLine if last character of previous line is '\'
470 if (lineCurrent
> 0) {
471 int chBack
= styler
.SafeGetCharAt(startPos
-1, 0);
472 int chBack2
= styler
.SafeGetCharAt(startPos
-2, 0);
473 int lineEndChar
= '!';
474 if (chBack2
== '\r' && chBack
== '\n') {
475 lineEndChar
= styler
.SafeGetCharAt(startPos
-3, 0);
476 } else if (chBack
== '\n' || chBack
== '\r') {
477 lineEndChar
= chBack2
;
479 continuationLine
= lineEndChar
== '\\';
483 // look back to set chPrevNonWhite properly for better regex colouring
486 while (--back
&& IsSpaceEquiv(styler
.StyleAt(back
)))
488 if (styler
.StyleAt(back
) == SCE_C_OPERATOR
) {
489 chPrevNonWhite
= styler
.SafeGetCharAt(back
);
493 StyleContext
sc(startPos
, length
, initStyle
, styler
, 0x7f);
494 LinePPState preproc
= vlls
.ForLine(lineCurrent
);
496 bool definitionsChanged
= false;
498 // Truncate ppDefineHistory before current line
500 if (!options
.updatePreprocessor
)
501 ppDefineHistory
.clear();
503 std::vector
<PPDefinition
>::iterator itInvalid
= std::find_if(ppDefineHistory
.begin(), ppDefineHistory
.end(), After(lineCurrent
-1));
504 if (itInvalid
!= ppDefineHistory
.end()) {
505 ppDefineHistory
.erase(itInvalid
, ppDefineHistory
.end());
506 definitionsChanged
= true;
509 std::map
<std::string
, std::string
> preprocessorDefinitions
= preprocessorDefinitionsStart
;
510 for (std::vector
<PPDefinition
>::iterator itDef
= ppDefineHistory
.begin(); itDef
!= ppDefineHistory
.end(); ++itDef
) {
511 preprocessorDefinitions
[itDef
->key
] = itDef
->value
;
514 std::string rawStringTerminator
= rawStringTerminators
.ValueAt(lineCurrent
-1);
515 SparseState
<std::string
> rawSTNew(lineCurrent
);
517 int activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
519 for (; sc
.More(); sc
.Forward()) {
521 if (sc
.atLineStart
) {
522 if ((sc
.state
== SCE_C_STRING
) || (sc
.state
== SCE_C_CHARACTER
)) {
523 // Prevent SCE_C_STRINGEOL from leaking back to previous line which
524 // ends with a line continuation by locking in the state upto this position.
525 sc
.SetState(sc
.state
);
527 // Reset states to begining of colourise so no surprises
528 // if different sets of lines lexed.
530 lastWordWasUUID
= false;
531 isIncludePreprocessor
= false;
532 if (preproc
.IsInactive()) {
533 activitySet
= activeFlag
;
534 sc
.SetState(sc
.state
| activitySet
);
538 if (sc
.Match("#else") || sc
.Match("#end") || sc
.Match("#if")) {
547 vlls
.Add(lineCurrent
, preproc
);
548 if (rawStringTerminator
!= "") {
549 rawSTNew
.Set(lineCurrent
-1, rawStringTerminator
);
553 // Handle line continuation generically.
555 if (sc
.chNext
== '\n' || sc
.chNext
== '\r') {
557 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
560 continuationLine
= true;
565 const bool atLineEndBeforeSwitch
= sc
.atLineEnd
;
567 // Determine if the current state should terminate.
568 switch (MaskActive(sc
.state
)) {
570 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
573 // We accept almost anything because of hex. and number suffixes
574 if (!(setWord
.Contains(sc
.ch
) || ((sc
.ch
== '+' || sc
.ch
== '-') && (sc
.chPrev
== 'e' || sc
.chPrev
== 'E')))) {
575 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
578 case SCE_C_IDENTIFIER
:
579 if (!setWord
.Contains(sc
.ch
) || (sc
.ch
== '.')) {
582 sc
.GetCurrent(s
, sizeof(s
));
584 sc
.GetCurrentLowered(s
, sizeof(s
));
586 if (keywords
.InList(s
)) {
587 lastWordWasUUID
= strcmp(s
, "uuid") == 0;
588 sc
.ChangeState(SCE_C_WORD
|activitySet
);
589 } else if (keywords2
.InList(s
)) {
590 sc
.ChangeState(SCE_C_WORD2
|activitySet
);
591 } else if (keywords4
.InList(s
)) {
592 sc
.ChangeState(SCE_C_GLOBALCLASS
|activitySet
);
594 const bool literalString
= sc
.ch
== '\"';
595 if (literalString
|| sc
.ch
== '\'') {
596 size_t lenS
= strlen(s
);
597 const bool raw
= literalString
&& sc
.chPrev
== 'R';
602 ((lenS
== 1) && ((s
[0] == 'L') || (s
[0] == 'u') || (s
[0] == 'U'))) ||
603 ((lenS
== 2) && literalString
&& (s
[0] == 'u') && (s
[1] == '8'));
606 sc
.ChangeState((raw
? SCE_C_STRINGRAW
: SCE_C_STRING
)|activitySet
);
608 sc
.ChangeState(SCE_C_CHARACTER
|activitySet
);
611 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
614 case SCE_C_PREPROCESSOR
:
615 if (sc
.atLineStart
&& !continuationLine
) {
616 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
617 } else if (options
.stylingWithinPreprocessor
) {
618 if (IsASpace(sc
.ch
)) {
619 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
622 if (sc
.Match('/', '*') || sc
.Match('/', '/')) {
623 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
628 if (sc
.Match('*', '/')) {
630 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
633 case SCE_C_COMMENTDOC
:
634 if (sc
.Match('*', '/')) {
636 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
637 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
638 // Verify that we have the conditions to mark a comment-doc-keyword
639 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
640 styleBeforeDCKeyword
= SCE_C_COMMENTDOC
;
641 sc
.SetState(SCE_C_COMMENTDOCKEYWORD
|activitySet
);
645 case SCE_C_COMMENTLINE
:
646 if (sc
.atLineStart
&& !continuationLine
) {
647 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
650 case SCE_C_COMMENTLINEDOC
:
651 if (sc
.atLineStart
&& !continuationLine
) {
652 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
653 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
654 // Verify that we have the conditions to mark a comment-doc-keyword
655 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '/' || sc
.chPrev
== '!') && (!IsASpace(sc
.chNext
))) {
656 styleBeforeDCKeyword
= SCE_C_COMMENTLINEDOC
;
657 sc
.SetState(SCE_C_COMMENTDOCKEYWORD
|activitySet
);
661 case SCE_C_COMMENTDOCKEYWORD
:
662 if ((styleBeforeDCKeyword
== SCE_C_COMMENTDOC
) && sc
.Match('*', '/')) {
663 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
);
665 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
666 } else if (!setDoxygen
.Contains(sc
.ch
)) {
669 sc
.GetCurrent(s
, sizeof(s
));
671 sc
.GetCurrentLowered(s
, sizeof(s
));
673 if (!IsASpace(sc
.ch
) || !keywords3
.InList(s
+ 1)) {
674 sc
.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR
|activitySet
);
676 sc
.SetState(styleBeforeDCKeyword
);
681 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
682 } else if (isIncludePreprocessor
) {
684 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
685 isIncludePreprocessor
= false;
687 } else if (sc
.ch
== '\\') {
688 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
691 } else if (sc
.ch
== '\"') {
692 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
695 case SCE_C_STRINGRAW
:
696 if (sc
.Match(rawStringTerminator
.c_str())) {
697 for (size_t termPos
=rawStringTerminator
.size(); termPos
; termPos
--)
699 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
700 rawStringTerminator
= "";
703 case SCE_C_CHARACTER
:
705 sc
.ChangeState(SCE_C_STRINGEOL
|activitySet
);
706 } else if (sc
.ch
== '\\') {
707 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
710 } else if (sc
.ch
== '\'') {
711 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
715 if (sc
.atLineStart
) {
716 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
717 } else if (sc
.ch
== '/') {
719 while ((sc
.ch
< 0x80) && islower(sc
.ch
))
720 sc
.Forward(); // gobble regex flags
721 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
722 } else if (sc
.ch
== '\\') {
723 // Gobble up the quoted character
724 if (sc
.chNext
== '\\' || sc
.chNext
== '/') {
729 case SCE_C_STRINGEOL
:
730 if (sc
.atLineStart
) {
731 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
736 if (sc
.chNext
== '\"') {
739 sc
.ForwardSetState(SCE_C_DEFAULT
|activitySet
);
743 case SCE_C_TRIPLEVERBATIM
:
744 if (sc
.Match("\"\"\"")) {
745 while (sc
.Match('"')) {
748 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
752 if (sc
.ch
== '\r' || sc
.ch
== '\n' || sc
.ch
== ')') {
753 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
757 if (sc
.atLineEnd
&& !atLineEndBeforeSwitch
) {
758 // State exit processing consumed characters up to end of line.
760 vlls
.Add(lineCurrent
, preproc
);
763 // Determine if a new state should be entered.
764 if (MaskActive(sc
.state
) == SCE_C_DEFAULT
) {
765 if (sc
.Match('@', '\"')) {
766 sc
.SetState(SCE_C_VERBATIM
|activitySet
);
768 } else if (options
.triplequotedStrings
&& sc
.Match("\"\"\"")) {
769 sc
.SetState(SCE_C_TRIPLEVERBATIM
|activitySet
);
771 } else if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
772 if (lastWordWasUUID
) {
773 sc
.SetState(SCE_C_UUID
|activitySet
);
774 lastWordWasUUID
= false;
776 sc
.SetState(SCE_C_NUMBER
|activitySet
);
778 } else if (setWordStart
.Contains(sc
.ch
) || (sc
.ch
== '@')) {
779 if (lastWordWasUUID
) {
780 sc
.SetState(SCE_C_UUID
|activitySet
);
781 lastWordWasUUID
= false;
783 sc
.SetState(SCE_C_IDENTIFIER
|activitySet
);
785 } else if (sc
.Match('/', '*')) {
786 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Qt/Doxygen doc. style
787 sc
.SetState(SCE_C_COMMENTDOC
|activitySet
);
789 sc
.SetState(SCE_C_COMMENT
|activitySet
);
791 sc
.Forward(); // Eat the * so it isn't used for the end of the comment
792 } else if (sc
.Match('/', '/')) {
793 if ((sc
.Match("///") && !sc
.Match("////")) || sc
.Match("//!"))
794 // Support of Qt/Doxygen doc. style
795 sc
.SetState(SCE_C_COMMENTLINEDOC
|activitySet
);
797 sc
.SetState(SCE_C_COMMENTLINE
|activitySet
);
798 } else if (sc
.ch
== '/'
799 && (setOKBeforeRE
.Contains(chPrevNonWhite
)
800 || followsReturnKeyword(sc
, styler
))
801 && (!setCouldBePostOp
.Contains(chPrevNonWhite
)
802 || !FollowsPostfixOperator(sc
, styler
))) {
803 sc
.SetState(SCE_C_REGEX
|activitySet
); // JavaScript's RegEx
804 } else if (sc
.ch
== '\"') {
805 if (sc
.chPrev
== 'R') {
806 sc
.SetState(SCE_C_STRINGRAW
|activitySet
);
807 rawStringTerminator
= ")";
808 for (int termPos
= sc
.currentPos
+ 1;; termPos
++) {
809 char chTerminator
= styler
.SafeGetCharAt(termPos
, '(');
810 if (chTerminator
== '(')
812 rawStringTerminator
+= chTerminator
;
814 rawStringTerminator
+= '\"';
816 sc
.SetState(SCE_C_STRING
|activitySet
);
818 isIncludePreprocessor
= false; // ensure that '>' won't end the string
819 } else if (isIncludePreprocessor
&& sc
.ch
== '<') {
820 sc
.SetState(SCE_C_STRING
|activitySet
);
821 } else if (sc
.ch
== '\'') {
822 sc
.SetState(SCE_C_CHARACTER
|activitySet
);
823 } else if (sc
.ch
== '#' && visibleChars
== 0) {
824 // Preprocessor commands are alone on their line
825 sc
.SetState(SCE_C_PREPROCESSOR
|activitySet
);
826 // Skip whitespace between # and preprocessor word
829 } while ((sc
.ch
== ' ' || sc
.ch
== '\t') && sc
.More());
831 sc
.SetState(SCE_C_DEFAULT
|activitySet
);
832 } else if (sc
.Match("include")) {
833 isIncludePreprocessor
= true;
835 if (options
.trackPreprocessor
) {
836 if (sc
.Match("ifdef") || sc
.Match("ifndef")) {
837 bool isIfDef
= sc
.Match("ifdef");
838 int i
= isIfDef
? 5 : 6;
839 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ i
+ 1, false);
840 bool foundDef
= preprocessorDefinitions
.find(restOfLine
) != preprocessorDefinitions
.end();
841 preproc
.StartSection(isIfDef
== foundDef
);
842 } else if (sc
.Match("if")) {
843 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 2, true);
844 bool ifGood
= EvaluateExpression(restOfLine
, preprocessorDefinitions
);
845 preproc
.StartSection(ifGood
);
846 } else if (sc
.Match("else")) {
847 if (!preproc
.CurrentIfTaken()) {
848 preproc
.InvertCurrentLevel();
849 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
851 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
852 } else if (!preproc
.IsInactive()) {
853 preproc
.InvertCurrentLevel();
854 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
856 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
858 } else if (sc
.Match("elif")) {
859 // Ensure only one chosen out of #if .. #elif .. #elif .. #else .. #endif
860 if (!preproc
.CurrentIfTaken()) {
862 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 2, true);
863 bool ifGood
= EvaluateExpression(restOfLine
, preprocessorDefinitions
);
865 preproc
.InvertCurrentLevel();
866 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
868 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
870 } else if (!preproc
.IsInactive()) {
871 preproc
.InvertCurrentLevel();
872 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
874 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
876 } else if (sc
.Match("endif")) {
877 preproc
.EndSection();
878 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
879 sc
.ChangeState(SCE_C_PREPROCESSOR
|activitySet
);
880 } else if (sc
.Match("define")) {
881 if (options
.updatePreprocessor
&& !preproc
.IsInactive()) {
882 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 6, true);
883 if (restOfLine
.find(")") == std::string::npos
) { // Don't handle macros with arguments
884 std::vector
<std::string
> tokens
= Tokenize(restOfLine
);
886 std::string
value("1");
887 if (tokens
.size() >= 1) {
889 if (tokens
.size() >= 2) {
892 preprocessorDefinitions
[key
] = value
;
893 ppDefineHistory
.push_back(PPDefinition(lineCurrent
, key
, value
));
894 definitionsChanged
= true;
901 } else if (isoperator(static_cast<char>(sc
.ch
))) {
902 sc
.SetState(SCE_C_OPERATOR
|activitySet
);
906 if (!IsASpace(sc
.ch
) && !IsSpaceEquiv(sc
.state
)) {
907 chPrevNonWhite
= sc
.ch
;
910 continuationLine
= false;
912 const bool rawStringsChanged
= rawStringTerminators
.Merge(rawSTNew
, lineCurrent
);
913 if (definitionsChanged
|| rawStringsChanged
)
914 styler
.ChangeLexerState(startPos
, startPos
+ length
);
918 // Store both the current line's fold level and the next lines in the
919 // level store to make it easy to pick up with each increment
920 // and to make it possible to fiddle the current level for "} else {".
922 void SCI_METHOD
LexerCPP::Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
927 LexAccessor
styler(pAccess
);
929 unsigned int endPos
= startPos
+ length
;
930 int visibleChars
= 0;
931 int lineCurrent
= styler
.GetLine(startPos
);
932 int levelCurrent
= SC_FOLDLEVELBASE
;
934 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
935 int levelMinCurrent
= levelCurrent
;
936 int levelNext
= levelCurrent
;
937 char chNext
= styler
[startPos
];
938 int styleNext
= MaskActive(styler
.StyleAt(startPos
));
939 int style
= MaskActive(initStyle
);
940 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
941 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
943 chNext
= styler
.SafeGetCharAt(i
+ 1);
944 int stylePrev
= style
;
946 styleNext
= MaskActive(styler
.StyleAt(i
+ 1));
947 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
948 if (options
.foldComment
&& options
.foldCommentMultiline
&& IsStreamCommentStyle(style
)) {
949 if (!IsStreamCommentStyle(stylePrev
) && (stylePrev
!= SCE_C_COMMENTLINEDOC
)) {
951 } else if (!IsStreamCommentStyle(styleNext
) && (styleNext
!= SCE_C_COMMENTLINEDOC
) && !atEOL
) {
952 // Comments don't end at end of line and the next character may be unstyled.
956 if (options
.foldComment
&& options
.foldCommentExplicit
&& ((style
== SCE_C_COMMENTLINE
) || options
.foldExplicitAnywhere
)) {
957 if (userDefinedFoldMarkers
) {
958 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
960 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
964 if ((ch
== '/') && (chNext
== '/')) {
965 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
966 if (chNext2
== '{') {
968 } else if (chNext2
== '}') {
974 if (options
.foldPreprocessor
&& (style
== SCE_C_PREPROCESSOR
)) {
976 unsigned int j
= i
+ 1;
977 while ((j
< endPos
) && IsASpaceOrTab(styler
.SafeGetCharAt(j
))) {
980 if (styler
.Match(j
, "region") || styler
.Match(j
, "if")) {
982 } else if (styler
.Match(j
, "end")) {
987 if (options
.foldSyntaxBased
&& (style
== SCE_C_OPERATOR
)) {
989 // Measure the minimum before a '{' to allow
990 // folding on "} else {"
991 if (levelMinCurrent
> levelNext
) {
992 levelMinCurrent
= levelNext
;
995 } else if (ch
== '}') {
1001 if (atEOL
|| (i
== endPos
-1)) {
1002 int levelUse
= levelCurrent
;
1003 if (options
.foldSyntaxBased
&& options
.foldAtElse
) {
1004 levelUse
= levelMinCurrent
;
1006 int lev
= levelUse
| levelNext
<< 16;
1007 if (visibleChars
== 0 && options
.foldCompact
)
1008 lev
|= SC_FOLDLEVELWHITEFLAG
;
1009 if (levelUse
< levelNext
)
1010 lev
|= SC_FOLDLEVELHEADERFLAG
;
1011 if (lev
!= styler
.LevelAt(lineCurrent
)) {
1012 styler
.SetLevel(lineCurrent
, lev
);
1015 levelCurrent
= levelNext
;
1016 levelMinCurrent
= levelCurrent
;
1017 if (atEOL
&& (i
== static_cast<unsigned int>(styler
.Length()-1))) {
1018 // There is an empty line at end of file so give it same level and empty
1019 styler
.SetLevel(lineCurrent
, (levelCurrent
| levelCurrent
<< 16) | SC_FOLDLEVELWHITEFLAG
);
1026 void LexerCPP::EvaluateTokens(std::vector
<std::string
> &tokens
) {
1028 // Evaluate defined() statements to either 0 or 1
1029 for (size_t i
=0; (i
+2)<tokens
.size();) {
1030 if ((tokens
[i
] == "defined") && (tokens
[i
+1] == "(")) {
1031 const char *val
= "0";
1032 if (tokens
[i
+2] == ")") {
1034 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 3);
1035 } else if (((i
+2)<tokens
.size()) && (tokens
[i
+3] == ")")) {
1037 tokens
.erase(tokens
.begin() + i
+ 1, tokens
.begin() + i
+ 4);
1046 // Find bracketed subexpressions and recurse on them
1047 std::vector
<std::string
>::iterator itBracket
= std::find(tokens
.begin(), tokens
.end(), "(");
1048 std::vector
<std::string
>::iterator itEndBracket
= std::find(tokens
.begin(), tokens
.end(), ")");
1049 while ((itBracket
!= tokens
.end()) && (itEndBracket
!= tokens
.end()) && (itEndBracket
> itBracket
)) {
1050 std::vector
<std::string
> inBracket(itBracket
+ 1, itEndBracket
);
1051 EvaluateTokens(inBracket
);
1053 // The insertion is done before the removal because there were failures with the opposite approach
1054 tokens
.insert(itBracket
, inBracket
.begin(), inBracket
.end());
1055 itBracket
= std::find(tokens
.begin(), tokens
.end(), "(");
1056 itEndBracket
= std::find(tokens
.begin(), tokens
.end(), ")");
1057 tokens
.erase(itBracket
, itEndBracket
+ 1);
1059 itBracket
= std::find(tokens
.begin(), tokens
.end(), "(");
1060 itEndBracket
= std::find(tokens
.begin(), tokens
.end(), ")");
1063 // Evaluate logical negations
1064 for (size_t j
=0; (j
+1)<tokens
.size();) {
1065 if (setNegationOp
.Contains(tokens
[j
][0])) {
1066 int isTrue
= atoi(tokens
[j
+1].c_str());
1067 if (tokens
[j
] == "!")
1069 std::vector
<std::string
>::iterator itInsert
=
1070 tokens
.erase(tokens
.begin() + j
, tokens
.begin() + j
+ 2);
1071 tokens
.insert(itInsert
, isTrue
? "1" : "0");
1077 // Evaluate expressions in precedence order
1078 enum precedence
{ precArithmetic
, precRelative
, precLogical
};
1079 for (int prec
=precArithmetic
; prec
<= precLogical
; prec
++) {
1080 // Looking at 3 tokens at a time so end at 2 before end
1081 for (size_t k
=0; (k
+2)<tokens
.size();) {
1082 char chOp
= tokens
[k
+1][0];
1084 ((prec
==precArithmetic
) && setArithmethicOp
.Contains(chOp
)) ||
1085 ((prec
==precRelative
) && setRelOp
.Contains(chOp
)) ||
1086 ((prec
==precLogical
) && setLogicalOp
.Contains(chOp
))
1088 int valA
= atoi(tokens
[k
].c_str());
1089 int valB
= atoi(tokens
[k
+2].c_str());
1091 if (tokens
[k
+1] == "+")
1092 result
= valA
+ valB
;
1093 else if (tokens
[k
+1] == "-")
1094 result
= valA
- valB
;
1095 else if (tokens
[k
+1] == "*")
1096 result
= valA
* valB
;
1097 else if (tokens
[k
+1] == "/")
1098 result
= valA
/ (valB
? valB
: 1);
1099 else if (tokens
[k
+1] == "%")
1100 result
= valA
% (valB
? valB
: 1);
1101 else if (tokens
[k
+1] == "<")
1102 result
= valA
< valB
;
1103 else if (tokens
[k
+1] == "<=")
1104 result
= valA
<= valB
;
1105 else if (tokens
[k
+1] == ">")
1106 result
= valA
> valB
;
1107 else if (tokens
[k
+1] == ">=")
1108 result
= valA
>= valB
;
1109 else if (tokens
[k
+1] == "==")
1110 result
= valA
== valB
;
1111 else if (tokens
[k
+1] == "!=")
1112 result
= valA
!= valB
;
1113 else if (tokens
[k
+1] == "||")
1114 result
= valA
|| valB
;
1115 else if (tokens
[k
+1] == "&&")
1116 result
= valA
&& valB
;
1118 sprintf(sResult
, "%d", result
);
1119 std::vector
<std::string
>::iterator itInsert
=
1120 tokens
.erase(tokens
.begin() + k
, tokens
.begin() + k
+ 3);
1121 tokens
.insert(itInsert
, sResult
);
1129 bool LexerCPP::EvaluateExpression(const std::string
&expr
, const std::map
<std::string
, std::string
> &preprocessorDefinitions
) {
1130 // Break into tokens, replacing with definitions
1132 std::vector
<std::string
> tokens
;
1133 const char *cp
= expr
.c_str();
1135 if (setWord
.Contains(*cp
)) {
1138 std::map
<std::string
, std::string
>::const_iterator it
= preprocessorDefinitions
.find(word
);
1139 if (it
!= preprocessorDefinitions
.end()) {
1140 tokens
.push_back(it
->second
);
1141 } else if (!word
.empty() && ((word
[0] >= '0' && word
[0] <= '9') || (word
== "defined"))) {
1142 tokens
.push_back(word
);
1148 if ((*cp
!= ' ') && (*cp
!= '\t')) {
1149 std::string
op(cp
, 1);
1150 if (setRelOp
.Contains(*cp
)) {
1151 if (setRelOp
.Contains(cp
[1])) {
1155 } else if (setLogicalOp
.Contains(*cp
)) {
1156 if (setLogicalOp
.Contains(cp
[1])) {
1161 tokens
.push_back(op
);
1167 EvaluateTokens(tokens
);
1169 // "0" or "" -> false else true
1170 bool isFalse
= tokens
.empty() ||
1171 ((tokens
.size() == 1) && ((tokens
[0] == "") || tokens
[0] == "0"));
1175 LexerModule
lmCPP(SCLEX_CPP
, LexerCPP::LexerFactoryCPP
, "cpp", cppWordLists
);
1176 LexerModule
lmCPPNoCase(SCLEX_CPPNOCASE
, LexerCPP::LexerFactoryCPPInsensitive
, "cppnocase", cppWordLists
);