Skip to content

Commit 9438749

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 125792d commit 9438749

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
@@ -881,13 +881,51 @@ _comp_variable_assignments()
881881
return 0
882882
}
883883

884-
_comp_return_hook()
885-
{
886-
((${#FUNCNAME[*]} != 2)) && return # this _will_ need some refinement and thought
887-
echo "Hello from return hook for ${FUNCNAME[1]}"
888-
echo "words: ${words[@]}"
889-
echo "COMPREPLY: ${COMPREPLY[@]}"
884+
_comp_finalize__depth=()
885+
_comp_finalize__target=()
886+
_comp_finalize__original_return_trap=
887+
888+
# This associative array contains the finalizer commands with the key
889+
# being the name of the completed command.
890+
declare -gA BASH_COMPLETION_FINALIZE_CMD_HOOKS
891+
892+
# This array contains the general finalizer commands that will be
893+
# executed for all the commands.
894+
declare -ga BASH_COMPLETION_FINALIZE_HOOKS
895+
896+
_comp_finalize()
897+
{
898+
((${#_comp_finalize__depth[@]})) || return 0
899+
while ((${#FUNCNAME[@]} <= ${_comp_finalize__depth[-1]})); do
900+
if [[ ${#FUNCNAME[@]} -eq ${_comp_finalize__depth[-1]} && ${FUNCNAME[1]} == "${_comp_finalize__target[-1]}" ]]; then
901+
# Call finalizer for each command
902+
local cmd=${words[0]-} _comp_local_hook
903+
if [[ $cmd ]]; then
904+
_comp_local_hook=${BASH_COMPLETION_FINALIZE_CMD_HOOKS[$cmd]-}
905+
eval -- "$_comp_local_hook"
906+
fi
907+
908+
# Call general finalizers
909+
if [[ ${BASH_COMPLETION_FINALIZE_HOOKS[*]+set} ]]; then
910+
for _comp_local_hook in "${BASH_COMPLETION_FINALIZE_HOOKS[@]}"; do
911+
eval -- "$_comp_local_hook"
912+
done
913+
fi
914+
fi
915+
916+
unset -v '_comp_finalize__depth[-1]'
917+
unset -v '_comp_finalize__target[-1]'
918+
if ((${#_comp_finalize__depth[@]} == 0)); then
919+
eval -- "${_comp_finalize__original_return_trap:-trap - RETURN}"
920+
_comp_finalize__original_return_trap=
921+
break
922+
fi
923+
done
890924
}
925+
# Note: We need to set "trace" function attribute of _comp_finalize to
926+
# make the trap restoration by "trap - RETURN" take effect in the
927+
# upper level.
928+
declare -ft _comp_finalize
891929

892930
# Initialize completion and deal with various general things: do file
893931
# and variable completion where appropriate, and adjust prev, words,
@@ -908,7 +946,28 @@ _init_completion()
908946
{
909947
local exclude="" flag outx errx inx OPTIND=1
910948

911-
trap _comp_return_hook RETURN
949+
if ((${#FUNCNAME[@]} >= 2)); then
950+
# Install "_comp_finalize" to the RETURN trap when "_init_completion" is
951+
# called for the top-level completion. [ Note: the completion function may
952+
# be called recursively using "_command_offset", etc. ]
953+
if ((${#_comp_finalize__depth[@]} == 0)); then
954+
if shopt -q extdebug || shopt -qo functrace; then
955+
# If extdebug / functrace is set, we need to explicitly save and
956+
# restore the original trap handler because the outer trap handlers
957+
# will be affected by "trap - RETURN" inside functions with these
958+
# settings.
959+
_comp_finalize__original_return_trap=$(trap -p RETURN)
960+
else
961+
# Otherwise, the outer RETURN trap will be restored when the RETURN
962+
# trap is removed inside the functions using "trap - RETURN". So, we
963+
# do not need to explicitly save the outer trap handler.
964+
_comp_finalize__original_return_trap=
965+
fi
966+
trap _comp_finalize RETURN
967+
fi
968+
_comp_finalize__depth+=("${#FUNCNAME[@]}")
969+
_comp_finalize__target+=("${FUNCNAME[1]}")
970+
fi
912971

913972
while getopts "n:e:o:i:s" flag "$@"; do
914973
case $flag in

0 commit comments

Comments
 (0)