Better support for parse errors in indenting

This commit is contained in:
ridiculousfish 2013-12-08 14:13:23 -08:00
parent dd0cc5ed9f
commit 67b1f14a6f
3 changed files with 43 additions and 13 deletions

View file

@ -3985,13 +3985,15 @@ int builtin_parse(parser_t &parser, wchar_t **argv)
stdout_buffer.append(errors.at(i).describe(src)); stdout_buffer.append(errors.at(i).describe(src));
stdout_buffer.push_back(L'\n'); stdout_buffer.push_back(L'\n');
} }
stdout_buffer.append(L"(Reparsed with continue after error)\n");
parse_tree.clear();
errors.clear();
parse_t::parse(src, parse_flag_continue_after_error, &parse_tree, &errors, true);
} }
else
{
const wcstring dump = parse_dump_tree(parse_tree, src); const wcstring dump = parse_dump_tree(parse_tree, src);
fprintf(stderr, "%ls", dump.c_str()); fprintf(stderr, "%ls", dump.c_str());
} }
}
return STATUS_BUILTIN_OK; return STATUS_BUILTIN_OK;
} }

View file

@ -742,9 +742,16 @@ static void test_indents()
{NULL, -1} {NULL, -1}
}; };
const indent_component_t components11[] =
{
{L"switch foo", 0},
{L"cas", 1}, //parse error indentation handling
{NULL, -1}
};
const indent_component_t *tests[] = {components1, components2, components3, components4, components5, components6, components7, components8, components9, components10};
const indent_component_t *tests[] = {components1, components2, components3, components4, components5, components6, components7, components8, components9, components10, components11};
for (size_t which = 0; which < sizeof tests / sizeof *tests; which++) for (size_t which = 0; which < sizeof tests / sizeof *tests; which++)
{ {
const indent_component_t *components = tests[which]; const indent_component_t *components = tests[which];

View file

@ -811,13 +811,19 @@ wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote)
trailing_indent is the indent for nodes with unrealized source, i.e. if I type 'if false <ret>' then we have an if node with an empty job list (without source) but we want the last line to be indented anyways. trailing_indent is the indent for nodes with unrealized source, i.e. if I type 'if false <ret>' then we have an if node with an empty job list (without source) but we want the last line to be indented anyways.
switch statements also indent. switch statements also indent.
max_visited_node_idx is the largest index we visited.
*/ */
static void compute_indents_recursive(const parse_node_tree_t &tree, node_offset_t node_idx, int node_indent, parse_token_type_t parent_type, std::vector<int> *indents, int *trailing_indent) static void compute_indents_recursive(const parse_node_tree_t &tree, node_offset_t node_idx, int node_indent, parse_token_type_t parent_type, std::vector<int> *indents, int *trailing_indent, node_offset_t *max_visited_node_idx)
{ {
/* Guard against incomplete trees */ /* Guard against incomplete trees */
if (node_idx > tree.size()) if (node_idx > tree.size())
return; return;
/* Update max_visited_node_idx */
if (node_idx > *max_visited_node_idx)
*max_visited_node_idx = node_idx;
/* We could implement this by utilizing the fish grammar. But there's an easy trick instead: almost everything that wraps a job list should be indented by 1. So just find all of the job lists. One exception is switch; the other exception is job_list itself: a job_list is a job and a job_list, and we want that child list to be indented the same as the parent. So just find all job_lists whose parent is not a job_list, and increment their indent by 1. */ /* We could implement this by utilizing the fish grammar. But there's an easy trick instead: almost everything that wraps a job list should be indented by 1. So just find all of the job lists. One exception is switch; the other exception is job_list itself: a job_list is a job and a job_list, and we want that child list to be indented the same as the parent. So just find all job_lists whose parent is not a job_list, and increment their indent by 1. */
const parse_node_t &node = tree.at(node_idx); const parse_node_t &node = tree.at(node_idx);
@ -866,7 +872,7 @@ static void compute_indents_recursive(const parse_node_tree_t &tree, node_offset
for (node_offset_t idx = 0; idx < node.child_count; idx++) for (node_offset_t idx = 0; idx < node.child_count; idx++)
{ {
/* Note we pass our type to our child, which becomes its parent node type */ /* Note we pass our type to our child, which becomes its parent node type */
compute_indents_recursive(tree, node.child_start + idx, node_indent, node_type, indents, trailing_indent); compute_indents_recursive(tree, node.child_start + idx, node_indent, node_type, indents, trailing_indent, max_visited_node_idx);
} }
} }
@ -876,14 +882,29 @@ std::vector<int> parse_util_compute_indents(const wcstring &src)
const size_t src_size = src.size(); const size_t src_size = src.size();
std::vector<int> indents(src_size, -1); std::vector<int> indents(src_size, -1);
/* Parse the string. We pass continue_after_error to produce a forest; the trailing indent of the last node we visited becomes the input indent of the next. I.e. in the case of 'switch foo ; cas', we get an invalid parse tree (since 'cas' is not valid) but we indent it as if it were a case item list */
parse_node_tree_t tree; parse_node_tree_t tree;
parse_t::parse(src, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, &tree, NULL /* errors */); parse_t::parse(src, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, &tree, NULL /* errors */);
/* Start indenting at the first node. If we have a parse error, we'll have to start indenting from the top again */
node_offset_t start_node_idx = 0;
int last_trailing_indent = 0;
while (start_node_idx < tree.size())
{
/* The indent that we'll get for the last line */ /* The indent that we'll get for the last line */
int trailing_indent = 0; int trailing_indent = 0;
/* Invoke the recursive version. As a hack, pass job_list for the 'parent' token, which will prevent the really-root job list from indenting */ /* Biggest offset we visited */
compute_indents_recursive(tree, 0 /* node index */, 0/* current indent */, symbol_job_list, &indents, &trailing_indent); node_offset_t max_visited_node_idx = 0;
/* Invoke the recursive version. As a hack, pass job_list for the 'parent' token type, which will prevent the really-root job list from indenting */
compute_indents_recursive(tree, start_node_idx, last_trailing_indent, symbol_job_list, &indents, &trailing_indent, &max_visited_node_idx);
/* We may have more to indent. The trailing indent becomes our current indent. Start at the node after the last we visited. */
last_trailing_indent = trailing_indent;
start_node_idx = max_visited_node_idx + 1;
}
int last_indent = 0; int last_indent = 0;
for (size_t i=0; i<src_size; i++) for (size_t i=0; i<src_size; i++)
@ -914,7 +935,7 @@ std::vector<int> parse_util_compute_indents(const wcstring &src)
{ {
if (!wcschr(L" \n\t\r", src.at(suffix_idx))) if (!wcschr(L" \n\t\r", src.at(suffix_idx)))
break; break;
indents.at(suffix_idx) = trailing_indent; indents.at(suffix_idx) = last_trailing_indent;
} }
return indents; return indents;