Created
February 2, 2024 22:31
-
-
Save ardnew/8499598d06a54a86b94e6107abdd80c3 to your computer and use it in GitHub Desktop.
bash-completion for alias of full command line (with subcommands)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# ============================================================================== | |
# |# | |
# |# Example aliases with bash-completion support | |
# |# | |
# | |
#complete-alias sc systemctl # sc <tab> => systemctl <tab> | |
#complete-alias scs systemctl status # scs <tab> => systemctl status <tab> | |
# | |
# ============================================================================== | |
# Wrapper for `_completion_alias` that automatically determines the completion | |
# function to use via `_completion_handler`. | |
complete-alias() { | |
if [[ ${#} -lt 2 ]]; then | |
printf -- 'usage:\n\tcomplete-alias alias command...\n' >&2 | |
return 1 | |
fi | |
_completion_alias "${1}" $( _completion_handler "${2}" ) "${@:2}" | |
} | |
# _completion_alias installs an existing bash-completion handler for a seperate | |
# executable or alias. | |
# | |
# For example, "complete-alias scs _systemctl systemctl status" will use the | |
# existing bash-completion function "_systemctl" to auto-complete arguments | |
# for "systemctl status" when typing the command "scs <tab>". | |
# | |
# The bash-completion function for a given command can be found using the | |
# function `_completion_handler` below. | |
_completion_alias() { | |
local name func | |
case ${#} in | |
0|1) return 1 ;; | |
2) _completion_alias "${1}" $( _completion_handler "${2}" ) "${2}" | |
return $? ;; | |
*) name=${1} | |
func=${2} ;; | |
esac | |
# if the completer is dynamic and not yet loaded, try to load it | |
# automatically using the given command | |
if [[ $( type -t "${func}" ) != function ]]; then | |
type -p _completion_loader &> /dev/null && | |
_completion_loader "${@:3}" | |
fi | |
[[ $( type -t "${func}" ) == function ]] || return 2 | |
comp=$( tr -d -c '[A-Za-z_]' <<< "${name}" ) | |
eval " | |
function _${comp} { | |
(( COMP_CWORD += $(( ${#} - 3 )) )) | |
COMP_LINE=\${COMP_LINE//${name}/${*:3}} | |
_comp_split COMP_WORDS \"\${COMP_LINE}\" | |
local -a args=( \"\${@}\" ) | |
for i in \${!args[@]}; do | |
[[ \"\${args[\${i}]}\" != "${name}" ]] || | |
args[\${i}]=${3} | |
done | |
"${func}" \"\${args[@]}\" | |
return 0 | |
} | |
" | |
complete -F "_${comp}" "${name}" | |
alias ${name}="${*:3}" | |
} | |
# Print the bash-completion function used to generate possible completions for | |
# a given command. Any completion options defined with the original spec are | |
# also carried over to the resulting spec. | |
# | |
# If no completion spec is defined for the given command, uses the default | |
# function "_minimal" provided by the bash-completion package. | |
# | |
# The primary use-case is for installing aliases that use the bash-completion | |
# results of other command lines, as with `_completion_alias` defined above. | |
_completion_handler() { | |
local cmd=${1} compcmd=${1} | |
local cspec=$( complete -p "${cmd}" 2>/dev/null ) | |
if [[ -z ${cspec} && "${cmd}" == */* ]]; then | |
cspec=$( complete -p ${cmd##*/} 2>/dev/null ) | |
[[ -n ${cspec} ]] && compcmd=${cmd##*/} | |
fi | |
if [[ -z ${cspec} ]]; then | |
compcmd=${cmd##*/} | |
_completion_loader ${compcmd} | |
cspec=$( complete -p ${compcmd} 2>/dev/null ) | |
fi | |
if [[ -n ${cspec} ]]; then | |
# apply and remove all option arguments | |
while [[ ${cspec} == *" -o "* ]]; do | |
cspec=${cspec#*-o } | |
compopt -o "${cspec%% *}" "${compcmd}" | |
cspec=${cspec#${cspec%% *} } | |
done | |
if [[ ${cspec#* -F } != "${cspec}" ]]; then | |
# easy case, we have a "<function>" arg of flag -F | |
local func=${cspec#*-F } | |
echo ${func%% *} | |
elif [[ ${cspec#* -C } != "${cspec}" ]]; then | |
local call=${cspec#*-C } | |
call=${call%% *} | |
# declare a named function that calls "<command>" arg of flag -C | |
eval "function _${call##*/} { ${call}; }" | |
#export -f _${call##*/} | |
echo "_${call##*/}" | |
else | |
# all other cases, we remove what we don't want. | |
cspec=${cspec#complete} # leading "complete" command | |
cspec=${cspec%%${compcmd}} # trailing "<compcmd>" (target) command | |
# whatever remains is our function | |
echo "${cspec}" | |
fi | |
# ============================================= | |
# TBD: handle builtin and action completions? | |
# ============================================= | |
else | |
# default completion handler | |
echo '_minimal' | |
fi | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment