Skip to content

Commit 05b4159

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 18685e3 commit 05b4159

File tree

1 file changed

+58
-7
lines changed

1 file changed

+58
-7
lines changed

bash_completion

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -819,13 +819,46 @@ _comp_variable_assignments()
819819
return 0
820820
}
821821

822-
_comp_return_hook()
823-
{
824-
((${#FUNCNAME[*]} != 2)) && return # this _will_ need some refinement and thought
825-
echo "Hello from return hook for ${FUNCNAME[1]}"
826-
echo "words: ${words[@]}"
827-
echo "COMPREPLY: ${COMPREPLY[@]}"
822+
_comp_finalize__depth=()
823+
_comp_finalize__original_return_trap=
824+
825+
# This associative array contains the finalizer commands with the key
826+
# being the name of the completed command.
827+
declare -gA BASH_COMPLETION_FINALIZE_CMD_HOOKS=()
828+
829+
# This array contains the general finalizer commands that will be
830+
# executed for all the commands.
831+
BASH_COMPLETION_FINALIZE_HOOKS=()
832+
833+
_comp_finalize()
834+
{
835+
((${#_comp_finalize__depth[@]})) || return 0
836+
while ((${#FUNCNAME[@]} <= ${_comp_finalize__depth[-1]})); do
837+
if ((${#FUNCNAME[@]} == ${_comp_finalize__depth[-1]})); then
838+
# Call finalizer for each command
839+
local __comp_hook=${BASH_COMPLETION_FINALIZE_CMD_HOOKS[${words[0]-}]-}
840+
eval -- "$__comp_hook"
841+
842+
# Call general finalizers
843+
if ((${#BASH_COMPLETION_FINALIZE_HOOKS[@]})); then
844+
for __comp_hook in "${BASH_COMPLETION_FINALIZE_HOOKS[@]}"; do
845+
eval -- "$__comp_hook"
846+
done
847+
fi
848+
fi
849+
850+
unset -v '_comp_finalize__depth[-1]'
851+
if ((${#_comp_finalize__depth[@]} == 0)); then
852+
eval -- "${_comp_finalize__original_return_trap:-trap - RETURN}"
853+
_comp_finalize__original_return_trap=
854+
break
855+
fi
856+
done
828857
}
858+
# Note: We need to set "trace" function attribute of _comp_finalize to
859+
# make the trap restoration by "trap - RETURN" take effect in the
860+
# upper level.
861+
declare -ft _comp_finalize
829862

830863
# Initialize completion and deal with various general things: do file
831864
# and variable completion where appropriate, and adjust prev, words,
@@ -846,7 +879,25 @@ _init_completion()
846879
{
847880
local exclude="" flag outx errx inx OPTIND=1
848881

849-
trap _comp_return_hook RETURN
882+
# Install "_comp_finalize" to the RETURN trap when "_init_completion" is
883+
# called for the top-level completion. [ Note: the completion function may
884+
# be called recursively using "_command_offset", etc. ]
885+
if ((${#_comp_finalize__depth[@]} == 0)); then
886+
if shopt -q extdebug || shopt -qo functrace; then
887+
# If extdebug / functrace is set, we need to explicitly save and
888+
# restore the original trap handler because the outer trap handlers
889+
# will be affected by "trap - RETURN" inside functions with these
890+
# settings.
891+
_comp_finalize__original_return_trap=$(trap -p RETURN)
892+
else
893+
# Otherwise, the outer RETURN trap will be restored when the RETURN
894+
# trap is removed inside the functions using "trap - RETURN". So, we
895+
# do not need to explicitly save the outer trap handler.
896+
_comp_finalize__original_return_trap=
897+
fi
898+
trap _comp_finalize RETURN
899+
fi
900+
_comp_finalize__depth+=(${#FUNCNAME[@]})
850901

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

0 commit comments

Comments
 (0)