aboutsummaryrefslogtreecommitdiffhomepage
path: root/lexers/LexBash.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'lexers/LexBash.cxx')
-rw-r--r--lexers/LexBash.cxx131
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;