mirror of
https://github.com/fish-shell/fish-shell
synced 2024-11-14 17:07:44 +00:00
Escape separators (colon and equals) to improve completion
Fish completes parts of words split by the separators, so things like
`dd if=/dev/sd<TAB>` work.
This commit improves interactive completion if completion strings legitimately
contain '=' or ':'. Consider this example where completion will suggest
a🅰️1 and other files in the cwd in addition to a:1
touch a:1; complete -C'ls a:'
This behavior remains unchanged, but this commit allows to quote or escape
separators, so that e.g. `ls "a:<TAB>` and `ls a\:<TAB>` successfully complete
the filename.
This also makes the completion insert those escapes automatically unless
already quoted.
So `ls a<TAB>` will give `ls a\:1`.
Both changes match bash's behavior.
This commit is contained in:
parent
54ed2ad440
commit
f7dac82ed6
6 changed files with 24 additions and 13 deletions
|
@ -999,6 +999,7 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring
|
|||
const bool escape_all = static_cast<bool>(flags & ESCAPE_ALL);
|
||||
const bool no_quoted = static_cast<bool>(flags & ESCAPE_NO_QUOTED);
|
||||
const bool no_tilde = static_cast<bool>(flags & ESCAPE_NO_TILDE);
|
||||
const bool escape_separators = static_cast<bool>(flags & ESCAPE_SEPARATORS);
|
||||
const bool no_caret = feature_test(features_t::stderr_nocaret);
|
||||
const bool no_qmark = feature_test(features_t::qmark_noglob);
|
||||
|
||||
|
@ -1098,9 +1099,12 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring
|
|||
case L';':
|
||||
case L'"':
|
||||
case L'%':
|
||||
case L'~': {
|
||||
case L'~':
|
||||
case L':':
|
||||
case L'=': {
|
||||
bool char_is_normal = (c == L'~' && no_tilde) || (c == L'^' && no_caret) ||
|
||||
(c == L'?' && no_qmark);
|
||||
(c == L'?' && no_qmark) ||
|
||||
((c == L':' || c == L'=') && !escape_separators);
|
||||
if (!char_is_normal) {
|
||||
need_escape = 1;
|
||||
if (escape_all) out += L'\\';
|
||||
|
|
|
@ -134,7 +134,9 @@ enum {
|
|||
/// string.
|
||||
ESCAPE_NO_QUOTED = 1 << 1,
|
||||
/// Do not escape tildes.
|
||||
ESCAPE_NO_TILDE = 1 << 2
|
||||
ESCAPE_NO_TILDE = 1 << 2,
|
||||
/// Escape colon and equal sign.
|
||||
ESCAPE_SEPARATORS = 1 << 3,
|
||||
};
|
||||
typedef unsigned int escape_flags_t;
|
||||
|
||||
|
|
|
@ -1098,6 +1098,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file,
|
|||
// Squelch file descriptions per issue #254.
|
||||
if (this->type() == COMPLETE_AUTOSUGGEST || do_file) flags |= expand_flag::no_descriptions;
|
||||
|
||||
// Expand words separated by '=' separately, unless '=' is escaped or quoted.
|
||||
// We have the following cases:
|
||||
//
|
||||
// --foo=bar => expand just bar
|
||||
|
@ -1105,14 +1106,16 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file,
|
|||
// foo=bar => expand the whole thing, and also just bar
|
||||
//
|
||||
// We also support colon separator (#2178). If there's more than one, prefer the last one.
|
||||
size_t sep_index = str.find_last_of(L"=:");
|
||||
bool complete_from_separator = (sep_index != wcstring::npos);
|
||||
size_t sep_index = str.size();
|
||||
do {
|
||||
sep_index = sep_index == 0 ? wcstring::npos : str.find_last_of(L"=:", sep_index - 1);
|
||||
} while (sep_index != wcstring::npos && is_backslashed(str, sep_index));
|
||||
wchar_t quote = L'\0';
|
||||
parse_util_get_parameter_info(str, str.size(), "e, NULL, NULL);
|
||||
bool complete_from_separator = (quote == L'\0') && (sep_index != wcstring::npos);
|
||||
bool complete_from_start = !complete_from_separator || !string_prefixes_string(L"-", str);
|
||||
|
||||
if (complete_from_separator) {
|
||||
// FIXME: This just cuts the token,
|
||||
// so any quoting or braces gets lost.
|
||||
// See #4954.
|
||||
const wcstring sep_string = wcstring(str, sep_index + 1);
|
||||
std::vector<completion_t> local_completions;
|
||||
if (expand_string(sep_string, &local_completions, flags, vars, parser, NULL) ==
|
||||
|
|
|
@ -523,7 +523,8 @@ void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_
|
|||
wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote, bool no_tilde) {
|
||||
wcstring result;
|
||||
if (quote == L'\0') {
|
||||
escape_flags_t flags = ESCAPE_ALL | ESCAPE_NO_QUOTED | (no_tilde ? ESCAPE_NO_TILDE : 0);
|
||||
escape_flags_t flags =
|
||||
ESCAPE_ALL | ESCAPE_NO_QUOTED | (no_tilde ? ESCAPE_NO_TILDE : 0) | ESCAPE_SEPARATORS;
|
||||
result = escape_string(cmd, flags);
|
||||
} else {
|
||||
// Here we are going to escape a string with quotes.
|
||||
|
|
|
@ -488,7 +488,6 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
|
|||
static volatile sig_atomic_t interrupted = 0;
|
||||
|
||||
// Prototypes for a bunch of functions defined later on.
|
||||
static bool is_backslashed(const wcstring &str, size_t pos);
|
||||
static wchar_t unescaped_quote(const wcstring &str, size_t pos);
|
||||
|
||||
/// Mode on startup, which we restore on exit.
|
||||
|
@ -2297,9 +2296,7 @@ static int can_read(int fd) {
|
|||
return select(fd + 1, &fds, 0, 0, &can_read_timeout) == 1;
|
||||
}
|
||||
|
||||
/// Test if the specified character in the specified string is backslashed. pos may be at the end of
|
||||
/// the string, which indicates if there is a trailing backslash.
|
||||
static bool is_backslashed(const wcstring &str, size_t pos) {
|
||||
bool is_backslashed(const wcstring &str, size_t pos) {
|
||||
// note pos == str.size() is OK.
|
||||
if (pos > str.size()) return false;
|
||||
|
||||
|
|
|
@ -232,4 +232,8 @@ void reader_bg_job_warning(const parser_t &parser);
|
|||
/// been executed between invocations of code.
|
||||
uint64_t reader_run_count();
|
||||
|
||||
/// Test if the specified character in the specified string is backslashed. pos may be at the end of
|
||||
/// the string, which indicates if there is a trailing backslash.
|
||||
bool is_backslashed(const wcstring &str, size_t pos);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue