Skip to content

Commit 1351139

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 1939b22 commit 1351139

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
@@ -885,13 +885,51 @@ _comp_variable_assignments()
885885
return 0
886886
}
887887

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

896934
# Initialize completion and deal with various general things: do file
897935
# and variable completion where appropriate, and adjust prev, words,
@@ -912,7 +950,28 @@ _init_completion()
912950
{
913951
local exclude="" flag outx errx inx OPTIND=1
914952

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

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

0 commit comments

Comments
 (0)