Skip to content

Commit 9661cdc

Browse files
committed
feat(bash_completion): support BASH_COMPLETION_FINALIZE{,_CMD}_HOOKS
- deal with nested completion initializations - rename the function `_comp_{return_hook => finalize}` - add an associative array `BASH_COMPLETION_FINALIZE_CMD_HOOKS` originally suggested as `_comp_return_hooks` in Ref. [1] - add a new array `BASH_COMPLETION_FINALIZE_HOOKS` [1] #720 (comment)
1 parent e6ab02f commit 9661cdc

File tree

1 file changed

+66
-7
lines changed

1 file changed

+66
-7
lines changed

bash_completion

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -902,13 +902,51 @@ _comp_variable_assignments()
902902
return 0
903903
}
904904

905-
_comp_return_hook()
906-
{
907-
((${#FUNCNAME[*]} != 2)) && return # this _will_ need some refinement and thought
908-
echo "Hello from return hook for ${FUNCNAME[1]}"
909-
echo "words: ${words[@]}"
910-
echo "COMPREPLY: ${COMPREPLY[@]}"
905+
_comp_finalize__depth=()
906+
_comp_finalize__target=()
907+
_comp_finalize__original_return_trap=
908+
909+
# This associative array contains the finalizer commands with the key
910+
# being the name of the completed command.
911+
declare -gA BASH_COMPLETION_FINALIZE_CMD_HOOKS
912+
913+
# This array contains the general finalizer commands that will be
914+
# executed for all the commands.
915+
declare -ga BASH_COMPLETION_FINALIZE_HOOKS
916+
917+
_comp_finalize()
918+
{
919+
((${#_comp_finalize__depth[@]})) || return 0
920+
while ((${#FUNCNAME[@]} <= ${_comp_finalize__depth[-1]})); do
921+
if [[ ${#FUNCNAME[@]} -eq ${_comp_finalize__depth[-1]} && ${FUNCNAME[1]-} == "${_comp_finalize__target[-1]}" ]]; then
922+
# Call finalizer for each command
923+
local cmd=${words[0]-} _comp_local_hook
924+
if [[ $cmd ]]; then
925+
_comp_local_hook=${BASH_COMPLETION_FINALIZE_CMD_HOOKS[$cmd]-}
926+
eval -- "$_comp_local_hook"
927+
fi
928+
929+
# Call general finalizers
930+
if [[ ${BASH_COMPLETION_FINALIZE_HOOKS[*]+set} ]]; then
931+
for _comp_local_hook in "${BASH_COMPLETION_FINALIZE_HOOKS[@]}"; do
932+
eval -- "$_comp_local_hook"
933+
done
934+
fi
935+
fi
936+
937+
unset -v '_comp_finalize__depth[-1]'
938+
unset -v '_comp_finalize__target[-1]'
939+
if ((${#_comp_finalize__depth[@]} == 0)); then
940+
eval -- "${_comp_finalize__original_return_trap:-trap - RETURN}"
941+
_comp_finalize__original_return_trap=
942+
break
943+
fi
944+
done
911945
}
946+
# Note: We need to set "trace" function attribute of _comp_finalize to
947+
# make the trap restoration by "trap - RETURN" take effect in the
948+
# upper level.
949+
declare -ft _comp_finalize
912950

913951
# Initialize completion and deal with various general things: do file
914952
# and variable completion where appropriate, and adjust prev, words,
@@ -943,7 +981,28 @@ _comp_initialize()
943981
{
944982
local exclude="" outx errx inx
945983

946-
trap _comp_return_hook RETURN
984+
if ((${#FUNCNAME[@]} >= 2)); then
985+
# Install "_comp_finalize" to the RETURN trap when "_init_completion" is
986+
# called for the top-level completion. [ Note: the completion function may
987+
# be called recursively using "_command_offset", etc. ]
988+
if ((${#_comp_finalize__depth[@]} == 0)); then
989+
if shopt -q extdebug || shopt -qo functrace; then
990+
# If extdebug / functrace is set, we need to explicitly save and
991+
# restore the original trap handler because the outer trap handlers
992+
# will be affected by "trap - RETURN" inside functions with these
993+
# settings.
994+
_comp_finalize__original_return_trap=$(trap -p RETURN)
995+
else
996+
# Otherwise, the outer RETURN trap will be restored when the RETURN
997+
# trap is removed inside the functions using "trap - RETURN". So, we
998+
# do not need to explicitly save the outer trap handler.
999+
_comp_finalize__original_return_trap=
1000+
fi
1001+
trap _comp_finalize RETURN
1002+
fi
1003+
_comp_finalize__depth+=("${#FUNCNAME[@]}")
1004+
_comp_finalize__target+=("${FUNCNAME[1]-}")
1005+
fi
9471006

9481007
local flag OPTIND=1 OPTARG='' OPTERR=0
9491008
while getopts "n:e:o:i:s" flag "$@"; do

0 commit comments

Comments
 (0)