Simplify slice parsing in highlighting

Factor out parsing of slices, which is only used for highlighting.
This commit is contained in:
ridiculousfish 2021-06-26 13:02:19 -07:00
parent a2dfd87928
commit 52c354a60f
3 changed files with 53 additions and 18 deletions

View File

@ -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<size_t>(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

View File

@ -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,

View File

@ -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.
///