Allow custom completions to have leading dots

By default, fish does not complete files that have leading dots, unless the
wildcard itself has a leading dot. However this also affected completions;
for example `git add` would not offer `.gitlab-ci.yml` because it has a
leading dot.

Relax this for custom completions. Default file expansion still
suppresses leading dots, but now custom completions can create
leading-dot completions and they will be offered.

Fixes #3707.

(cherry picked from commit b7de768c73)
This commit is contained in:
ridiculousfish 2022-09-12 15:33:29 -07:00 committed by David Adam
parent f6676350a7
commit b28eae8be9
5 changed files with 36 additions and 7 deletions

View file

@ -49,6 +49,7 @@ Interactive improvements
- A longstanding issue where items in the pager would sometimes display without proper formatting has been fixed (:issue:`9617`).
- The :kbd:`Alt` +\ :kbd:`l` binding, which lists the directory of the token under the cursor, correctly expands tilde (``~``) to the home directory (:issue:`9954`).
- Various fish utilities that use an external pager will now try a selection of common pagers if the :envvar:`PAGER` environment variable is not set, or write the output to the screen without a pager if there is not one available (:issue:`10074`)
- Command-specific tab completions may now offer results whose first character is a period. For example, it is now possible to tab-complete ``git add`` for files with leading periods. The default file completions hide these files, unless the token itself has a leading period (:issue:`3707`).
Improved prompts
^^^^^^^^^^^^^^^^

View file

@ -366,7 +366,8 @@ class completer_t {
bool conditions_test(const wcstring_list_t &conditions);
void complete_strings(const wcstring &wc_escaped, const description_func_t &desc_func,
const completion_list_t &possible_comp, complete_flags_t flags);
const completion_list_t &possible_comp, complete_flags_t flags,
expand_flags_t extra_expand_flags = {});
expand_flags_t expand_flags() const {
expand_flags_t result{};
@ -510,12 +511,16 @@ static void parse_cmd_string(const wcstring &str, wcstring *path, wcstring *cmd,
/// @param possible_comp
/// the list of possible completions to iterate over
/// @param flags
/// The flags
/// The flags controlling completion
/// @param extra_expand_flags
/// Additional flags controlling expansion.
void completer_t::complete_strings(const wcstring &wc_escaped, const description_func_t &desc_func,
const completion_list_t &possible_comp, complete_flags_t flags) {
const completion_list_t &possible_comp, complete_flags_t flags,
expand_flags_t extra_expand_flags) {
wcstring tmp = wc_escaped;
if (!expand_one(tmp,
this->expand_flags() | expand_flag::skip_cmdsubst | expand_flag::skip_wildcards,
this->expand_flags() | extra_expand_flags | expand_flag::skip_cmdsubst |
expand_flag::skip_wildcards,
ctx))
return;
@ -525,7 +530,7 @@ void completer_t::complete_strings(const wcstring &wc_escaped, const description
const wcstring &comp_str = comp.completion;
if (!comp_str.empty()) {
wildcard_complete(comp_str, wc.c_str(), desc_func, &this->completions,
this->expand_flags(), flags);
this->expand_flags() | extra_expand_flags, flags);
}
}
}
@ -730,7 +735,9 @@ void completer_t::complete_from_args(const wcstring &str, const wcstring &args,
ctx.parser->set_last_statuses(status);
}
this->complete_strings(escape_string(str), const_desc(desc), possible_comp, flags);
// Allow leading dots - see #3707.
this->complete_strings(escape_string(str), const_desc(desc), possible_comp, flags,
expand_flag::allow_nonliteral_leading_dot);
}
static size_t leading_dash_count(const wchar_t *str) {

View file

@ -45,6 +45,10 @@ enum class expand_flag {
/// Disallow directory abbreviations like /u/l/b for /usr/local/bin. Only applicable if
/// fuzzy_match is set.
no_fuzzy_directories,
/// Allows matching a leading dot even if the wildcard does not contain one.
/// By default, wildcards only match a leading dot literally; this is why e.g. '*' does not
/// match hidden files.
allow_nonliteral_leading_dot,
/// Do expansions specifically to support cd. This means using CDPATH as a list of potential
/// working directories, and to use logical instead of physical paths.
special_for_cd,

View file

@ -191,7 +191,8 @@ static wildcard_result_t wildcard_complete_internal(const wchar_t *const str, si
// Maybe early out for hidden files. We require that the wildcard match these exactly (i.e. a
// dot); ANY_STRING not allowed.
if (is_first_call && str[0] == L'.' && wc[0] != L'.') {
if (is_first_call && !params.expand_flags.get(expand_flag::allow_nonliteral_leading_dot) &&
str[0] == L'.' && wc[0] != L'.') {
return wildcard_result_t::no_match;
}

View file

@ -531,6 +531,22 @@ begin
# CHECK: Empty completions
end
rm -$f $tmpdir/*
# Leading dots are not completed for default file completion,
# but may be for custom command (e.g. git add).
function dotty
end
function notty
end
complete -c dotty --no-files -a '(echo .a*)'
touch .abc .def
complete -C'notty '
echo "Should be nothing"
# CHECK: Should be nothing
complete -C'dotty '
# CHECK: .abc
rm -r $tmpdir
complete -C'complete --command=mktemp' | string replace -rf '=mktemp\t.*' '=mktemp'