diff --git a/bash_completion b/bash_completion index e3f9ebcc67b..0e9fd8f4a13 100644 --- a/bash_completion +++ b/bash_completion @@ -1289,11 +1289,14 @@ _comp_quote_compgen() # Ignored with `-d`. # OPTIONS # -d Complete only on directories - # -f Perform `compopt -o filenames` modifications manually. This # suffixes a slash to a directory name. When "-C " or "-P # " is specified to the "_comp_compgen" call, this option # is automatically turned on. +# -X PATTERN +# @since 2.18 Specify the filename pattern to exclude, or the +# filename pattern to include prefixed by "!". When this option is +# specified, $1 should be empty or not specified. # @return 0 if at least one completion is generated, or 1 otherwise. # # @since 2.12 @@ -1302,7 +1305,7 @@ _comp_compgen_filedir() _comp_compgen_tilde && return local -a toks - local _dir="" _filenames="" + local _dir="" _filenames="" _xspec="" if [[ ${_comp_compgen__dir-} || ${_comp_compgen__prefix-} ]]; then # When "-C " or "-P " is specified, the working directory # does not contain the generated candidates as filenames. In the case @@ -1317,10 +1320,11 @@ _comp_compgen_filedir() fi local OPTIND=1 OPTARG="" OPTERR=0 _opt - while getopts ":df" _opt "$@"; do + while getopts ":dfX:" _opt "$@"; do case $_opt in d) _dir=set ;; f) _filenames=set ;; + X) _xspec=$OPTARG ;; *) printf "bash_completion: %s: usage error\n" "$FUNCNAME" >&2 return 2 @@ -1329,6 +1333,10 @@ _comp_compgen_filedir() done shift "$((OPTIND - 1))" local _arg=${1-} + if [[ $_xspec && $_arg ]]; then + printf 'bash_completion: %s: "-X xspec" and "$1" cannot be specified simultaneously\n' "$FUNCNAME" >&2 + return 2 + fi if [[ $_dir ]]; then _comp_compgen -v toks -- -d @@ -1344,7 +1352,7 @@ _comp_compgen_filedir() # Munge xspec to contain uppercase version too # https://lists.gnu.org/archive/html/bug-bash/2010-09/msg00036.html # news://news.gmane.io/4C940E1C.1010304@case.edu - local _xspec=${_arg:+"!*.@($_arg|${_arg^^})"} + [[ $_xspec ]] || _xspec=${_arg:+"!*.@($_arg|${_arg^^})"} local _opts=(-f -X "$_xspec") # Use plusdirs to get dir completions if we have a xspec; if we don't, @@ -1543,20 +1551,8 @@ _comp_variable_assignments() case $prev in TZ) - cur=/usr/share/zoneinfo/$cur - _comp_compgen_filedir - if ((${#COMPREPLY[@]})); then - for i in "${!COMPREPLY[@]}"; do - if [[ ${COMPREPLY[i]} == *.tab ]]; then - unset -v 'COMPREPLY[i]' - continue - elif [[ -d ${COMPREPLY[i]} ]]; then - COMPREPLY[i]+=/ - compopt -o nospace - fi - COMPREPLY[i]=${COMPREPLY[i]#/usr/share/zoneinfo/} - done - fi + _comp_compgen -C /usr/share/zoneinfo filedir -f \ + -X '@(*.tab|leapseconds|leap-seconds.list|tzdata.zi)' ;; TERM) _comp_compgen_terms diff --git a/completions-core/lvm.bash b/completions-core/lvm.bash index 5ac6042b263..47003b595d6 100644 --- a/completions-core/lvm.bash +++ b/completions-core/lvm.bash @@ -28,11 +28,7 @@ _comp_cmd_lvm__logicalvolumes() _comp_compgen_split -- "$(lvscan 2>/dev/null | command sed -n -e "s|^.*'\(.*\)'.*$|\1|p")" if [[ $cur == /dev/mapper/* ]]; then - _comp_compgen -a filedir - local i - for i in "${!COMPREPLY[@]}"; do - [[ ${COMPREPLY[i]} == */control ]] && unset -v 'COMPREPLY[i]' - done + _comp_compgen -a filedir -X '*/control' fi } diff --git a/completions-core/ssh.bash b/completions-core/ssh.bash index 011387902d7..7608270e549 100644 --- a/completions-core/ssh.bash +++ b/completions-core/ssh.bash @@ -286,8 +286,7 @@ _comp_xfunc_ssh_compgen_identityfile() { local cur=$cur tmp [[ ! $cur && -d ~/.ssh ]] && cur=~/.ssh/id - _comp_compgen -v tmp -c "$cur" filedir && - _comp_compgen -U tmp -- -W '"${tmp[@]}"' -X "${1:+!}*.pub" + _comp_compgen -c "$cur" filedir -X "${1:+!}*.pub" } _comp_deprecate_func 2.12 _ssh_identityfile _comp_xfunc_ssh_compgen_identityfile diff --git a/test/t/unit/test_unit_compgen_filedir.py b/test/t/unit/test_unit_compgen_filedir.py index 6f6d72751c4..dbac7067d39 100644 --- a/test/t/unit/test_unit_compgen_filedir.py +++ b/test/t/unit/test_unit_compgen_filedir.py @@ -37,6 +37,11 @@ def functions(self, request, bash): "_fcd() { local cur=$(_get_cword); unset -v COMPREPLY; _comp_compgen -C _filedir filedir -d; };" "complete -F _fcd fcd", ) + assert_bash_exec( + bash, + "_X() { local cur;_comp_get_words cur; unset -v COMPREPLY; _comp_compgen_filedir -X '*.e1'; }; " + "complete -F _X X", + ) @pytest.fixture(scope="class") def non_windows_testdir(self, bash, tmp_path_factory): @@ -312,6 +317,10 @@ def test_29_dotdot(self, bash, functions, funcname): "..folder/", ] + def test_option_X_1(self, bash, functions): + completion = assert_complete(bash, "X ", cwd="_filedir/ext") + assert completion == sorted("ff.e2 foo/ hh.e2 ii.E1".split()) + def test_fallback_1(self, bash, functions): """Directory names should also be generated by the filedir fallback""" with bash_env_saved(bash) as bash_env: