Indent escaped newlines

Similar to what fish_indent does. After typing "echo \" and hitting return,
the cursor will be indented.

A possible annoyance is that when you have multiple indented lines

	echo 1 \
	    2 \
	    3 \
	    4 \

If you remove lines in the middle with Control-k, the lines below
the deleted one will start jumping around, as they are disconnected
from and reconnected to "echo".
This commit is contained in:
Johannes Altmanninger 2021-02-08 05:01:17 +01:00
parent 511747d59e
commit 5e8a248758
3 changed files with 82 additions and 0 deletions

View File

@ -272,6 +272,7 @@ Interactive improvements
- fish is now more resilient against broken terminal modes (:issue:`7133`, :issue:`4873`).
- fish handles being in control of the TTY without owning its own process group better, avoiding some hangs in special configurations (:issue:`7388`).
- Keywords can now be colored differently by setting the ``fish_color_keyword`` variable (but ``fish_color_command`` will still be used if it is unset) (:issue:`7678`).
- Just like new ``fish_indent``, the interactive reader will indent continuation lines that follow a line ending in a backslash, ``|``, ``&&`` or ``||`` (:issue:`7694`).
New or improved bindings
^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -1451,6 +1451,60 @@ static void test_indents() {
1, "\n" //
);
// Continuation lines.
add_test(&tests, //
0, "echo 'continuation line' \\", //
1, "\ncont", //
0, "\n" //
);
add_test(&tests, //
0, "echo 'empty continuation line' \\", //
1, "\n" //
);
add_test(&tests, //
0, "begin # continuation line in block", //
1, "\necho \\", //
2, "\ncont" //
);
add_test(&tests, //
0, "begin # empty continuation line in block", //
1, "\necho \\", //
2, "\n", //
0, "\nend" //
);
add_test(&tests, //
0, "echo 'multiple continuation lines' \\", //
1, "\nline1 \\", //
1, "\n# comment", //
1, "\n# more comment", //
1, "\nline2 \\", //
1, "\n" //
);
add_test(&tests, //
0, "echo # comment ending in \\", //
0, "\nline" //
);
add_test(&tests, //
0, "echo 'multiple empty continuation lines' \\", //
1, "\n\\", //
1, "\n", //
0, "\n" //
);
add_test(&tests, //
0, "echo 'multiple statements with continuation lines' \\", //
1, "\nline 1", //
0, "\necho \\", //
1, "\n" //
);
// This is an edge case, probably okay to change the behavior here.
add_test(&tests, //
0, "begin", 1, " \\", //
2, "\necho 'continuation line in block header' \\", //
2, "\n", //
1, "\n", //
0, "\nend" //
);
int test_idx = 0;
for (const test_t &test : tests) {
// Construct the input text and expected indents.

View File

@ -679,6 +679,7 @@ std::vector<int> parse_util_compute_indents(const wcstring &src) {
auto range = node.source_range();
if (range.length > 0 && node.category == category_t::leaf) {
record_line_continuations_until(range.start);
std::fill(indents.begin() + last_leaf_end, indents.begin() + range.start,
last_indent);
}
@ -714,6 +715,21 @@ std::vector<int> parse_util_compute_indents(const wcstring &src) {
return nls.source(src).find(L'\n') != wcstring::npos;
}
void record_line_continuations_until(size_t offset) {
wcstring gap_text = src.substr(last_leaf_end, offset - last_leaf_end);
size_t escaped_nl = gap_text.find(L"\\\n");
if (escaped_nl == wcstring::npos) return;
auto end = src.begin() + offset;
auto newline = src.begin() + last_leaf_end + escaped_nl + 1;
// The gap text might contain multiple newlines if there are multiple lines that
// don't contain an AST node, for example, comment lines, or lines containing only
// the escaped newline.
do {
line_continuations.push_back(newline - src.begin());
newline = std::find(newline + 1, end, L'\n');
} while (newline != end);
}
// The one-past-the-last index of the most recently encountered leaf node.
// We use this to populate the indents even if there's no tokens in the range.
size_t last_leaf_end{0};
@ -730,10 +746,14 @@ std::vector<int> parse_util_compute_indents(const wcstring &src) {
// Initialize our starting indent to -1, as our top-level node is a job list which
// will immediately increment it.
int indent{-1};
// List of locations of escaped newline characters.
std::vector<size_t> line_continuations;
};
indent_visitor_t iv(src, indents);
node_visitor(iv).accept(ast.top());
iv.record_line_continuations_until(indents.size());
std::fill(indents.begin() + iv.last_leaf_end, indents.end(), iv.last_indent);
// All newlines now get the *next* indent.
@ -755,6 +775,13 @@ std::vector<int> parse_util_compute_indents(const wcstring &src) {
next_indent = indents.at(idx);
}
}
// Add an extra level of indentation to continuation lines.
for (size_t idx : iv.line_continuations) {
do {
indents.at(idx)++;
} while (++idx < src_size && src.at(idx) != L'\n');
}
return indents;
}