diff --git a/builtin.cpp b/builtin.cpp index 59f3cf719..3172dbdc5 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -3985,12 +3985,14 @@ int builtin_parse(parser_t &parser, wchar_t **argv) stdout_buffer.append(errors.at(i).describe(src)); 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); - fprintf(stderr, "%ls", dump.c_str()); - } + const wcstring dump = parse_dump_tree(parse_tree, src); + fprintf(stderr, "%ls", dump.c_str()); } return STATUS_BUILTIN_OK; } diff --git a/fish_tests.cpp b/fish_tests.cpp index b4d37f8b8..5e488aae6 100644 --- a/fish_tests.cpp +++ b/fish_tests.cpp @@ -741,10 +741,17 @@ static void test_indents() {L"", 2}, {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++) { const indent_component_t *components = tests[which]; diff --git a/parse_util.cpp b/parse_util.cpp index 842c6f75b..cb33915e3 100644 --- a/parse_util.cpp +++ b/parse_util.cpp @@ -811,12 +811,18 @@ 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 ' 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. + + 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 *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 *indents, int *trailing_indent, node_offset_t *max_visited_node_idx) { /* Guard against incomplete trees */ if (node_idx > tree.size()) 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. */ @@ -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++) { /* 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 parse_util_compute_indents(const wcstring &src) const size_t src_size = src.size(); std::vector 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_t::parse(src, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, &tree, NULL /* errors */); - /* The indent that we'll get for the last line */ - int trailing_indent = 0; + /* 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; - /* Invoke the recursive version. As a hack, pass job_list for the 'parent' token, which will prevent the really-root job list from indenting */ - compute_indents_recursive(tree, 0 /* node index */, 0/* current indent */, symbol_job_list, &indents, &trailing_indent); + while (start_node_idx < tree.size()) + { + /* The indent that we'll get for the last line */ + int trailing_indent = 0; + + /* Biggest offset we visited */ + 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; for (size_t i=0; i parse_util_compute_indents(const wcstring &src) { if (!wcschr(L" \n\t\r", src.at(suffix_idx))) break; - indents.at(suffix_idx) = trailing_indent; + indents.at(suffix_idx) = last_trailing_indent; } return indents;