Teach fish_indent about escaped newlines

Correctly indents code like:

   alpha | \
     # comment
     beta
This commit is contained in:
ridiculousfish 2018-05-07 15:52:04 -07:00
parent 6f57fef8f8
commit 98d736f916
3 changed files with 113 additions and 31 deletions

View file

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

View file

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

View file

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