aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--lexers/LexCPP.cxx758
1 files changed, 646 insertions, 112 deletions
diff --git a/lexers/LexCPP.cxx b/lexers/LexCPP.cxx
index 8a9395e17..73ba70a42 100644
--- a/lexers/LexCPP.cxx
+++ b/lexers/LexCPP.cxx
@@ -10,16 +10,33 @@
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
+#include <assert.h>
-#include "Platform.h"
+#ifdef _MSC_VER
+#pragma warning(disable: 4786)
+#endif
+#ifdef __BORLANDC__
+// Borland C++ displays warnings in vector header without this
+#pragma option -w-ccc -w-rch
+#endif
-#include "PropSet.h"
-#include "Accessor.h"
-#include "StyleContext.h"
-#include "KeyWords.h"
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+
+#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
+
+#include "PropSetSimple.h"
+#include "WordList.h"
+#include "LexAccessor.h"
+#include "Accessor.h"
+#include "StyleContext.h"
#include "CharacterSet.h"
+#include "LexerModule.h"
+#include "OptionSet.h"
#ifdef SCI_NAMESPACE
using namespace Scintilla;
@@ -39,7 +56,7 @@ static bool IsSpaceEquiv(int state) {
// a = b+++/ptn/...
// Putting a space between the '++' post-inc operator and the '+' binary op
// fixes this, and is highly recommended for readability anyway.
-static bool FollowsPostfixOperator(StyleContext &sc, Accessor &styler) {
+static bool FollowsPostfixOperator(StyleContext &sc, LexAccessor &styler) {
int pos = (int) sc.currentPos;
while (--pos > 0) {
char ch = styler[pos];
@@ -50,18 +67,321 @@ static bool FollowsPostfixOperator(StyleContext &sc, Accessor &styler) {
return false;
}
-static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
- Accessor &styler, bool caseSensitive) {
+static std::string GetRestOfLine(LexAccessor &styler, int start, bool allowSpace) {
+ std::string restOfLine;
+ int i =0;
+ char ch = styler.SafeGetCharAt(start + i, '\n');
+ while ((ch != '\r') && (ch != '\n')) {
+ if (allowSpace || (ch != ' '))
+ restOfLine += ch;
+ i++;
+ ch = styler.SafeGetCharAt(start + i, '\n');
+ }
+ return restOfLine;
+}
+
+static bool IsStreamCommentStyle(int style) {
+ return style == SCE_C_COMMENT ||
+ style == SCE_C_COMMENTDOC ||
+ style == SCE_C_COMMENTDOCKEYWORD ||
+ style == SCE_C_COMMENTDOCKEYWORDERROR;
+}
+
+static std::vector<std::string> Tokenize(const std::string &s) {
+ // Break into space separated tokens
+ std::string word;
+ std::vector<std::string> tokens;
+ for (const char *cp = s.c_str(); *cp; cp++) {
+ if ((*cp == ' ') || (*cp == '\t')) {
+ if (!word.empty()) {
+ tokens.push_back(word);
+ word = "";
+ }
+ } else {
+ word += *cp;
+ }
+ }
+ if (!word.empty()) {
+ tokens.push_back(word);
+ }
+ return tokens;
+}
+
+struct PPDefinition {
+ int line;
+ std::string key;
+ std::string value;
+ PPDefinition(int line_, const std::string &key_, const std::string &value_) :
+ line(line_), key(key_), value(value_) {
+ }
+};
+
+class LinePPState {
+ int state;
+ int ifTaken;
+ int level;
+ bool ValidLevel() const {
+ return level >= 0 && level < 32;
+ }
+ int maskLevel() const {
+ return 1 << level;
+ }
+public:
+ LinePPState() : state(0), ifTaken(0), level(-1) {
+ }
+ bool IsInactive() const {
+ return state != 0;
+ }
+ bool CurrentIfTaken() {
+ return (ifTaken & maskLevel()) != 0;
+ }
+ void StartSection(bool on) {
+ level++;
+ if (ValidLevel()) {
+ if (on) {
+ state &= ~maskLevel();
+ ifTaken |= maskLevel();
+ } else {
+ state |= maskLevel();
+ ifTaken &= ~maskLevel();
+ }
+ }
+ }
+ void EndSection() {
+ if (ValidLevel()) {
+ state &= ~maskLevel();
+ ifTaken &= ~maskLevel();
+ }
+ level--;
+ }
+ void InvertCurrentLevel() {
+ if (ValidLevel()) {
+ state ^= maskLevel();
+ ifTaken |= maskLevel();
+ }
+ }
+};
+
+// Hold the preprocessor state for each line seen.
+// Currently one entry per line but could become sparse with just one entry per preprocessor line.
+class PPStates {
+ std::vector<LinePPState> vlls;
+public:
+ LinePPState ForLine(int line) {
+ if ((line > 0) && (vlls.size() > static_cast<size_t>(line))) {
+ return vlls[line];
+ } else {
+ return LinePPState();
+ }
+ }
+ void Add(int line, LinePPState lls) {
+ vlls.resize(line+1);
+ vlls[line] = lls;
+ }
+};
+
+// An individual named option for use in an OptionSet
+
+// Options used for LexerCPP
+struct OptionsCPP {
+ bool stylingWithinPreprocessor;
+ bool identifiersAllowDollars;
+ bool trackPreprocessor;
+ bool updatePreprocessor;
+ bool foldComment;
+ bool foldPreprocessor;
+ bool foldCompact;
+ bool foldAtElse;
+ int OO_i;
+ std::string OO_s;
+ OptionsCPP() {
+ stylingWithinPreprocessor = false;
+ identifiersAllowDollars = true;
+ trackPreprocessor = true;
+ updatePreprocessor = true;
+ foldComment = false;
+ foldPreprocessor = false;
+ foldCompact = false;
+ foldAtElse = false;
+ }
+};
+
+static const char *const cppWordLists[] = {
+ "Primary keywords and identifiers",
+ "Secondary keywords and identifiers",
+ "Documentation comment keywords",
+ "Global classes and typedefs",
+ "Preprocessor definitions",
+ 0,
+};
+
+struct OptionSetCPP : public OptionSet<OptionsCPP> {
+ OptionSetCPP() {
+ DefineProperty("styling.within.preprocessor", &OptionsCPP::stylingWithinPreprocessor,
+ "For C++ code, determines whether all preprocessor code is styled in the "
+ "preprocessor style (0, the default) or only from the initial # to the end "
+ "of the command word(1).");
+
+ DefineProperty("lexer.cpp.allow.dollars", &OptionsCPP::identifiersAllowDollars,
+ "Set to 0 to disallow the '$' character in identifiers with the cpp lexer.");
+
+ DefineProperty("lexer.cpp.track.preprocessor", &OptionsCPP::trackPreprocessor,
+ "Set to 1 to interpret #if/#else/#endif to grey out code that is not active.");
+
+ DefineProperty("lexer.cpp.update.preprocessor", &OptionsCPP::updatePreprocessor,
+ "Set to 1 to update preprocessor definitions when #define found.");
+
+ DefineProperty("fold.comment", &OptionsCPP::foldComment,
+ "This option enables folding multi-line comments and explicit fold points when using the C++ lexer. "
+ "Explicit fold points allows adding extra folding by placing a //{ comment at the start and a //} "
+ "at the end of a section that should fold.");
+
+ DefineProperty("fold.preprocessor", &OptionsCPP::foldPreprocessor,
+ "This option enables folding preprocessor directives when using the C++ lexer. "
+ "Includes C#'s explicit #region and #endregion folding directives.");
+
+ DefineProperty("fold.compact", &OptionsCPP::foldCompact);
+
+ DefineProperty("fold.at.else", &OptionsCPP::foldAtElse,
+ "This option enables C++ folding on a \"} else {\" line of an if statement.");
+
+ DefineWordListSets(cppWordLists);
+ }
+};
+
+class LexerCPP : public ILexer {
+ bool caseSensitive;
+ CharacterSet setWord;
+ CharacterSet setNegationOp;
+ CharacterSet setArithmethicOp;
+ CharacterSet setRelOp;
+ CharacterSet setLogicalOp;
+ PPStates vlls;
+ std::vector<PPDefinition> ppDefineHistory;
+ PropSetSimple props;
+ WordList keywords;
+ WordList keywords2;
+ WordList keywords3;
+ WordList keywords4;
+ WordList ppDefinitions;
+ std::map<std::string, std::string> preprocessorDefinitionsStart;
+ OptionsCPP options;
+ OptionSetCPP osCPP;
+public:
+ LexerCPP(bool caseSensitive_) :
+ caseSensitive(caseSensitive_),
+ setWord(CharacterSet::setAlphaNum, "._", 0x80, true),
+ setNegationOp(CharacterSet::setNone, "!"),
+ setArithmethicOp(CharacterSet::setNone, "+-/*%"),
+ setRelOp(CharacterSet::setNone, "=!<>"),
+ setLogicalOp(CharacterSet::setNone, "|&") {
+ }
+ ~LexerCPP() {
+ }
+ void SCI_METHOD Release() {
+ delete this;
+ }
+ int SCI_METHOD Version() const {
+ return lvOriginal;
+ }
+ const char * SCI_METHOD PropertyNames() {
+ return osCPP.PropertyNames();
+ }
+ int SCI_METHOD PropertyType(const char *name) {
+ return osCPP.PropertyType(name);
+ }
+ const char * SCI_METHOD DescribeProperty(const char *name) {
+ return osCPP.DescribeProperty(name);
+ }
+ int SCI_METHOD PropertySet(const char *key, const char *val);
+ const char * SCI_METHOD DescribeWordListSets() {
+ return osCPP.DescribeWordListSets();
+ }
+ int SCI_METHOD WordListSet(int n, const char *wl);
+ void SCI_METHOD Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess);
+ void SCI_METHOD Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess);
+
+ void * SCI_METHOD PrivateCall(int, void *) {
+ return 0;
+ }
+
+ static ILexer *LexerFactoryCPP() {
+ return new LexerCPP(true);
+ }
+ static ILexer *LexerFactoryCPPInsensitive() {
+ return new LexerCPP(false);
+ }
+
+ void EvaluateTokens(std::vector<std::string> &tokens);
+ bool EvaluateExpression(const std::string &expr, const std::map<std::string, std::string> &preprocessorDefinitions);
+};
+
+int SCI_METHOD LexerCPP::PropertySet(const char *key, const char *val) {
+ if (osCPP.PropertySet(&options, key, val)) {
+ return 0;
+ }
+ return -1;
+}
+
+int SCI_METHOD LexerCPP::WordListSet(int n, const char *wl) {
+ WordList *wordListN = 0;
+ switch (n) {
+ case 0:
+ wordListN = &keywords;
+ break;
+ case 1:
+ wordListN = &keywords2;
+ break;
+ case 2:
+ wordListN = &keywords3;
+ break;
+ case 3:
+ wordListN = &keywords4;
+ break;
+ case 4:
+ wordListN = &ppDefinitions;
+ break;
+ }
+ int firstModification = -1;
+ if (wordListN) {
+ WordList wlNew;
+ wlNew.Set(wl);
+ if (*wordListN != wlNew) {
+ wordListN->Set(wl);
+ firstModification = 0;
+ if (n == 4) {
+ // Rebuild preprocessorDefinitions
+ preprocessorDefinitionsStart.clear();
+ for (int nDefinition = 0; nDefinition < ppDefinitions.len; nDefinition++) {
+ char *cpDefinition = ppDefinitions.words[nDefinition];
+ char *cpEquals = strchr(cpDefinition, '=');
+ if (cpEquals) {
+ std::string name(cpDefinition, cpEquals - cpDefinition);
+ std::string val(cpEquals+1);
+ preprocessorDefinitionsStart[name] = val;
+ } else {
+ std::string name(cpDefinition);
+ std::string val("1");
+ preprocessorDefinitionsStart[name] = val;
+ }
+ }
+ }
+ }
+ }
+ return firstModification;
+}
- WordList &keywords = *keywordlists[0];
- WordList &keywords2 = *keywordlists[1];
- WordList &keywords3 = *keywordlists[2];
- WordList &keywords4 = *keywordlists[3];
+// Functor used to truncate history
+struct After {
+ int line;
+ After(int line_) : line(line_) {}
+ bool operator() (PPDefinition &p) const {
+ return p.line > line;
+ }
+};
- // property styling.within.preprocessor
- // For C++ code, determines whether all preprocessor code is styled in the preprocessor style (0, the default)
- // or only from the initial # to the end of the command word(1).
- bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;
+void SCI_METHOD LexerCPP::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
+ LexAccessor styler(pAccess);
CharacterSet setOKBeforeRE(CharacterSet::setNone, "([{=,:;!%^&*|?~+-");
CharacterSet setCouldBePostOp(CharacterSet::setNone, "+-");
@@ -71,9 +391,7 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);
CharacterSet setWord(CharacterSet::setAlphaNum, "._", 0x80, true);
- // property lexer.cpp.allow.dollars
- // Set to 0 to disallow the '$' character in identifiers with the cpp lexer.
- if (styler.GetPropertyInt("lexer.cpp.allow.dollars", 1) != 0) {
+ if (options.identifiersAllowDollars) {
setWordStart.Add('$');
setWord.Add('$');
}
@@ -85,9 +403,9 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
bool continuationLine = false;
bool isIncludePreprocessor = false;
+ int lineCurrent = styler.GetLine(startPos);
if (initStyle == SCE_C_PREPROCESSOR) {
// Set continuationLine if last character of previous line is '\'
- int lineCurrent = styler.GetLine(startPos);
if (lineCurrent > 0) {
int chBack = styler.SafeGetCharAt(startPos-1, 0);
int chBack2 = styler.SafeGetCharAt(startPos-2, 0);
@@ -111,7 +429,30 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
}
}
- StyleContext sc(startPos, length, initStyle, styler);
+ StyleContext sc(startPos, length, initStyle, styler, 0x7f);
+ LinePPState preproc = vlls.ForLine(lineCurrent);
+
+ bool definitionsChanged = false;
+
+ // Truncate ppDefineHistory before current line
+
+ if (!options.updatePreprocessor)
+ ppDefineHistory.clear();
+
+ std::vector<PPDefinition>::iterator itInvalid = std::find_if(ppDefineHistory.begin(), ppDefineHistory.end(), After(lineCurrent-1));
+ if (itInvalid != ppDefineHistory.end()) {
+ ppDefineHistory.erase(itInvalid, ppDefineHistory.end());
+ definitionsChanged = true;
+ }
+
+ std::map<std::string, std::string> preprocessorDefinitions = preprocessorDefinitionsStart;
+ for (std::vector<PPDefinition>::iterator itDef = ppDefineHistory.begin(); itDef != ppDefineHistory.end(); ++itDef) {
+ preprocessorDefinitions[itDef->key] = itDef->value;
+ }
+
+ const int maskActivity = 0x3F;
+
+ int activitySet = preproc.IsInactive() ? 0x40 : 0;
for (; sc.More(); sc.Forward()) {
@@ -126,6 +467,22 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
visibleChars = 0;
lastWordWasUUID = false;
isIncludePreprocessor = false;
+ if (preproc.IsInactive()) {
+ activitySet = 0x40;
+ sc.SetState(sc.state | activitySet);
+ }
+ if (activitySet) {
+ if (sc.ch == '#') {
+ if (sc.Match("#else") || sc.Match("#end") || sc.Match("#if")) {
+ //activitySet = 0;
+ }
+ }
+ }
+ }
+
+ if (sc.atLineEnd) {
+ lineCurrent++;
+ vlls.Add(lineCurrent, preproc);
}
// Handle line continuation generically.
@@ -141,14 +498,14 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
}
// Determine if the current state should terminate.
- switch (sc.state) {
+ switch (sc.state & maskActivity) {
case SCE_C_OPERATOR:
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
break;
case SCE_C_NUMBER:
// We accept almost anything because of hex. and number suffixes
if (!setWord.Contains(sc.ch)) {
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
}
break;
case SCE_C_IDENTIFIER:
@@ -161,59 +518,59 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
}
if (keywords.InList(s)) {
lastWordWasUUID = strcmp(s, "uuid") == 0;
- sc.ChangeState(SCE_C_WORD);
+ sc.ChangeState(SCE_C_WORD|activitySet);
} else if (keywords2.InList(s)) {
- sc.ChangeState(SCE_C_WORD2);
+ sc.ChangeState(SCE_C_WORD2|activitySet);
} else if (keywords4.InList(s)) {
- sc.ChangeState(SCE_C_GLOBALCLASS);
+ sc.ChangeState(SCE_C_GLOBALCLASS|activitySet);
}
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
}
break;
case SCE_C_PREPROCESSOR:
if (sc.atLineStart && !continuationLine) {
- sc.SetState(SCE_C_DEFAULT);
- } else if (stylingWithinPreprocessor) {
+ sc.SetState(SCE_C_DEFAULT|activitySet);
+ } else if (options.stylingWithinPreprocessor) {
if (IsASpace(sc.ch)) {
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
}
} else {
if (sc.Match('/', '*') || sc.Match('/', '/')) {
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
}
}
break;
case SCE_C_COMMENT:
if (sc.Match('*', '/')) {
sc.Forward();
- sc.ForwardSetState(SCE_C_DEFAULT);
+ sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
}
break;
case SCE_C_COMMENTDOC:
if (sc.Match('*', '/')) {
sc.Forward();
- sc.ForwardSetState(SCE_C_DEFAULT);
+ sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
} else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
// Verify that we have the conditions to mark a comment-doc-keyword
if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
styleBeforeDCKeyword = SCE_C_COMMENTDOC;
- sc.SetState(SCE_C_COMMENTDOCKEYWORD);
+ sc.SetState(SCE_C_COMMENTDOCKEYWORD|activitySet);
}
}
break;
case SCE_C_COMMENTLINE:
if (sc.atLineStart) {
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
}
break;
case SCE_C_COMMENTLINEDOC:
if (sc.atLineStart) {
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
} else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
// Verify that we have the conditions to mark a comment-doc-keyword
if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) {
styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC;
- sc.SetState(SCE_C_COMMENTDOCKEYWORD);
+ sc.SetState(SCE_C_COMMENTDOCKEYWORD|activitySet);
}
}
break;
@@ -221,7 +578,7 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) {
sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
sc.Forward();
- sc.ForwardSetState(SCE_C_DEFAULT);
+ sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
} else if (!setDoxygen.Contains(sc.ch)) {
char s[100];
if (caseSensitive) {
@@ -230,17 +587,17 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
sc.GetCurrentLowered(s, sizeof(s));
}
if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {
- sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
+ sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR|activitySet);
}
sc.SetState(styleBeforeDCKeyword);
}
break;
case SCE_C_STRING:
if (sc.atLineEnd) {
- sc.ChangeState(SCE_C_STRINGEOL);
+ sc.ChangeState(SCE_C_STRINGEOL|activitySet);
} else if (isIncludePreprocessor) {
if (sc.ch == '>') {
- sc.ForwardSetState(SCE_C_DEFAULT);
+ sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
isIncludePreprocessor = false;
}
} else if (sc.ch == '\\') {
@@ -248,28 +605,28 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
sc.Forward();
}
} else if (sc.ch == '\"') {
- sc.ForwardSetState(SCE_C_DEFAULT);
+ sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
}
break;
case SCE_C_CHARACTER:
if (sc.atLineEnd) {
- sc.ChangeState(SCE_C_STRINGEOL);
+ sc.ChangeState(SCE_C_STRINGEOL|activitySet);
} else if (sc.ch == '\\') {
if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
sc.Forward();
}
} else if (sc.ch == '\'') {
- sc.ForwardSetState(SCE_C_DEFAULT);
+ sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
}
break;
case SCE_C_REGEX:
if (sc.atLineStart) {
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
} else if (sc.ch == '/') {
sc.Forward();
while ((sc.ch < 0x80) && islower(sc.ch))
sc.Forward(); // gobble regex flags
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
} else if (sc.ch == '\\') {
// Gobble up the quoted character
if (sc.chNext == '\\' || sc.chNext == '/') {
@@ -279,7 +636,7 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
break;
case SCE_C_STRINGEOL:
if (sc.atLineStart) {
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
}
break;
case SCE_C_VERBATIM:
@@ -287,72 +644,138 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
if (sc.chNext == '\"') {
sc.Forward();
} else {
- sc.ForwardSetState(SCE_C_DEFAULT);
+ sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
}
}
break;
case SCE_C_UUID:
if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
}
}
// Determine if a new state should be entered.
- if (sc.state == SCE_C_DEFAULT) {
+ if ((sc.state & maskActivity) == SCE_C_DEFAULT) {
if (sc.Match('@', '\"')) {
- sc.SetState(SCE_C_VERBATIM);
+ sc.SetState(SCE_C_VERBATIM|activitySet);
sc.Forward();
} else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
if (lastWordWasUUID) {
- sc.SetState(SCE_C_UUID);
+ sc.SetState(SCE_C_UUID|activitySet);
lastWordWasUUID = false;
} else {
- sc.SetState(SCE_C_NUMBER);
+ sc.SetState(SCE_C_NUMBER|activitySet);
}
} else if (setWordStart.Contains(sc.ch) || (sc.ch == '@')) {
if (lastWordWasUUID) {
- sc.SetState(SCE_C_UUID);
+ sc.SetState(SCE_C_UUID|activitySet);
lastWordWasUUID = false;
} else {
- sc.SetState(SCE_C_IDENTIFIER);
+ sc.SetState(SCE_C_IDENTIFIER|activitySet);
}
} else if (sc.Match('/', '*')) {
if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
- sc.SetState(SCE_C_COMMENTDOC);
+ sc.SetState(SCE_C_COMMENTDOC|activitySet);
} else {
- sc.SetState(SCE_C_COMMENT);
+ sc.SetState(SCE_C_COMMENT|activitySet);
}
sc.Forward(); // Eat the * so it isn't used for the end of the comment
} else if (sc.Match('/', '/')) {
if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!"))
// Support of Qt/Doxygen doc. style
- sc.SetState(SCE_C_COMMENTLINEDOC);
+ sc.SetState(SCE_C_COMMENTLINEDOC|activitySet);
else
- sc.SetState(SCE_C_COMMENTLINE);
+ sc.SetState(SCE_C_COMMENTLINE|activitySet);
} else if (sc.ch == '/' && setOKBeforeRE.Contains(chPrevNonWhite) &&
(!setCouldBePostOp.Contains(chPrevNonWhite) || !FollowsPostfixOperator(sc, styler))) {
- sc.SetState(SCE_C_REGEX); // JavaScript's RegEx
+ sc.SetState(SCE_C_REGEX|activitySet); // JavaScript's RegEx
} else if (sc.ch == '\"') {
- sc.SetState(SCE_C_STRING);
+ sc.SetState(SCE_C_STRING|activitySet);
isIncludePreprocessor = false; // ensure that '>' won't end the string
} else if (isIncludePreprocessor && sc.ch == '<') {
- sc.SetState(SCE_C_STRING);
+ sc.SetState(SCE_C_STRING|activitySet);
} else if (sc.ch == '\'') {
- sc.SetState(SCE_C_CHARACTER);
+ sc.SetState(SCE_C_CHARACTER|activitySet);
} else if (sc.ch == '#' && visibleChars == 0) {
// Preprocessor commands are alone on their line
- sc.SetState(SCE_C_PREPROCESSOR);
+ sc.SetState(SCE_C_PREPROCESSOR|activitySet);
// Skip whitespace between # and preprocessor word
do {
sc.Forward();
} while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
if (sc.atLineEnd) {
- sc.SetState(SCE_C_DEFAULT);
+ sc.SetState(SCE_C_DEFAULT|activitySet);
} else if (sc.Match("include")) {
isIncludePreprocessor = true;
+ } else {
+ if (options.trackPreprocessor) {
+ if (sc.Match("ifdef") || sc.Match("ifndef")) {
+ bool isIfDef = sc.Match("ifdef");
+ int i = isIfDef ? 5 : 6;
+ std::string restOfLine = GetRestOfLine(styler, sc.currentPos + i + 1, false);
+ bool foundDef = preprocessorDefinitions.find(restOfLine) != preprocessorDefinitions.end();
+ preproc.StartSection(isIfDef == foundDef);
+ } else if (sc.Match("if")) {
+ std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 2, true);
+ bool ifGood = EvaluateExpression(restOfLine, preprocessorDefinitions);
+ preproc.StartSection(ifGood);
+ } else if (sc.Match("else")) {
+ if (!preproc.CurrentIfTaken()) {
+ preproc.InvertCurrentLevel();
+ activitySet = preproc.IsInactive() ? 0x40 : 0;
+ if (!activitySet)
+ sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
+ } else if (!preproc.IsInactive()) {
+ preproc.InvertCurrentLevel();
+ activitySet = preproc.IsInactive() ? 0x40 : 0;
+ if (!activitySet)
+ sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
+ }
+ } else if (sc.Match("elif")) {
+ // Ensure only one chosen out of #if .. #elif .. #elif .. #else .. #endif
+ if (!preproc.CurrentIfTaken()) {
+ // Similar to #if
+ std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 2, true);
+ bool ifGood = EvaluateExpression(restOfLine, preprocessorDefinitions);
+ if (ifGood) {
+ preproc.InvertCurrentLevel();
+ activitySet = preproc.IsInactive() ? 0x40 : 0;
+ if (!activitySet)
+ sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
+ }
+ } else if (!preproc.IsInactive()) {
+ preproc.InvertCurrentLevel();
+ activitySet = preproc.IsInactive() ? 0x40 : 0;
+ if (!activitySet)
+ sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
+ }
+ } else if (sc.Match("endif")) {
+ preproc.EndSection();
+ activitySet = preproc.IsInactive() ? 0x40 : 0;
+ sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
+ } else if (sc.Match("define")) {
+ if (options.updatePreprocessor && !preproc.IsInactive()) {
+ std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 6, true);
+ if (restOfLine.find(")") == std::string::npos) { // Don't handle macros with arguments
+ std::vector<std::string> tokens = Tokenize(restOfLine);
+ std::string key;
+ std::string value("1");
+ if (tokens.size() >= 1) {
+ key = tokens[0];
+ if (tokens.size() >= 2) {
+ value = tokens[1];
+ }
+ preprocessorDefinitions[key] = value;
+ ppDefineHistory.push_back(PPDefinition(lineCurrent, key, value));
+ definitionsChanged = true;
+ }
+ }
+ }
+ }
+ }
}
} else if (isoperator(static_cast<char>(sc.ch))) {
- sc.SetState(SCE_C_OPERATOR);
+ sc.SetState(SCE_C_OPERATOR|activitySet);
}
}
@@ -362,38 +785,19 @@ static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, Wo
}
continuationLine = false;
}
+ if (definitionsChanged)
+ styler.ChangeLexerState(startPos, startPos + length);
sc.Complete();
-}
-
-static bool IsStreamCommentStyle(int style) {
- return style == SCE_C_COMMENT ||
- style == SCE_C_COMMENTDOC ||
- style == SCE_C_COMMENTDOCKEYWORD ||
- style == SCE_C_COMMENTDOCKEYWORDERROR;
+ styler.Flush();
}
// Store both the current line's fold level and the next lines in the
// level store to make it easy to pick up with each increment
// and to make it possible to fiddle the current level for "} else {".
-static void FoldCppDoc(unsigned int startPos, int length, int initStyle,
- WordList *[], Accessor &styler) {
-
- // property fold.comment
- // This option enables folding multi-line comments and explicit fold points when using the C++ lexer.
- // Explicit fold points allows adding extra folding by placing a //{ comment at the start and a //}
- // at the end of a section that should fold.
- bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
-
- // property fold.preprocessor
- // This option enables folding preprocessor directives when using the C++ lexer.
- // Includes C#'s explicit #region and #endregion folding directives.
- bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
- bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+void SCI_METHOD LexerCPP::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
- // property fold.at.else
- // This option enables C++ folding on a "} else {" line of an if statement.
- bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
+ LexAccessor styler(pAccess);
unsigned int endPos = startPos + length;
int visibleChars = 0;
@@ -413,7 +817,7 @@ static void FoldCppDoc(unsigned int startPos, int length, int initStyle,
style = styleNext;
styleNext = styler.StyleAt(i + 1);
bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
- if (foldComment && IsStreamCommentStyle(style)) {
+ if (options.foldComment && IsStreamCommentStyle(style)) {
if (!IsStreamCommentStyle(stylePrev) && (stylePrev != SCE_C_COMMENTLINEDOC)) {
levelNext++;
} else if (!IsStreamCommentStyle(styleNext) && (styleNext != SCE_C_COMMENTLINEDOC) && !atEOL) {
@@ -421,7 +825,7 @@ static void FoldCppDoc(unsigned int startPos, int length, int initStyle,
levelNext--;
}
}
- if (foldComment && (style == SCE_C_COMMENTLINE)) {
+ if (options.foldComment && (style == SCE_C_COMMENTLINE)) {
if ((ch == '/') && (chNext == '/')) {
char chNext2 = styler.SafeGetCharAt(i + 2);
if (chNext2 == '{') {
@@ -431,7 +835,7 @@ static void FoldCppDoc(unsigned int startPos, int length, int initStyle,
}
}
}
- if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
+ if (options.foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
if (ch == '#') {
unsigned int j = i + 1;
while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
@@ -460,11 +864,11 @@ static void FoldCppDoc(unsigned int startPos, int length, int initStyle,
visibleChars++;
if (atEOL || (i == endPos-1)) {
int levelUse = levelCurrent;
- if (foldAtElse) {
+ if (options.foldAtElse) {
levelUse = levelMinCurrent;
}
int lev = levelUse | levelNext << 16;
- if (visibleChars == 0 && foldCompact)
+ if (visibleChars == 0 && options.foldCompact)
lev |= SC_FOLDLEVELWHITEFLAG;
if (levelUse < levelNext)
lev |= SC_FOLDLEVELHEADERFLAG;
@@ -483,24 +887,154 @@ static void FoldCppDoc(unsigned int startPos, int length, int initStyle,
}
}
-static const char *const cppWordLists[] = {
- "Primary keywords and identifiers",
- "Secondary keywords and identifiers",
- "Documentation comment keywords",
- "Unused",
- "Global classes and typedefs",
- 0,
-};
+void LexerCPP::EvaluateTokens(std::vector<std::string> &tokens) {
+
+ // Evaluate defined() statements to either 0 or 1
+ for (size_t i=0; (i+2)<tokens.size();) {
+ if ((tokens[i] == "defined") && (tokens[i+1] == "(")) {
+ const char *val = "0";
+ if (tokens[i+2] == ")") {
+ // defined()
+ tokens.erase(tokens.begin() + i + 1, tokens.begin() + i + 3);
+ } else if (((i+2)<tokens.size()) && (tokens[i+3] == ")")) {
+ // defined(<int>)
+ tokens.erase(tokens.begin() + i + 1, tokens.begin() + i + 4);
+ val = "1";
+ }
+ tokens[i] = val;
+ } else {
+ i++;
+ }
+ }
+
+ // Find bracketed subexpressions and recurse on them
+ std::vector<std::string>::iterator itBracket = std::find(tokens.begin(), tokens.end(), "(");
+ std::vector<std::string>::iterator itEndBracket = std::find(tokens.begin(), tokens.end(), ")");
+ while ((itBracket != tokens.end()) && (itEndBracket != tokens.end()) && (itEndBracket > itBracket)) {
+ std::vector<std::string> inBracket(itBracket + 1, itEndBracket);
+ EvaluateTokens(inBracket);
-static void ColouriseCppDocSensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
- Accessor &styler) {
- ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, true);
+ // The insertion is done before the removal because there were failures with the opposite approach
+ tokens.insert(itBracket, inBracket.begin(), inBracket.end());
+ itBracket = std::find(tokens.begin(), tokens.end(), "(");
+ itEndBracket = std::find(tokens.begin(), tokens.end(), ")");
+ tokens.erase(itBracket, itEndBracket + 1);
+
+ itBracket = std::find(tokens.begin(), tokens.end(), "(");
+ itEndBracket = std::find(tokens.begin(), tokens.end(), ")");
+ }
+
+ // Evaluate logical negations
+ for (size_t j=0; (j+1)<tokens.size();) {
+ if (setNegationOp.Contains(tokens[j][0])) {
+ int isTrue = atoi(tokens[j+1].c_str());
+ if (tokens[j] == "!")
+ isTrue = !isTrue;
+ std::vector<std::string>::iterator itInsert =
+ tokens.erase(tokens.begin() + j, tokens.begin() + j + 2);
+ tokens.insert(itInsert, isTrue ? "1" : "0");
+ } else {
+ j++;
+ }
+ }
+
+ // Evaluate expressions in precedence order
+ enum precedence { precArithmetic, precRelative, precLogical };
+ for (int prec=precArithmetic; prec <= precLogical; prec++) {
+ // Looking at 3 tokens at a time so end at 2 before end
+ for (size_t k=0; (k+2)<tokens.size();) {
+ char chOp = tokens[k+1][0];
+ if (
+ ((prec==precArithmetic) && setArithmethicOp.Contains(chOp)) ||
+ ((prec==precRelative) && setRelOp.Contains(chOp)) ||
+ ((prec==precLogical) && setLogicalOp.Contains(chOp))
+ ) {
+ int valA = atoi(tokens[k].c_str());
+ int valB = atoi(tokens[k+2].c_str());
+ int result = 0;
+ if (tokens[k+1] == "+")
+ result = valA + valB;
+ else if (tokens[k+1] == "-")
+ result = valA - valB;
+ else if (tokens[k+1] == "*")
+ result = valA * valB;
+ else if (tokens[k+1] == "/")
+ result = valA / valB;
+ else if (tokens[k+1] == "%")
+ result = valA % valB;
+ else if (tokens[k+1] == "<")
+ result = valA < valB;
+ else if (tokens[k+1] == "<=")
+ result = valA <= valB;
+ else if (tokens[k+1] == ">")
+ result = valA > valB;
+ else if (tokens[k+1] == ">=")
+ result = valA >= valB;
+ else if (tokens[k+1] == "==")
+ result = valA == valB;
+ else if (tokens[k+1] == "!=")
+ result = valA != valB;
+ else if (tokens[k+1] == "||")
+ result = valA || valB;
+ else if (tokens[k+1] == "&&")
+ result = valA && valB;
+ char sResult[30];
+ sprintf(sResult, "%d", result);
+ std::vector<std::string>::iterator itInsert =
+ tokens.erase(tokens.begin() + k, tokens.begin() + k + 3);
+ tokens.insert(itInsert, sResult);
+ } else {
+ k++;
+ }
+ }
+ }
}
-static void ColouriseCppDocInsensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
- Accessor &styler) {
- ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, false);
+bool LexerCPP::EvaluateExpression(const std::string &expr, const std::map<std::string, std::string> &preprocessorDefinitions) {
+ // Break into tokens, replacing with definitions
+ std::string word;
+ std::vector<std::string> tokens;
+ const char *cp = expr.c_str();
+ for (;;) {
+ if (setWord.Contains(*cp)) {
+ word += *cp;
+ } else {
+ std::map<std::string, std::string>::const_iterator it = preprocessorDefinitions.find(word);
+ if (it != preprocessorDefinitions.end()) {
+ tokens.push_back(it->second);
+ } else if (!word.empty() && ((word[0] >= '0' && word[0] <= '9') || (word == "defined"))) {
+ tokens.push_back(word);
+ }
+ word = "";
+ if (!*cp) {
+ break;
+ }
+ if ((*cp != ' ') && (*cp != '\t')) {
+ std::string op(cp, 1);
+ if (setRelOp.Contains(*cp)) {
+ if (setRelOp.Contains(cp[1])) {
+ op += cp[1];
+ cp++;
+ }
+ } else if (setLogicalOp.Contains(*cp)) {
+ if (setLogicalOp.Contains(cp[1])) {
+ op += cp[1];
+ cp++;
+ }
+ }
+ tokens.push_back(op);
+ }
+ }
+ cp++;
+ }
+
+ EvaluateTokens(tokens);
+
+ // "0" or "" -> false else true
+ bool isFalse = tokens.empty() ||
+ ((tokens.size() == 1) && ((tokens[0] == "") || tokens[0] == "0"));
+ return !isFalse;
}
-LexerModule lmCPP(SCLEX_CPP, ColouriseCppDocSensitive, "cpp", FoldCppDoc, cppWordLists);
-LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, ColouriseCppDocInsensitive, "cppnocase", FoldCppDoc, cppWordLists);
+LexerModule lmCPP(SCLEX_CPP, LexerCPP::LexerFactoryCPP, "cpp", cppWordLists);
+LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, LexerCPP::LexerFactoryCPPInsensitive, "cppnocase", cppWordLists);