diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 12545786d..59e560bd5 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -65,16 +65,59 @@ static wcstring read_file(FILE *f) { return result; } -// Append whitespace as necessary. If we have a newline, append the appropriate indent. Otherwise, -// append a space. -static void append_whitespace(indent_t node_indent, bool do_indent, bool has_new_line, - wcstring *out_result) { - if (!has_new_line) { - out_result->push_back(L' '); - } else if (do_indent) { - out_result->append(node_indent * SPACES_PER_INDENT, L' '); +struct prettifier_t { + // Original source. + const wcstring &source; + + // The prettifier output. + wcstring output; + + // Whether to indent, or just insert spaces. + const bool do_indent; + + // Whether we are at the beginning of a new line. + bool has_new_line = true; + + // Whether we need to append a continuation new line before continuing. + bool needs_continuation_newline = false; + + // Additional indentation due to line continuation (escaped newline) + uint32_t line_continuation_indent = 0; + + prettifier_t(const wcstring &source, bool do_indent) : source(source), do_indent(do_indent) {} + + void prettify_node_recursive(const parse_node_tree_t &tree, + node_offset_t node_idx, indent_t node_indent, + parse_token_type_t parent_type); + + void maybe_prepend_escaped_newline(const parse_node_t &node) { + if (node.has_preceding_escaped_newline()) { + output.append(L" \\"); + append_newline(true); + } } -} + + void append_newline(bool is_continuation = false) { + output.push_back('\n'); + has_new_line = true; + needs_continuation_newline = false; + line_continuation_indent = is_continuation ? 1 : 0; + } + + // Append whitespace as necessary. If we have a newline, append the appropriate indent. Otherwise, + // append a space. + void append_whitespace(indent_t node_indent) { + if (needs_continuation_newline) { + append_newline(true); + } + if (!has_new_line) { + output.push_back(L' '); + } else if (do_indent) { + output.append((node_indent + line_continuation_indent) * SPACES_PER_INDENT, L' '); + } + } + +}; // Dump a parse tree node in a form helpful to someone debugging the behavior of this program. static void dump_node(indent_t node_indent, const parse_node_t &node, const wcstring &source) { @@ -106,10 +149,9 @@ static void dump_node(indent_t node_indent, const parse_node_t &node, const wcst token_type_description(node.type), prevc_str, source_txt.c_str(), nextc_str); } -static void prettify_node_recursive(const wcstring &source, const parse_node_tree_t &tree, +void prettifier_t::prettify_node_recursive(const parse_node_tree_t &tree, node_offset_t node_idx, indent_t node_indent, - parse_token_type_t parent_type, bool *has_new_line, - wcstring *out_result, bool do_indent) { + parse_token_type_t parent_type) { const parse_node_t &node = tree.at(node_idx); const parse_token_type_t node_type = node.type; const parse_token_type_t prev_node_type = @@ -130,32 +172,36 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre if (dump_parse_tree) dump_node(node_indent, node, source); - if (node.has_comments()) // handle comments, which come before the text - { + // Prepend any escaped newline. + maybe_prepend_escaped_newline(node); + + // handle comments, which come before the text + if (node.has_comments()) { auto comment_nodes = tree.comment_nodes_for_node(node); for (const auto &comment : comment_nodes) { - append_whitespace(node_indent, do_indent, *has_new_line, out_result); + maybe_prepend_escaped_newline(*comment.node()); + append_whitespace(node_indent); auto source_range = comment.source_range(); - out_result->append(source, source_range->start, source_range->length); + output.append(source, source_range->start, source_range->length); + needs_continuation_newline = true; } } if (node_type == parse_token_type_end) { - out_result->push_back(L'\n'); - *has_new_line = true; + append_newline(); } else if ((node_type >= FIRST_PARSE_TOKEN_TYPE && node_type <= LAST_PARSE_TOKEN_TYPE) || node_type == parse_special_type_parse_error) { if (node.keyword != parse_keyword_none) { - append_whitespace(node_indent, do_indent, *has_new_line, out_result); - out_result->append(keyword_description(node.keyword)); - *has_new_line = false; + append_whitespace(node_indent); + output.append(keyword_description(node.keyword)); + has_new_line = false; } else if (node.has_source()) { // Some type representing a particular token. if (prev_node_type != parse_token_type_redirection) { - append_whitespace(node_indent, do_indent, *has_new_line, out_result); + append_whitespace(node_indent); } - out_result->append(source, node.source_start, node.source_length); - *has_new_line = false; + output.append(source, node.source_start, node.source_length); + has_new_line = false; } } @@ -164,8 +210,7 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre // Note: We pass our type to our child, which becomes its parent node type. // Note: While node.child_start could be -1 (NODE_OFFSET_INVALID) the addition is safe // because we won't execute this call in that case since node.child_count should be zero. - prettify_node_recursive(source, tree, node.child_start + idx, node_indent, node_type, - has_new_line, out_result, do_indent); + prettify_node_recursive(tree, node.child_start + idx, node_indent, node_type); } } @@ -185,17 +230,15 @@ static wcstring prettify(const wcstring &src, bool do_indent) { // We may have a forest of disconnected trees on a parse failure. We have to handle all nodes // that have no parent, and all parse errors. - bool has_new_line = true; - wcstring result; + prettifier_t prettifier{src, do_indent}; for (node_offset_t i = 0; i < parse_tree.size(); i++) { const parse_node_t &node = parse_tree.at(i); if (node.parent == NODE_OFFSET_INVALID || node.type == parse_special_type_parse_error) { // A root node. - prettify_node_recursive(src, parse_tree, i, 0, symbol_job_list, &has_new_line, &result, - do_indent); + prettifier.prettify_node_recursive(parse_tree, i, 0, symbol_job_list); } } - return result; + return std::move(prettifier.output); } // Helper for output_set_writer diff --git a/tests/indent.in b/tests/indent.in index 2d98dd8eb..0bc64947f 100644 --- a/tests/indent.in +++ b/tests/indent.in @@ -103,4 +103,23 @@ d e" true "builtin" yes en"d" + +alpha | \ + beta + +gamma | \ +# comment3 +delta + +if true +echo abc +end + +if false # comment4 +and true && false +echo abc;end + +echo hi | + +echo bye ' | ../test/root/bin/fish_indent diff --git a/tests/indent.out b/tests/indent.out index 5a4d9dc0f..9819b89a0 100644 --- a/tests/indent.out +++ b/tests/indent.out @@ -105,3 +105,23 @@ end while true builtin yes end + +alpha | \ + beta + +gamma | \ + # comment3 + delta + +if true + echo abc +end + +if false # comment4 + and true && false + echo abc +end + +echo hi | + +echo bye