aboutsummaryrefslogtreecommitdiffhomepage
path: root/lexers/LexCoffeeScript.cxx
diff options
context:
space:
mode:
authorActiveState <unknown>2016-09-24 15:40:32 +1000
committerActiveState <unknown>2016-09-24 15:40:32 +1000
commit6f7b281e3f5ce743c78c41e51f7cd1c1ae40b3d9 (patch)
tree760ef7ac797f98be4b44e192dbb28bd720c280f7 /lexers/LexCoffeeScript.cxx
parenteadbd5b47a18280a54d6223aca2383cd04d6007a (diff)
downloadscintilla-mirror-6f7b281e3f5ce743c78c41e51f7cd1c1ae40b3d9.tar.gz
Bug [#1865]. Style interpolated code in strings for CoffeeScript.
Diffstat (limited to 'lexers/LexCoffeeScript.cxx')
-rw-r--r--lexers/LexCoffeeScript.cxx72
1 files changed, 71 insertions, 1 deletions
diff --git a/lexers/LexCoffeeScript.cxx b/lexers/LexCoffeeScript.cxx
index f32560016..63ee172e2 100644
--- a/lexers/LexCoffeeScript.cxx
+++ b/lexers/LexCoffeeScript.cxx
@@ -40,6 +40,36 @@ static bool IsSpaceEquiv(int state) {
|| state == SCE_COFFEESCRIPT_REGEX);
}
+// Store the current lexer state and brace count prior to starting a new
+// `#{}` interpolation level.
+// Based on LexRuby.cxx.
+static void enterInnerExpression(int *p_inner_string_types,
+ int *p_inner_expn_brace_counts,
+ int& inner_string_count,
+ int state,
+ int& brace_counts
+ ) {
+ p_inner_string_types[inner_string_count] = state;
+ p_inner_expn_brace_counts[inner_string_count] = brace_counts;
+ brace_counts = 0;
+ ++inner_string_count;
+}
+
+// Restore the lexer state and brace count for the previous `#{}` interpolation
+// level upon returning to it.
+// Note the previous lexer state is the return value and needs to be restored
+// manually by the StyleContext.
+// Based on LexRuby.cxx.
+static int exitInnerExpression(int *p_inner_string_types,
+ int *p_inner_expn_brace_counts,
+ int& inner_string_count,
+ int& brace_counts
+ ) {
+ --inner_string_count;
+ brace_counts = p_inner_expn_brace_counts[inner_string_count];
+ return p_inner_string_types[inner_string_count];
+}
+
// Preconditions: sc.currentPos points to a character after '+' or '-'.
// The test for pos reaching 0 should be redundant,
// and is in only for safety measures.
@@ -88,6 +118,27 @@ static void ColouriseCoffeeScriptDoc(Sci_PositionU startPos, Sci_Position length
int chPrevNonWhite = ' ';
int visibleChars = 0;
+ // String/Regex interpolation variables, based on LexRuby.cxx.
+ // In most cases a value of 2 should be ample for the code the user is
+ // likely to enter. For example,
+ // "Filling the #{container} with #{liquid}..."
+ // from the CoffeeScript homepage nests to a level of 2
+ // If the user actually hits a 6th occurrence of '#{' in a double-quoted
+ // string (including regexes), it will stay as a string. The problem with
+ // this is that quotes might flip, a 7th '#{' will look like a comment,
+ // and code-folding might be wrong.
+#define INNER_STRINGS_MAX_COUNT 5
+ // These vars track our instances of "...#{,,,'..#{,,,}...',,,}..."
+ int inner_string_types[INNER_STRINGS_MAX_COUNT];
+ // Track # braces when we push a new #{ thing
+ int inner_expn_brace_counts[INNER_STRINGS_MAX_COUNT];
+ int inner_string_count = 0;
+ int brace_counts = 0; // Number of #{ ... } things within an expression
+ for (int i = 0; i < INNER_STRINGS_MAX_COUNT; i++) {
+ inner_string_types[i] = 0;
+ inner_expn_brace_counts[i] = 0;
+ }
+
// look back to set chPrevNonWhite properly for better regex colouring
Sci_Position endPos = startPos + length;
if (startPos > 0 && IsSpaceEquiv(initStyle)) {
@@ -109,7 +160,7 @@ static void ColouriseCoffeeScriptDoc(Sci_PositionU startPos, Sci_Position length
StyleContext sc(startPos, endPos - startPos, initStyle, styler);
- for (; sc.More(); sc.Forward()) {
+ for (; sc.More();) {
if (sc.atLineStart) {
// Reset states to beginning of colourise so no surprises
@@ -164,6 +215,15 @@ static void ColouriseCoffeeScriptDoc(Sci_PositionU startPos, Sci_Position length
}
} else if (sc.ch == '\"') {
sc.ForwardSetState(SCE_COFFEESCRIPT_DEFAULT);
+ } else if (sc.ch == '#' && sc.chNext == '{' && inner_string_count < INNER_STRINGS_MAX_COUNT) {
+ // process interpolated code #{ ... }
+ enterInnerExpression(inner_string_types,
+ inner_expn_brace_counts,
+ inner_string_count,
+ sc.state,
+ brace_counts);
+ sc.SetState(SCE_COFFEESCRIPT_OPERATOR);
+ sc.ForwardSetState(SCE_COFFEESCRIPT_DEFAULT);
}
break;
case SCE_COFFEESCRIPT_CHARACTER:
@@ -255,6 +315,15 @@ static void ColouriseCoffeeScriptDoc(Sci_PositionU startPos, Sci_Position length
// Handle '..' and '...' operators correctly.
if (sc.ch == '.') {
for (int i = 0; i < 2 && sc.chNext == '.'; i++, sc.Forward()) ;
+ } else if (sc.ch == '{') {
+ ++brace_counts;
+ } else if (sc.ch == '}' && --brace_counts <= 0 && inner_string_count > 0) {
+ // Return to previous state before #{ ... }
+ sc.ForwardSetState(exitInnerExpression(inner_string_types,
+ inner_expn_brace_counts,
+ inner_string_count,
+ brace_counts));
+ continue; // skip sc.Forward() at loop end
}
}
}
@@ -263,6 +332,7 @@ static void ColouriseCoffeeScriptDoc(Sci_PositionU startPos, Sci_Position length
chPrevNonWhite = sc.ch;
visibleChars++;
}
+ sc.Forward();
}
sc.Complete();
}