diff --git a/src/highlight.cpp b/src/highlight.cpp index 5e75a6192..e83c7908d 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -511,20 +511,18 @@ static size_t color_variable(const wchar_t *in, size_t in_len, // Handle a slice, up to dollar_count of them. Note that we currently don't do any validation of // the slice's contents, e.g. $foo[blah] will not show an error even though it's invalid. - for (size_t slice_count = 0; slice_count < dollar_count && in[idx] == L'['; slice_count++) { - const wchar_t *slice_begin = nullptr, *slice_end = nullptr; - int located = parse_util_locate_slice(in + idx, &slice_begin, &slice_end, false); - if (located == 1) { - size_t slice_begin_idx = slice_begin - in, slice_end_idx = slice_end - in; - assert(slice_end_idx > slice_begin_idx); - colors[slice_begin_idx] = highlight_role_t::operat; - colors[slice_end_idx] = highlight_role_t::operat; - idx = slice_end_idx + 1; - } else if (located == 0) { + for (size_t slice_count = 0; slice_count < dollar_count; slice_count++) { + long slice_len = parse_util_slice_length(in + idx); + if (slice_len > 0) { + size_t slice_ulen = static_cast(slice_len); + colors[idx] = highlight_role_t::operat; + colors[idx + slice_ulen - 1] = highlight_role_t::operat; + idx += slice_ulen; + } else if (slice_len == 0) { // not a slice break; } else { - assert(located < 0); + assert(slice_len < 0); // Syntax error. Normally the entire token is colored red for us, but inside a // double-quoted string that doesn't happen. As such, color the variable + the slice // start red. Coloring any more than that looks bad, unless we're willing to try and diff --git a/src/parse_util.cpp b/src/parse_util.cpp index b21a36266..7746e1c35 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -208,10 +208,46 @@ static int parse_util_locate_brackets_of_type(const wchar_t *in, const wchar_t * return 1; } -int parse_util_locate_slice(const wchar_t *in, const wchar_t **begin, const wchar_t **end, - bool accept_incomplete) { - return parse_util_locate_brackets_of_type(in, begin, end, bracket_type_t::slice, - accept_incomplete); +long parse_util_slice_length(const wchar_t *in) { + assert(in && "null parameter"); + const wchar_t openc = L'['; + const wchar_t closec = L']'; + bool escaped = false; + + // Check for initial opening [ + if (*in != openc) return 0; + int bracket_count = 1; + + assert(in && "null parameter"); + for (const wchar_t *pos = in + 1; *pos; pos++) { + if (!escaped) { + if (*pos == L'\'' || *pos == L'"') { + const wchar_t *q_end = quote_end(pos, *pos); + if (q_end && *q_end) { + pos = q_end; + } else { + break; + } + } else { + if (*pos == openc) { + bracket_count++; + } else if (*pos == closec) { + bracket_count--; + if (bracket_count == 0) { + // pos points at the closing ], so add 1. + return pos - in + 1; + } + } + } + } + if (*pos == '\\') { + escaped = !escaped; + } else { + escaped = false; + } + } + assert(bracket_count > 0 && "Should have unclosed brackets"); + return -1; } static int parse_util_locate_brackets_range(const wcstring &str, size_t *inout_cursor_offset, diff --git a/src/parse_util.h b/src/parse_util.h index d446075e1..c290072a5 100644 --- a/src/parse_util.h +++ b/src/parse_util.h @@ -14,9 +14,10 @@ namespace ast { struct argument_t; } -/// Same as parse_util_locate_cmdsubst, but handles square brackets [ ]. -int parse_util_locate_slice(const wchar_t *in, const wchar_t **begin, const wchar_t **end, - bool accept_incomplete); +/// Handles slices: the square brackets in an expression like $foo[5..4] +/// \return the length of the slice starting at \p in, or 0 if there is no slice, or -1 on error. +/// This never accepts incomplete slices. +long parse_util_slice_length(const wchar_t *in); /// Alternative API. Iterate over command substitutions. ///