mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-12 04:58:57 +00:00
Teach fish_indent about escaped newlines
Correctly indents code like: alpha | \ # comment beta
This commit is contained in:
parent
6f57fef8f8
commit
98d736f916
3 changed files with 113 additions and 31 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue