Skip to content

Commit

Permalink
feat(_comp_compgen): support -i cmd and -x cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed May 28, 2023
1 parent d678b9f commit 39cc200
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 5 deletions.
39 changes: 34 additions & 5 deletions bash_completion
Original file line number Diff line number Diff line change
Expand Up @@ -457,11 +457,14 @@ _comp_compgen__error_fallback()
# @return True (0) if at least one completion is generated, False (1) if no
# completion is generated, or 2 with an incorrect usage.
#
# Usage #2: _comp_compgen [-aR|-v arr|-c cur|-C dir] name args...
# Usage #2: _comp_compgen [-aR|-v arr|-c cur|-C dir|-i cmd|-x cmd] name args...
# 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.
# OPTIONS
# -x cmd Call exported generator `_comp_xfunc_CMD_compgen_NAME`
# -i cmd Call internal generator `_comp_cmd_CMD__compgen_NAME`
# @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`
Expand Down Expand Up @@ -516,7 +519,7 @@ _comp_compgen()
shopt -u nocasematch
fi
local OPTIND=1 OPTARG="" OPTERR=0 _opt
while getopts ':av:Rc:C:lF:' _opt "$@"; do
while getopts ':av:Rc:C:lF:i:x:' _opt "$@"; do
case $_opt in
a) _append=set ;;
v)
Expand All @@ -537,6 +540,20 @@ _comp_compgen()
;;
l) _has_ifs=set _ifs=$'\n' ;;
F) _has_ifs=set _ifs=$OPTARG ;;
[ix])
if [[ ! $OPTARG ]]; then
printf 'bash_completion: %s: -%s: invalid command name `%s'\''\n' "$FUNCNAME" "$_opt" "$OPTARG" >&2
return 2
elif [[ $_icmd ]]; then
printf 'bash_completion: %s: -%s: `-i %s'\'' is already specified\n' "$FUNCNAME" "$_opt" "$_icmd" >&2
return 2
elif [[ $_xcmd ]]; then
printf 'bash_completion: %s: -%s: `-x %s'\'' is already specified\n' "$FUNCNAME" "$_opt" "$_xcmd" >&2
return 2
fi
;;&
i) _icmd=$OPTARG ;;
x) _xcmd=$OPTARG ;;
*)
printf 'bash_completion: %s: usage error\n' "$FUNCNAME" >&2
return 2
Expand All @@ -558,8 +575,16 @@ _comp_compgen()
return 2
fi

if ! declare -F "_comp_compgen_$1" &>/dev/null; then
printf 'bash_completion: %s: unrecognized category `%s'\'' (function _comp_compgen_%s not found)\n' "$FUNCNAME" "$1" "$1" >&2
local -a _generator
if [[ $_icmd ]]; then
_generator=("_comp_cmd_${_icmd//[^a-zA-Z0-9_]/_}__compgen_$1")
elif [[ $_xcmd ]]; then
_generator=(_comp_xfunc "$_xcmd" "compgen_$1")
else
_generator=("_comp_compgen_$1")
fi
if ! declare -F "${_generator[0]}" &>/dev/null; then
printf 'bash_completion: %s: unrecognized generator `%s'\'' (function %s not found)\n' "$FUNCNAME" "$1" "${_generator[0]}" >&2
return 2
fi

Expand All @@ -581,7 +606,7 @@ _comp_compgen()
# Note: we use $1 as a part of a function name, and we use $2... as
# arguments to the function if any.
# shellcheck disable=SC2145
_comp_compgen_"$@"
"${_generator[@]}" "${@:2}"
local _status=$?

# Go back to the original directory.
Expand All @@ -595,6 +620,10 @@ _comp_compgen()
fi

# usage: _comp_compgen [options] -- [compgen_options]
if [[ $_icmd || $_xcmd ]]; then
printf 'bash_completion: %s: generator name is unspecified for `%s'\''\n' "$FUNCNAME" "${_icmd:+-i $_icmd}${_xcmd:+x $_xcmd}" >&2
return 2
fi

# Note: $* in the below checks would be affected by uncontrolled IFS in
# bash >= 5.0, so we need to set IFS to the normal value. The behavior in
Expand Down
19 changes: 19 additions & 0 deletions test/fixtures/_comp_compgen/completions/compgen-cmd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Dummy completion file for _comp_compgen tests -*- shell-script -*-

_comp_xfunc_compgen_cmd1_compgen_generator1() {
_comp_compgen -- -W '5foo 6bar 7baz'
}

_comp_cmd_compgen_cmd1__compgen_generator2() {
_comp_compgen -- -W '5abc 6def 7ghi'
}

_comp_cmd_compgen_cmd1() {
local cur prev words cword comp_args
_comp_initialize -- "$@" || return
_comp_compgen -- -W '012 123 234'
_comp_compgen -ai compgen-cmd1 generator2
} &&
complete -F _comp_cmd_compgen_cmd1 compgen-cmd1

# ex: filetype=sh
11 changes: 11 additions & 0 deletions test/fixtures/_comp_compgen/completions/compgen-cmd2
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Dummy completion file for _comp_compgen tests -*- shell-script -*-

_comp_cmd_compgen_cmd2() {
local cur prev words cword comp_args
_comp_initialize -- "$@" || return
_comp_compgen -- -W '012 123 234'
_comp_compgen -ax compgen-cmd1 generator1
} &&
complete -F _comp_cmd_compgen_cmd2 compgen-cmd2

# ex: filetype=sh
18 changes: 18 additions & 0 deletions test/t/unit/test_unit_compgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,21 @@ 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"

def test_7_icmd(self, bash, functions):
with bash_env_saved(bash) as bash_env:
bash_env.write_variable(
"BASH_COMPLETION_USER_DIR", "$PWD/_comp_compgen", quote=False
)

completions = assert_complete(bash, "compgen-cmd1 '")
assert completions == ["012", "123", "234", "5abc", "6def", "7ghi"]

def test_7_xcmd(self, bash, functions):
with bash_env_saved(bash) as bash_env:
bash_env.write_variable(
"BASH_COMPLETION_USER_DIR", "$PWD/_comp_compgen", quote=False
)

completions = assert_complete(bash, "compgen-cmd2 '")
assert completions == ["012", "123", "234", "5foo", "6bar", "7baz"]

0 comments on commit 39cc200

Please sign in to comment.