Skip to content

Commit 2c69bc0

Browse files
authored
Merge pull request #1222 from akinomyoga/compgen-V
fix: use Bash 5.3 `compgen -V` to generate completions including newlines
2 parents 01b34cb + 2b5f9fa commit 2c69bc0

File tree

1 file changed

+121
-50
lines changed

1 file changed

+121
-50
lines changed

bash_completion

Lines changed: 121 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -655,29 +655,111 @@ _comp_compgen()
655655
printf 'bash_completion: %s: unrecognized generator `%s'\'' (function %s not found)\n' "$FUNCNAME" "$1" "${_generator[0]}" >&2
656656
return 2
657657
fi
658+
shift
658659

659-
((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}"
660+
_comp_compgen__call_generator "$@"
661+
else
662+
# usage: _comp_compgen [options] -- [compgen_options]
663+
if [[ $_icmd || $_xcmd ]]; then
664+
printf 'bash_completion: %s: generator name is unspecified for `%s'\''\n' "$FUNCNAME" "${_icmd:+-i $_icmd}${_xcmd:+x $_xcmd}" >&2
665+
return 2
666+
fi
667+
668+
# Note: $* in the below checks would be affected by uncontrolled IFS in
669+
# bash >= 5.0, so we need to set IFS to the normal value. The behavior
670+
# in bash < 5.0, where unquoted $* in conditional command did not honor
671+
# IFS, was a bug.
672+
# Note: Also, ${_cur:+-- "$_cur"} and ${_append:+-a} would be affected
673+
# by uncontrolled IFS.
674+
local IFS=$' \t\n'
675+
# Note: extglob *\$?(\{)[0-9]* can be extremely slow when the string
676+
# "${*:2:_nopt}" becomes longer, so we test \$[0-9] and \$\{[0-9]
677+
# separately.
678+
if [[ $* == *\$[0-9]* || $* == *\$\{[0-9]* ]]; then
679+
printf 'bash_completion: %s: positional parameter $1, $2, ... do not work inside this function\n' "$FUNCNAME" >&2
680+
return 2
681+
fi
660682

683+
_comp_compgen__call_builtin "$@"
684+
fi
685+
}
686+
687+
# Helper function for _comp_compgen. This function calls a generator.
688+
# @param $1... generator_args
689+
# @var[in] _dir
690+
# @var[in] _cur
691+
# @arr[in] _generator
692+
# @arr[in] _upvars
693+
# @var[in] _append
694+
# @var[in] _var
695+
_comp_compgen__call_generator()
696+
{
697+
((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}"
698+
699+
if [[ $_dir ]]; then
700+
local _original_pwd=$PWD
701+
local PWD=${PWD-} OLDPWD=${OLDPWD-}
702+
# Note: We also redirect stdout because `cd` may output the target
703+
# directory to stdout when CDPATH is set.
704+
command cd -- "$_dir" &>/dev/null ||
705+
{
706+
_comp_compgen__error_fallback
707+
return
708+
}
709+
fi
710+
711+
local _comp_compgen__append=$_append
712+
local _comp_compgen__var=$_var
713+
local _comp_compgen__cur=$_cur cur=$_cur
714+
# Note: we use $1 as a part of a function name, and we use $2... as
715+
# arguments to the function if any.
716+
# shellcheck disable=SC2145
717+
"${_generator[@]}" "$@"
718+
local _status=$?
719+
720+
# Go back to the original directory.
721+
# Note: Failure of this line results in the change of the current
722+
# directory visible to the user. We intentionally do not redirect
723+
# stderr so that the error message appear in the terminal.
724+
# shellcheck disable=SC2164
725+
[[ $_dir ]] && command cd -- "$_original_pwd"
726+
727+
return "$_status"
728+
}
729+
730+
# Helper function for _comp_compgen. This function calls the builtin compgen.
731+
# @param $1... compgen_args
732+
# @var[in] _dir
733+
# @var[in] _ifs
734+
# @var[in] _cur
735+
# @arr[in] _upvars
736+
# @var[in] _append
737+
# @var[in] _var
738+
if ((BASH_VERSINFO[0] > 5 || BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3)); then
739+
# bash >= 5.3 has `compgen -V array_name`
740+
_comp_compgen__call_builtin()
741+
{
661742
if [[ $_dir ]]; then
662743
local _original_pwd=$PWD
663744
local PWD=${PWD-} OLDPWD=${OLDPWD-}
664745
# Note: We also redirect stdout because `cd` may output the target
665746
# directory to stdout when CDPATH is set.
666-
command cd -- "$_dir" &>/dev/null ||
667-
{
668-
_comp_compgen__error_fallback
669-
return
670-
}
747+
command cd -- "$_dir" &>/dev/null || {
748+
_comp_compgen__error_fallback
749+
return
750+
}
671751
fi
672752

673-
local _comp_compgen__append=$_append
674-
local _comp_compgen__var=$_var
675-
local _comp_compgen__cur=$_cur cur=$_cur
676-
# Note: we use $1 as a part of a function name, and we use $2... as
677-
# arguments to the function if any.
678-
# shellcheck disable=SC2145
679-
"${_generator[@]}" "${@:2}"
680-
local _status=$?
753+
local -a _result=()
754+
755+
# Note: We specify -X '' to exclude empty completions to make the
756+
# behavior consistent with the implementation for Bash < 5.3 where
757+
# `_comp_split -l` removes empty lines. If the caller specifies -X
758+
# pat, the effect of -X '' is overwritten by the specified one.
759+
IFS=$_ifs compgen -V _result -X '' "$@" ${_cur:+-- "$_cur"} || {
760+
_comp_compgen__error_fallback
761+
return
762+
}
681763

682764
# Go back to the original directory.
683765
# Note: Failure of this line results in the change of the current
@@ -686,46 +768,35 @@ _comp_compgen()
686768
# shellcheck disable=SC2164
687769
[[ $_dir ]] && command cd -- "$_original_pwd"
688770

689-
return "$_status"
690-
fi
691-
692-
# usage: _comp_compgen [options] -- [compgen_options]
693-
if [[ $_icmd || $_xcmd ]]; then
694-
printf 'bash_completion: %s: generator name is unspecified for `%s'\''\n' "$FUNCNAME" "${_icmd:+-i $_icmd}${_xcmd:+x $_xcmd}" >&2
695-
return 2
696-
fi
697-
698-
# Note: $* in the below checks would be affected by uncontrolled IFS in
699-
# bash >= 5.0, so we need to set IFS to the normal value. The behavior in
700-
# bash < 5.0, where unquoted $* in conditional command did not honor IFS,
701-
# was a bug.
702-
# Note: Also, ${_cur:+-- "$_cur"} and ${_append:+-a} would be affected by
703-
# uncontrolled IFS.
704-
local IFS=$' \t\n'
705-
# Note: extglob *\$?(\{)[0-9]* can be extremely slow when the string
706-
# "${*:2:_nopt}" becomes longer, so we test \$[0-9] and \$\{[0-9]
707-
# separately.
708-
if [[ $* == *\$[0-9]* || $* == *\$\{[0-9]* ]]; then
709-
printf 'bash_completion: %s: positional parameter $1, $2, ... do not work inside this function\n' "$FUNCNAME" >&2
710-
return 2
711-
fi
712-
713-
local _result
714-
_result=$(
715-
if [[ $_dir ]]; then
716-
# Note: We also redirect stdout because `cd` may output the target
717-
# directory to stdout when CDPATH is set.
718-
command cd -- "$_dir" &>/dev/null || return
771+
((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}"
772+
((${#_result[@]})) || return
773+
if [[ $_append ]]; then
774+
eval -- "$_var+=(\"\${_result[@]}\")"
775+
else
776+
eval -- "$_var=(\"\${_result[@]}\")"
719777
fi
720-
IFS=$_ifs compgen "$@" ${_cur:+-- "$_cur"}
721-
) || {
722-
_comp_compgen__error_fallback
723778
return
724779
}
780+
else
781+
_comp_compgen__call_builtin()
782+
{
783+
local _result
784+
_result=$(
785+
if [[ $_dir ]]; then
786+
# Note: We also redirect stdout because `cd` may output the target
787+
# directory to stdout when CDPATH is set.
788+
command cd -- "$_dir" &>/dev/null || return
789+
fi
790+
IFS=$_ifs compgen "$@" ${_cur:+-- "$_cur"}
791+
) || {
792+
_comp_compgen__error_fallback
793+
return
794+
}
725795

726-
((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}"
727-
_comp_split -l ${_append:+-a} "$_var" "$_result"
728-
}
796+
((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}"
797+
_comp_split -l ${_append:+-a} "$_var" "$_result"
798+
}
799+
fi
729800

730801
# usage: _comp_compgen_set [words...]
731802
# Reset COMPREPLY with the specified WORDS. If no arguments are specified, the

0 commit comments

Comments
 (0)