mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 05:13:10 +00:00
Teach parse_util_escape_string_with_quote about tildes
Properly escape literal tildes in tab completion results. Currently we always escape tildes in unquoted arguments; in the future we may escape only leading tildes. Fixes #2274
This commit is contained in:
parent
a261beef02
commit
01d87455e1
5 changed files with 44 additions and 17 deletions
|
@ -37,6 +37,7 @@ This section is for changes merged to the `major` branch that are not also merge
|
||||||
- A new input binding `pager-toggle-search` toggles the search field in the completions pager on and off. By default this is bound to control-s.
|
- A new input binding `pager-toggle-search` toggles the search field in the completions pager on and off. By default this is bound to control-s.
|
||||||
- Slicing $history (in particular, `$history[1]` for the last executed command) is much faster.
|
- Slicing $history (in particular, `$history[1]` for the last executed command) is much faster.
|
||||||
- The pager will now show the full command instead of just its last line if the number of completions is large (#4702).
|
- The pager will now show the full command instead of just its last line if the number of completions is large (#4702).
|
||||||
|
- Tildes in file names are now properly escaped in completions (#2274)
|
||||||
|
|
||||||
## Other significant changes
|
## Other significant changes
|
||||||
- Command substitution output is now limited to 10 MB by default (#3822).
|
- Command substitution output is now limited to 10 MB by default (#3822).
|
||||||
|
|
|
@ -358,6 +358,33 @@ static void test_escape_crazy() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_escape_quotes() {
|
||||||
|
say(L"Testing escaping with quotes");
|
||||||
|
// These are "raw string literals"
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc", L'\0') == L"abc");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc~def", L'\0') == L"abc\\~def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc~def", L'\0', true) == L"abc~def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc\\~def", L'\0') == L"abc\\\\\\~def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc\\~def", L'\0', true) == L"abc\\\\~def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"~abc", L'\0') == L"\\~abc");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"~abc", L'\0', true) == L"~abc");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"~abc|def", L'\0') == L"\\~abc\\|def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"|abc~def", L'\0') == L"\\|abc\\~def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"|abc~def", L'\0', true) == L"\\|abc~def");
|
||||||
|
|
||||||
|
// Note tildes are not expanded inside quotes, so no_tilde is ignored with a quote.
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc", L'\'') == L"abc");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc\\def", L'\'') == L"abc\\def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc'def", L'\'') == L"abc\\'def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'\'') == L"~abc\\'def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'\'', true) == L"~abc\\'def");
|
||||||
|
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc", L'"') == L"abc");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"abc\\def", L'"') == L"abc\\def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'"') == L"~abc'def");
|
||||||
|
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'"', true) == L"~abc'def");
|
||||||
|
}
|
||||||
|
|
||||||
static void test_format(void) {
|
static void test_format(void) {
|
||||||
say(L"Testing formatting functions");
|
say(L"Testing formatting functions");
|
||||||
struct {
|
struct {
|
||||||
|
@ -4435,6 +4462,7 @@ int main(int argc, char **argv) {
|
||||||
if (should_test_function("error_messages")) test_error_messages();
|
if (should_test_function("error_messages")) test_error_messages();
|
||||||
if (should_test_function("escape")) test_unescape_sane();
|
if (should_test_function("escape")) test_unescape_sane();
|
||||||
if (should_test_function("escape")) test_escape_crazy();
|
if (should_test_function("escape")) test_escape_crazy();
|
||||||
|
if (should_test_function("escape")) test_escape_quotes();
|
||||||
if (should_test_function("format")) test_format();
|
if (should_test_function("format")) test_format();
|
||||||
if (should_test_function("convert")) test_convert();
|
if (should_test_function("convert")) test_convert();
|
||||||
if (should_test_function("convert_nulls")) test_convert_nulls();
|
if (should_test_function("convert_nulls")) test_convert_nulls();
|
||||||
|
|
|
@ -511,10 +511,11 @@ void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_
|
||||||
free(cmd_tmp);
|
free(cmd_tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote) {
|
wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote, bool no_tilde) {
|
||||||
wcstring result;
|
wcstring result;
|
||||||
if (quote == L'\0') {
|
if (quote == L'\0') {
|
||||||
result = escape_string(cmd, ESCAPE_ALL | ESCAPE_NO_QUOTED | ESCAPE_NO_TILDE);
|
escape_flags_t flags = ESCAPE_ALL | ESCAPE_NO_QUOTED | (no_tilde ? ESCAPE_NO_TILDE : 0);
|
||||||
|
result = escape_string(cmd, flags);
|
||||||
} else {
|
} else {
|
||||||
bool unescapable = false;
|
bool unescapable = false;
|
||||||
for (size_t i = 0; i < cmd.size(); i++) {
|
for (size_t i = 0; i < cmd.size(); i++) {
|
||||||
|
@ -660,8 +661,7 @@ std::vector<int> parse_util_compute_indents(const wcstring &src) {
|
||||||
// foo ; cas', we get an invalid parse tree (since 'cas' is not valid) but we indent it as if it
|
// 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.
|
// were a case item list.
|
||||||
parse_node_tree_t tree;
|
parse_node_tree_t tree;
|
||||||
parse_tree_from_string(src,
|
parse_tree_from_string(src, parse_flag_continue_after_error | parse_flag_include_comments |
|
||||||
parse_flag_continue_after_error | parse_flag_include_comments |
|
|
||||||
parse_flag_accept_incomplete_tokens,
|
parse_flag_accept_incomplete_tokens,
|
||||||
&tree, NULL /* errors */);
|
&tree, NULL /* errors */);
|
||||||
|
|
||||||
|
|
|
@ -114,8 +114,9 @@ void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_
|
||||||
|
|
||||||
/// Attempts to escape the string 'cmd' using the given quote type, as determined by the quote
|
/// Attempts to escape the string 'cmd' using the given quote type, as determined by the quote
|
||||||
/// character. The quote can be a single quote or double quote, or L'\0' to indicate no quoting (and
|
/// character. The quote can be a single quote or double quote, or L'\0' to indicate no quoting (and
|
||||||
/// thus escaping should be with backslashes).
|
/// thus escaping should be with backslashes). Optionally do not escape tildes.
|
||||||
wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote);
|
wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote,
|
||||||
|
bool no_tilde = false);
|
||||||
|
|
||||||
/// Given a string, parse it as fish code and then return the indents. The return value has the same
|
/// Given a string, parse it as fish code and then return the indents. The return value has the same
|
||||||
/// size as the string.
|
/// size as the string.
|
||||||
|
|
|
@ -678,7 +678,8 @@ void reader_write_title(const wcstring &cmd, bool reset_cursor_position) {
|
||||||
fish_title_command = L"fish_title";
|
fish_title_command = L"fish_title";
|
||||||
if (!cmd.empty()) {
|
if (!cmd.empty()) {
|
||||||
fish_title_command.append(L" ");
|
fish_title_command.append(L" ");
|
||||||
fish_title_command.append(parse_util_escape_string_with_quote(cmd, L'\0'));
|
fish_title_command.append(
|
||||||
|
escape_string(cmd, ESCAPE_ALL | ESCAPE_NO_QUOTED | ESCAPE_NO_TILDE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1018,9 +1019,10 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag
|
||||||
const wcstring &command_line, size_t *inout_cursor_pos,
|
const wcstring &command_line, size_t *inout_cursor_pos,
|
||||||
bool append_only) {
|
bool append_only) {
|
||||||
const wchar_t *val = val_str.c_str();
|
const wchar_t *val = val_str.c_str();
|
||||||
bool add_space = !static_cast<bool>(flags & COMPLETE_NO_SPACE);
|
bool add_space = !bool(flags & COMPLETE_NO_SPACE);
|
||||||
bool do_replace = static_cast<bool>(flags & COMPLETE_REPLACES_TOKEN);
|
bool do_replace = bool(flags & COMPLETE_REPLACES_TOKEN);
|
||||||
bool do_escape = !static_cast<bool>(flags & COMPLETE_DONT_ESCAPE);
|
bool do_escape = !bool(flags & COMPLETE_DONT_ESCAPE);
|
||||||
|
bool no_tilde = bool(flags & COMPLETE_DONT_ESCAPE_TILDES);
|
||||||
|
|
||||||
const size_t cursor_pos = *inout_cursor_pos;
|
const size_t cursor_pos = *inout_cursor_pos;
|
||||||
bool back_into_trailing_quote = false;
|
bool back_into_trailing_quote = false;
|
||||||
|
@ -1036,8 +1038,6 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag
|
||||||
wcstring sb(buff, begin - buff);
|
wcstring sb(buff, begin - buff);
|
||||||
|
|
||||||
if (do_escape) {
|
if (do_escape) {
|
||||||
// Respect COMPLETE_DONT_ESCAPE_TILDES.
|
|
||||||
bool no_tilde = static_cast<bool>(flags & COMPLETE_DONT_ESCAPE_TILDES);
|
|
||||||
wcstring escaped = escape_string(
|
wcstring escaped = escape_string(
|
||||||
val, ESCAPE_ALL | ESCAPE_NO_QUOTED | (no_tilde ? ESCAPE_NO_TILDE : 0));
|
val, ESCAPE_ALL | ESCAPE_NO_QUOTED | (no_tilde ? ESCAPE_NO_TILDE : 0));
|
||||||
sb.append(escaped);
|
sb.append(escaped);
|
||||||
|
@ -1061,9 +1061,6 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag
|
||||||
wchar_t quote = L'\0';
|
wchar_t quote = L'\0';
|
||||||
wcstring replaced;
|
wcstring replaced;
|
||||||
if (do_escape) {
|
if (do_escape) {
|
||||||
// Note that we ignore COMPLETE_DONT_ESCAPE_TILDES here. We get away with this because
|
|
||||||
// unexpand_tildes only operates on completions that have COMPLETE_REPLACES_TOKEN set,
|
|
||||||
// but we ought to respect them.
|
|
||||||
parse_util_get_parameter_info(command_line, cursor_pos, "e, NULL, NULL);
|
parse_util_get_parameter_info(command_line, cursor_pos, "e, NULL, NULL);
|
||||||
|
|
||||||
// If the token is reported as unquoted, but ends with a (unescaped) quote, and we can
|
// If the token is reported as unquoted, but ends with a (unescaped) quote, and we can
|
||||||
|
@ -1079,7 +1076,7 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
replaced = parse_util_escape_string_with_quote(val_str, quote);
|
replaced = parse_util_escape_string_with_quote(val_str, quote, no_tilde);
|
||||||
} else {
|
} else {
|
||||||
replaced = val;
|
replaced = val;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue