diff options
author | nyamatongwe <devnull@localhost> | 2012-09-19 22:17:13 +1000 |
---|---|---|
committer | nyamatongwe <devnull@localhost> | 2012-09-19 22:17:13 +1000 |
commit | dea2c5b9bbbce473bc8baa886eddf4f7019d39f3 (patch) | |
tree | 76197f0a20ead6ac7951ead7dab063f6223f0e99 | |
parent | 3cc4d170eb5201c72318fa3e4b319c2e37c3c170 (diff) | |
download | scintilla-mirror-dea2c5b9bbbce473bc8baa886eddf4f7019d39f3.tar.gz |
Handle nested delimiter pairs. Feature #3569352.
Bugs #1515556, #3008483, #3512208, #3515392.
From Kein-Hong Man.
-rw-r--r-- | lexers/LexBash.cxx | 131 |
1 files changed, 124 insertions, 7 deletions
diff --git a/lexers/LexBash.cxx b/lexers/LexBash.cxx index b9d14ce4f..2dc8707e5 100644 --- a/lexers/LexBash.cxx +++ b/lexers/LexBash.cxx @@ -49,6 +49,17 @@ using namespace Scintilla; #define BASH_CMD_ARITH 4 #define BASH_CMD_DELIM 5 +// state constants for nested delimiter pairs, used by +// SCE_SH_STRING and SCE_SH_BACKTICKS processing +#define BASH_DELIM_LITERAL 0 +#define BASH_DELIM_STRING 1 +#define BASH_DELIM_CSTRING 2 +#define BASH_DELIM_LSTRING 3 +#define BASH_DELIM_COMMAND 4 +#define BASH_DELIM_BACKTICK 5 + +#define BASH_DELIM_STACK_MAX 7 + static inline int translateBashDigit(int ch) { if (ch >= '0' && ch <= '9') { return ch - '0'; @@ -154,6 +165,60 @@ static void ColouriseBashDoc(unsigned int startPos, int length, int initStyle, }; QuoteCls Quote; + class QuoteStackCls { // Class to manage quote pairs that nest + public: + int Count; + int Up, Down; + int Style; + int Depth; // levels pushed + int *CountStack; + int *UpStack; + int *StyleStack; + QuoteStackCls() { + Count = 0; + Up = '\0'; + Down = '\0'; + Style = 0; + Depth = 0; + CountStack = new int[BASH_DELIM_STACK_MAX]; + UpStack = new int[BASH_DELIM_STACK_MAX]; + StyleStack = new int[BASH_DELIM_STACK_MAX]; + } + void Start(int u, int s) { + Count = 1; + Up = u; + Down = opposite(Up); + Style = s; + } + void Push(int u, int s) { + if (Depth >= BASH_DELIM_STACK_MAX) + return; + CountStack[Depth] = Count; + UpStack [Depth] = Up; + StyleStack[Depth] = Style; + Depth++; + Count = 1; + Up = u; + Down = opposite(Up); + Style = s; + } + void Pop(void) { + if (Depth <= 0) + return; + Depth--; + Count = CountStack[Depth]; + Up = UpStack [Depth]; + Style = StyleStack[Depth]; + Down = opposite(Up); + } + ~QuoteStackCls() { + delete []CountStack; + delete []UpStack; + delete []StyleStack; + } + }; + QuoteStackCls QuoteStack; + int numBase = 0; int digit; unsigned int endPos = startPos + length; @@ -429,9 +494,56 @@ static void ColouriseBashDoc(unsigned int startPos, int length, int initStyle, } } break; - case SCE_SH_STRING: // delimited styles + case SCE_SH_STRING: // delimited styles, can nest case SCE_SH_BACKTICKS: - case SCE_SH_PARAM: + if (sc.ch == '\\' && QuoteStack.Up != '\\') { + if (QuoteStack.Style != BASH_DELIM_LITERAL) + sc.Forward(); + } else if (sc.ch == QuoteStack.Down) { + QuoteStack.Count--; + if (QuoteStack.Count == 0) { + if (QuoteStack.Depth > 0) { + QuoteStack.Pop(); + } else + sc.ForwardSetState(SCE_SH_DEFAULT); + } + } else if (sc.ch == QuoteStack.Up) { + QuoteStack.Count++; + } else { + if (QuoteStack.Style == BASH_DELIM_STRING || + QuoteStack.Style == BASH_DELIM_LSTRING + ) { // do nesting for "string", $"locale-string" + if (sc.ch == '`') { + QuoteStack.Push(sc.ch, BASH_DELIM_BACKTICK); + } else if (sc.ch == '$' && sc.chNext == '(') { + sc.Forward(); + QuoteStack.Push(sc.ch, BASH_DELIM_COMMAND); + } + } else if (QuoteStack.Style == BASH_DELIM_COMMAND || + QuoteStack.Style == BASH_DELIM_BACKTICK + ) { // do nesting for $(command), `command` + if (sc.ch == '\'') { + QuoteStack.Push(sc.ch, BASH_DELIM_LITERAL); + } else if (sc.ch == '\"') { + QuoteStack.Push(sc.ch, BASH_DELIM_STRING); + } else if (sc.ch == '`') { + QuoteStack.Push(sc.ch, BASH_DELIM_BACKTICK); + } else if (sc.ch == '$') { + if (sc.chNext == '\'') { + sc.Forward(); + QuoteStack.Push(sc.ch, BASH_DELIM_CSTRING); + } else if (sc.chNext == '\"') { + sc.Forward(); + QuoteStack.Push(sc.ch, BASH_DELIM_LSTRING); + } else if (sc.chNext == '(') { + sc.Forward(); + QuoteStack.Push(sc.ch, BASH_DELIM_COMMAND); + } + } + } + } + break; + case SCE_SH_PARAM: // ${parameter} if (sc.ch == '\\' && Quote.Up != '\\') { sc.Forward(); } else if (sc.ch == Quote.Down) { @@ -508,13 +620,13 @@ static void ColouriseBashDoc(unsigned int startPos, int length, int initStyle, sc.SetState(SCE_SH_COMMENTLINE); } else if (sc.ch == '\"') { sc.SetState(SCE_SH_STRING); - Quote.Start(sc.ch); + QuoteStack.Start(sc.ch, BASH_DELIM_STRING); } else if (sc.ch == '\'') { sc.SetState(SCE_SH_CHARACTER); Quote.Start(sc.ch); } else if (sc.ch == '`') { sc.SetState(SCE_SH_BACKTICKS); - Quote.Start(sc.ch); + QuoteStack.Start(sc.ch, BASH_DELIM_BACKTICK); } else if (sc.ch == '$') { if (sc.Match("$((")) { sc.SetState(SCE_SH_OPERATOR); // handle '((' later @@ -524,17 +636,22 @@ static void ColouriseBashDoc(unsigned int startPos, int length, int initStyle, sc.Forward(); if (sc.ch == '{') { sc.ChangeState(SCE_SH_PARAM); + Quote.Start(sc.ch); } else if (sc.ch == '\'') { sc.ChangeState(SCE_SH_STRING); + QuoteStack.Start(sc.ch, BASH_DELIM_CSTRING); } else if (sc.ch == '"') { sc.ChangeState(SCE_SH_STRING); - } else if (sc.ch == '(' || sc.ch == '`') { + QuoteStack.Start(sc.ch, BASH_DELIM_LSTRING); + } else if (sc.ch == '(') { sc.ChangeState(SCE_SH_BACKTICKS); + QuoteStack.Start(sc.ch, BASH_DELIM_COMMAND); + } else if (sc.ch == '`') { // $` seen in a configure script, valid? + sc.ChangeState(SCE_SH_BACKTICKS); + QuoteStack.Start(sc.ch, BASH_DELIM_BACKTICK); } else { continue; // scalar has no delimiter pair } - // fallthrough, open delim for $[{'"(`] - Quote.Start(sc.ch); } else if (sc.Match('<', '<')) { sc.SetState(SCE_SH_HERE_DELIM); HereDoc.State = 0; |