diff --git a/bash_completion b/bash_completion index 31529920c7f..03646d5d381 100644 --- a/bash_completion +++ b/bash_completion @@ -397,6 +397,23 @@ _comp_split() ((_new_size > _old_size)) } +# Helper function for _comp_compgen +# @var[in] $? +# @var[in] _var +# @var[in] _append +# @return original $? +_comp_compgen__error_fallback() +{ + local _status=$? + if [[ $_append ]]; then + # make sure existence of variable + eval -- "$_var+=()" + else + eval -- "$_var=()" + fi + return "$_status" +} + # Provide a common interface to generate completion candidates in COMPREPLY or # in a specified array. # OPTIONS @@ -414,6 +431,7 @@ _comp_split() # -c cur Set a word used as a prefix to filter the completions. The default # is ${cur-}. # -R The same as -c ''. Use raw outputs without filtering. +# -C dir Evaluate compgen/generator in the specified directory. # @var[in,opt] cur Used as the default value of a prefix to filter the # completions. # @@ -437,10 +455,10 @@ _comp_split() # as `-v arr` as a part of the `_comp_compgen` options. # # Usage #2: _comp_compgen [-alR|-v arr|-c cur] name args... -# Call `_comp_compgen_NAME ARGS...` with the specified options. This provides -# a common interface to call the functions `_comp_compgen_NAME`, which produce -# completion candidates, with custom options [-alR|-v arr|-c cur]. The option -# `-F sep` is not used with this usage. +# Call the generator `_comp_compgen_NAME ARGS...` with the specified options. +# This provides a common interface to call the functions `_comp_compgen_NAME`, +# which produce completion candidates, with custom options [-alR|-v arr|-c +# cur]. The option `-F sep` is not used with this usage. # @param $1... name args Calls the function _comp_compgen_NAME with the # specified ARGS (if $1 does not start with a hyphen `-`). The options # [-alR|-v arr|-c cur] are inherited by the child calls of `_comp_compgen` @@ -484,10 +502,15 @@ _comp_compgen() local _append=${_comp_compgen__append-} local _var=${_comp_compgen__var-COMPREPLY} local _cur=${_comp_compgen__cur-${cur-}} - local _ifs=$' \t\n' + local _ifs=$' \t\n' _dir="" + local _old_nocasematch="" + if shopt -q nocasematch; then + _old_nocasematch=set + shopt -u nocasematch + fi local OPTIND=1 OPTARG="" OPTERR=0 _opt - while getopts ':alF:v:Rc:' _opt "$@"; do + while getopts ':alF:v:Rc:C:' _opt "$@"; do case $_opt in a) _append=set ;; v) @@ -501,12 +524,20 @@ _comp_compgen() F) _ifs=$OPTARG ;; c) _cur=$OPTARG ;; R) _cur="" ;; + C) + if [[ ! $OPTARG ]]; then + printf 'bash_completion: %s: -C: invalid directory name `%s'\''.\n' "$FUNCNAME" "$OPTARG" >&2 + return 2 + fi + _dir=$OPTARG + ;; *) printf 'bash_completion: %s: usage error\n' "$FUNCNAME" >&2 return 2 ;; esac done + [[ $_old_nocasematch ]] && shopt -s nocasematch shift "$((OPTIND - 1))" if (($# == 0)); then printf 'bash_completion: %s: unexpected number of arguments.\n' "$FUNCNAME" >&2 @@ -521,6 +552,18 @@ _comp_compgen() return 2 fi + if [[ $_dir ]]; then + local _original_pwd=$PWD + local PWD=${PWD-} OLDPWD=${OLDPWD-} + # Note: We also redirect stdout because `cd` may output the target + # directory to stdout when CDPATH is set. + command cd -- "$_dir" &>/dev/null || + { + _comp_compgen__error_fallback + return + } + fi + local _comp_compgen__append=$_append local _comp_compgen__var=$_var local _comp_compgen__cur=$_cur cur=$_cur @@ -528,7 +571,16 @@ _comp_compgen() # arguments to the function if any. # shellcheck disable=SC2145 _comp_compgen_"$@" - return + local _status=$? + + # Go back to the original directory. + # Note: Failure of this line results in the change of the current + # directory visible to the user. We intentionally do not redirect + # stderr so that the error message appear in the terminal. + # shellcheck disable=SC2164 + [[ $_dir ]] && command cd -- "$_original_pwd" + + return "$_status" fi # usage: _comp_compgen [options] -- [compgen_options] @@ -550,16 +602,15 @@ _comp_compgen() local _result _result=$( + if [[ $_dir ]]; then + # Note: We also redirect stdout because `cd` may output the target + # directory to stdout when CDPATH is set. + command cd -- "$_dir" &>/dev/null || return + fi IFS=$_ifs compgen "$@" ${_cur:+-- "$_cur"} ) || { - local _status=$? - if [[ $_append ]]; then - # make sure existence of variable - eval -- "$_var+=()" - else - eval -- "$_var=()" - fi - return "$_status" + _comp_compgen__error_fallback + return } _comp_split -l ${_append:+-a} "$_var" "$_result" @@ -1508,13 +1559,19 @@ _available_interfaces() } # Echo number of CPUs, falling back to 1 on failure. -# TODO:API: rename per conventions, rework to use vars rather than outputting -_ncpus() +# @var[out] ret +# @return 0 if it successfully obtained the number of CPUs, or otherwise 1 +# @since 2.12 +_comp_get_ncpus() { local var=NPROCESSORS_ONLN [[ $OSTYPE == *@(linux|msys|cygwin)* ]] && var=_$var - local n=$(getconf $var 2>/dev/null) - printf %s "${n:-1}" + if ret=$(getconf $var 2>/dev/null) && ((ret >= 1)); then + return 0 + else + ret=1 + return 1 + fi } # Perform tilde (~) completion diff --git a/bash_completion.d/000_bash_completion_compat.bash b/bash_completion.d/000_bash_completion_compat.bash index 618ead21e98..2405aff47d9 100644 --- a/bash_completion.d/000_bash_completion_compat.bash +++ b/bash_completion.d/000_bash_completion_compat.bash @@ -335,4 +335,12 @@ _parse_usage() return 0 } +# @deprecated 2.12 Use `_comp_get_ncpus`. +_ncpus() +{ + local ret + _comp_get_ncpus + printf %s "$ret" +} + # ex: filetype=sh diff --git a/completions/2to3 b/completions/2to3 index c22d12dc88e..7b5d04908ab 100644 --- a/completions/2to3 +++ b/completions/2to3 @@ -15,7 +15,9 @@ _comp_cmd_2to3() return ;; -j | --processes) - COMPREPLY=($(compgen -W "{1..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "{1..$ret}" return ;; -o | --output-dir) diff --git a/completions/_modules b/completions/_modules index c208fc27695..f549cd19f88 100644 --- a/completions/_modules +++ b/completions/_modules @@ -21,30 +21,26 @@ # being sourced before it and thus before the `module' alias has been defined. [[ -f /etc/profile.d/modules.sh ]] || return 1 -# TODO:API: rename per conventions, rework to use vars rather than outputting -_module_list() +_comp_cmd_module__compgen_list() { local modules="$(command sed 's/:/ /g' <<<"$LOADEDMODULES" | sort)" - compgen -W "$modules" -- "$1" + _comp_compgen -- -W "$modules" } -# TODO:API: rename per conventions, rework to use vars rather than outputting -_module_path() +_comp_cmd_module__compgen_path() { local modules="$(command sed 's/:/ /g' <<<"$MODULEPATH" | sort)" - compgen -W "$modules" -- "$1" + _comp_compgen -- -W "$modules" } -# TODO:API: rename per conventions, rework to use vars rather than outputting -_module_avail() +_comp_cmd_module__compgen_avail() { local modules="$( module avail 2>&1 | command grep -E -v '^(-|$)' | xargs printf '%s\n' | command sed -e 's/(default)//g' | sort )" - - compgen -W "$modules" -- "$1" + _comp_compgen -- -W "$modules" } # A completion function for the module alias @@ -65,19 +61,23 @@ _comp_cmd_module() elif ((cword == 2)); then case $prev in add | display | help | load | show | whatis) - COMPREPLY=($(_module_avail "$cur")) + # TODO:API: use generator call? + _comp_cmd_module__compgen_avail ;; rm | switch | swap | unload | update) - COMPREPLY=($(_module_list "$cur")) + # TODO:API: use generator call? + _comp_cmd_module__compgen_list ;; unuse) - COMPREPLY=($(_module_path "$cur")) + # TODO:API: use generator call? + _comp_cmd_module__compgen_path ;; esac elif ((cword == 3)); then case ${words[1]} in swap | switch) - COMPREPLY=($(_module_avail "$cur")) + # TODO:API: use generator call? + _comp_cmd_module__compgen_avail ;; esac fi diff --git a/completions/_mount.linux b/completions/_mount.linux index 51c445ba108..89c6ec3ca64 100644 --- a/completions/_mount.linux +++ b/completions/_mount.linux @@ -37,17 +37,11 @@ _comp_cmd_mount() return ;; -L) - COMPREPLY=($( - command cd "/dev/disk/by-label/" 2>/dev/null || return - compgen -f -- "$cur" - )) + _comp_compgen -C "/dev/disk/by-label/" -- -f return ;; -U) - COMPREPLY=($( - command cd "/dev/disk/by-uuid/" 2>/dev/null || return - compgen -f -- "$cur" - )) + _comp_compgen -C "/dev/disk/by-uuid/" -- -f return ;; -O | --test-opts) diff --git a/completions/_slackpkg b/completions/_slackpkg index 0f5d36ca561..898c19e9459 100644 --- a/completions/_slackpkg +++ b/completions/_slackpkg @@ -65,11 +65,8 @@ _comp_cmd_slackpkg() ;; install-template | remove-template) if [[ -e $confdir/templates ]]; then - COMPREPLY=($( - command cd -- "$confdir/templates" - compgen -f -X "!*.template" -- "$cur" - )) - COMPREPLY=(${COMPREPLY[@]%.template}) + _comp_compgen -C "$confdir/templates" -- -f -X \ + "!?*.template" && COMPREPLY=("${COMPREPLY[@]%.template}") fi return ;; @@ -77,10 +74,7 @@ _comp_cmd_slackpkg() _comp_compgen_filedir _comp_compgen -a -- -W 'a ap d e f k kde kdei l n t tcl x xap xfce y' - COMPREPLY+=($( - command cd /var/log/packages - compgen -f -- "$cur" - )) + _comp_compgen -aC /var/log/packages -- -f return ;; install | reinstall | upgrade | blacklist | download) diff --git a/completions/_umount.linux b/completions/_umount.linux index 257d194898d..bb259752848 100644 --- a/completions/_umount.linux +++ b/completions/_umount.linux @@ -81,9 +81,8 @@ _comp_cmd_umount__linux_fstab() local i for i in ${!COMPREPLY[*]}; do [[ ${COMPREPLY[i]} == "$realcur"* ]] && - COMPREPLY+=($(command cd -- "$dircur" 2>/dev/null && - compgen -f -d -P "$dircur" \ - -X "!${COMPREPLY[i]##"$dirrealcur"}" -- "$basecur")) + _comp_compgen -aC "$dircur" -c "$basecur" -- \ + -f -d -P "$dircur" -X "!${COMPREPLY[i]##"$dirrealcur"}" done fi fi diff --git a/completions/bts b/completions/bts index 4272015604e..41197c341e5 100644 --- a/completions/bts +++ b/completions/bts @@ -1,22 +1,24 @@ # bts completion -*- shell-script -*- -# List bug numbers from bugs cache in ~/.devscripts_cache/bts -# TODO:API: rename per conventions, rework to use vars rather than outputting -_cached_bugs() +# Generate bug numbers from bugs cache in ~/.devscripts_cache/bts +# TODO:API: generator +_comp_cmd_bts__cached_bugs() { - [[ -d $HOME/.devscripts_cache/bts ]] && + [[ -d $HOME/.devscripts_cache/bts ]] || return 1 + local bugs=$( find "$HOME/.devscripts_cache/bts" -maxdepth 1 \ -name "${cur}[0-9]*.html" \ -printf "%f\n" | cut -d'.' -f1 + ) + _comp_compgen -aR -- -W '$bugs' } -# List APT source packages prefixed with "src:" -# TODO:API: rename per conventions, rework to use vars rather than outputting -_src_packages_with_prefix() +# Generate APT source packages prefixed with "src:" +# TODO:API: generator +_comp_cmd_bts__src_packages_with_prefix() { - ppn=${cur:4} # partial package name, after stripping "src:" - compgen -P "src:" -W '$(_comp_xfunc apt-cache sources "$ppn")' \ - -- "$ppn" + local ppn=${cur:4} # partial package name, after stripping "src:" + _comp_compgen -ac "$ppn" -- -P "src:" -W '$(_comp_xfunc apt-cache sources "$ppn")' } _comp_cmd_bts() @@ -26,9 +28,9 @@ _comp_cmd_bts() case $prev in show | bugs) - COMPREPLY=($(compgen -W 'release-critical RC from: tag: - usertag:' -- "$cur") $(_cached_bugs) - $(_src_packages_with_prefix)) + _comp_compgen -- -W 'release-critical RC from: tag: usertag:' + _comp_cmd_bts__cached_bugs + _comp_cmd_bts__src_packages_with_prefix return ;; select) @@ -38,8 +40,8 @@ _comp_cmd_bts() return ;; status) - COMPREPLY=($(compgen -W 'file: fields: verbose' -- "$cur") - $(_cached_bugs)) + _comp_compgen -- -W 'file: fields: verbose' + _comp_cmd_bts__cached_bugs return ;; block | unblock) @@ -57,7 +59,8 @@ _comp_cmd_bts() return ;; clone | "done" | reopen | archive | unarchive | retitle | summary | submitter | found | notfound | fixed | notfixed | merge | forcemerge | unmerge | claim | unclaim | forwarded | notforwarded | owner | noowner | subscribe | unsubscribe | reportspam | spamreport | affects | usertag | usertags | reassign | tag | tags) - COMPREPLY=($(_cached_bugs)) + COMPREPLY=() + _comp_cmd_bts__cached_bugs return ;; package) @@ -65,15 +68,15 @@ _comp_cmd_bts() return ;; cache) - COMPREPLY=($(_comp_xfunc apt-cache packages) - $(_src_packages_with_prefix) - $(compgen -W 'from: release-critical RC' -- "$cur")) + COMPREPLY=($(_comp_xfunc apt-cache packages)) + _comp_cmd_bts__src_packages_with_prefix + _comp_compgen -a -- -W 'from: release-critical RC' return ;; cleancache) - COMPREPLY=($(_comp_xfunc apt-cache packages) - $(_src_packages_with_prefix) - $(compgen -W 'from: tag: usertag: ALL' -- "$cur")) + COMPREPLY=($(_comp_xfunc apt-cache packages)) + _comp_cmd_bts__src_packages_with_prefix + _comp_compgen -a -- -W 'from: tag: usertag: ALL' return ;; user) diff --git a/completions/bzip2 b/completions/bzip2 index aede765cc2e..e46f8c4ae75 100644 --- a/completions/bzip2 +++ b/completions/bzip2 @@ -12,7 +12,9 @@ _comp_cmd_bzip2() return ;; -${noargopts}n) - COMPREPLY=($(compgen -W "{1..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "{1..$ret}" return ;; esac diff --git a/completions/cppcheck b/completions/cppcheck index e3aadef02e3..eda632d5b64 100644 --- a/completions/cppcheck +++ b/completions/cppcheck @@ -42,7 +42,9 @@ _comp_cmd_cppcheck() return ;; -j) - COMPREPLY=($(compgen -W "{2..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "{1..$ret}" return ;; --language | -x) diff --git a/completions/feh b/completions/feh index ae47df0e018..5ba9751f7fd 100644 --- a/completions/feh +++ b/completions/feh @@ -29,16 +29,12 @@ _comp_cmd_feh() fi local font_path # font_path="$(imlib2-config --prefix 2>/dev/null)/share/imlib2/data/fonts" - # COMPREPLY=( $(command cd -- "$font_path" 2>/dev/null; compgen -f \ - # -X "!*.@([tT][tT][fF])" -S / -- "$cur") ) + # _comp_compgen -C "$font_path" -- -f -X "!*.@([tT][tT][fF])" -S / for ((i = ${#words[@]} - 1; i > 0; i--)); do if [[ ${words[i]} == -@(C|-fontpath) ]]; then font_path="${words[i + 1]}" - COMPREPLY+=($( - command cd -- "$font_path" 2>/dev/null - compgen -f \ - -X "!*.@([tT][tT][fF])" -S / -- "$cur" - )) + _comp_compgen -aC "$font_path" -- \ + -f -X "!*.@([tT][tT][fF])" -S / fi done compopt -o nospace diff --git a/completions/flake8 b/completions/flake8 index 0ccb509b553..e85809b35b6 100644 --- a/completions/flake8 +++ b/completions/flake8 @@ -16,7 +16,9 @@ _comp_cmd_flake8() return ;; --jobs | -${noargopts}j) - COMPREPLY=($(compgen -W "auto {1..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "auto {1..$ret}" return ;; --output-file | --append-config | --config) diff --git a/completions/gzip b/completions/gzip index 67857f9e281..05074ee4dc8 100644 --- a/completions/gzip +++ b/completions/gzip @@ -13,7 +13,9 @@ _comp_cmd_gzip() return ;; --processes | -${noargopts}p) - COMPREPLY=($(compgen -W "{1..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_comgpen -- -W "{1..$ret}" return ;; esac diff --git a/completions/isort b/completions/isort index 6129978b2e0..2a292ba3d3d 100644 --- a/completions/isort +++ b/completions/isort @@ -15,7 +15,9 @@ _comp_cmd_isort() return ;; --jobs | -j) - COMPREPLY=($(compgen -W "{1..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_comgpen -- -W "{1..$ret}" return ;; --multi-line | -m) diff --git a/completions/lrzip b/completions/lrzip index a9601b22a00..16da0b97f20 100644 --- a/completions/lrzip +++ b/completions/lrzip @@ -34,7 +34,9 @@ _comp_cmd_lrzip() return ;; --threads | -${noargopts}p) - COMPREPLY=($(compgen -W "{1..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "{1..$ret}" return ;; esac diff --git a/completions/lzip b/completions/lzip index 3fc7406ab3e..31c6f37534b 100644 --- a/completions/lzip +++ b/completions/lzip @@ -18,7 +18,9 @@ _comp_cmd_lzip() decompress=set ;; --threads | -${noargopts}n) - COMPREPLY=($(compgen -W "{1..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "{1..$ret}" return ;; --output | -${noargopts}o) diff --git a/completions/make b/completions/make index 11e3ae91877..8090d7699c2 100644 --- a/completions/make +++ b/completions/make @@ -165,7 +165,9 @@ _comp_cmd_make() return ;; --jobs | -${noargopts}j) - COMPREPLY=($(compgen -W "{1..$(($(_ncpus) * 2))}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "{1..$((ret * 2))}" return ;; esac diff --git a/completions/pylint b/completions/pylint index 2a0d1c8adb5..9e27f657a7b 100644 --- a/completions/pylint +++ b/completions/pylint @@ -78,7 +78,9 @@ _comp_cmd_pylint() return ;; --jobs | -${noargopts}j) - COMPREPLY=($(compgen -W "{1..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "{1..$ret}" return ;; --confidence) diff --git a/completions/pytest b/completions/pytest index 05ad0bdc833..d8bbff83fa0 100644 --- a/completions/pytest +++ b/completions/pytest @@ -81,7 +81,9 @@ _comp_cmd_pytest() return ;; --numprocesses | -${noargopts}n) - COMPREPLY=($(compgen -W "{1..$(_ncpus)} auto" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "{1..$ret} auto" return ;; --dist | --vcr-record?(-mode)) diff --git a/completions/removepkg b/completions/removepkg index 1f780e50069..73b632bc8b9 100644 --- a/completions/removepkg +++ b/completions/removepkg @@ -15,10 +15,7 @@ _comp_cmd_removepkg() fi local root=${ROOT:-/} - COMPREPLY=($( - command cd -- "$root/var/log/packages" 2>/dev/null || return 1 - compgen -f -- "$cur" - )) + _comp_compgen -C "$root/var/log/packages" -- -f } && complete -F _comp_cmd_removepkg removepkg diff --git a/completions/sbopkg b/completions/sbopkg index 4f58894415a..6b1acb29080 100644 --- a/completions/sbopkg +++ b/completions/sbopkg @@ -64,11 +64,8 @@ _comp_cmd_sbopkg() COMPREPLY=($( command sed -ne "/^SLACKBUILD NAME: $cur/{s/^SLACKBUILD NAME: //;p}" \ "$REPO_ROOT/$REPO_NAME/$REPO_BRANCH/SLACKBUILDS.TXT" - ) - $( - command cd -- "$QUEUEDIR" - compgen -f -X "!*.sqf" -- "$cur" )) + _comp_compgen -aC "$QUEUEDIR" -- -f -X "!*.sqf" } && complete -F _comp_cmd_sbopkg sbopkg diff --git a/completions/slapt-get b/completions/slapt-get index dcd9d560771..0085ed428c9 100644 --- a/completions/slapt-get +++ b/completions/slapt-get @@ -69,10 +69,7 @@ _comp_cmd_slapt_get() return ;; ins) # --remove|--filelist - COMPREPLY=($( - command cd /var/log/packages - compgen -f -- "$cur" - )) + _comp_compgen -C /var/log/packages -- -f return ;; set) # --install-set diff --git a/completions/xdg-mime b/completions/xdg-mime index 0cfc1261ba2..b779454468d 100644 --- a/completions/xdg-mime +++ b/completions/xdg-mime @@ -5,10 +5,7 @@ _comp_cmd_xdg_mime__mimetype() local d i local -a arr for d in /usr/share/mime /usr/local/share/mime; do - arr=($( - command cd "$d" 2>/dev/null || exit 1 - compgen -f -o plusdirs -X "!*.xml" -- "$cur" - )) || continue + _comp_compgen -v arr -C "$d" -- -f -o plusdirs -X "!*.xml" || continue for i in "${!arr[@]}"; do case ${arr[i]} in packages*) unset -v "arr[i]" ;; # not a MIME type dir diff --git a/completions/xz b/completions/xz index 52d1e01505f..c89b3afa3f0 100644 --- a/completions/xz +++ b/completions/xz @@ -26,7 +26,9 @@ _comp_cmd_xz() return ;; --threads | -${noargopts}T) - COMPREPLY=($(compgen -W "{0..$(_ncpus)}" -- "$cur")) + local ret + _comp_get_ncpus + _comp_compgen -- -W "{0..$ret}" return ;; --memlimit | --memlimit-compress | --memlimit-decompress | --memory | \ diff --git a/test/t/unit/test_unit_compgen.py b/test/t/unit/test_unit_compgen.py index 86d042a6058..90ef54540f0 100644 --- a/test/t/unit/test_unit_compgen.py +++ b/test/t/unit/test_unit_compgen.py @@ -1,6 +1,7 @@ import pytest +import re -from conftest import assert_bash_exec, bash_env_saved +from conftest import assert_bash_exec, bash_env_saved, assert_complete @pytest.mark.bashcomp(cmd=None) @@ -13,11 +14,27 @@ def functions(self, bash): ) assert_bash_exec( bash, - '_comp__test_words() { local -a arr=(00) input; input=("${@:1:$#-1}"); _comp_compgen -v arr -c "${@:$#}" -- -W \'${input[@]+"${input[@]}"}\'; _comp__test_dump; }', + '_comp__test_compgen() { local -a arr=(00); _comp_compgen -v arr "$@"; _comp__test_dump; }', ) assert_bash_exec( bash, - '_comp__test_words_ifs() { local -a arr=(00); local input=$2; _comp_compgen -F "$1" -v arr -c "${@:$#}" -- -W \'$input\'; _comp__test_dump; }', + '_comp__test_words() { local -a input=("${@:1:$#-1}"); _comp__test_compgen -c "${@:$#}" -- -W \'${input[@]+"${input[@]}"}\'; }', + ) + assert_bash_exec( + bash, + '_comp__test_words_ifs() { local input=$2; _comp__test_compgen -F "$1" -c "${@:$#}" -- -W \'$input\'; }', + ) + + assert_bash_exec( + bash, + '_comp_cmd_fc() { _comp_compgen -c "$(_get_cword)" -C _filedir filedir; }; ' + "complete -F _comp_cmd_fc fc; " + "complete -F _comp_cmd_fc -o filenames fc2", + ) + assert_bash_exec( + bash, + '_comp_cmd_fcd() { _comp_compgen -c "$(_get_cword)" -C _filedir filedir -d; }; ' + "complete -F _comp_cmd_fcd fcd", ) def test_1_basic(self, bash, functions): @@ -83,3 +100,31 @@ def test_5_option_F(self, bash, functions): want_output=True, ) assert output.strip() == "< 1><3 4><6 >< >" + + def test_6_option_C_1(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c a -C _filedir filedir", + want_output=True, + ) + set1 = set(re.findall(r"<[^<>]*>", output.strip())) + assert set1 == {"", "", "", "", "", ""} + + def test_6_option_C_2(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c b -C _filedir -- -d", + want_output=True, + ) + assert output.strip() == "" + + @pytest.mark.parametrize("funcname", "fc fc2".split()) + def test_6_option_C_3(self, bash, functions, funcname): + completion = assert_complete(bash, "%s _filedir ab/" % funcname) + assert completion == "e" + + @pytest.mark.complete(r"fcd a\ ") + def test_6_option_C_4(self, functions, completion): + # Note: we are not in the original directory that "b" exists, so Bash + # will not suffix a slash to the directory name. + assert completion == "b"