Skip to content

Instantly share code, notes, and snippets.

@ardnew
Created February 2, 2024 22:31
Show Gist options
  • Save ardnew/8499598d06a54a86b94e6107abdd80c3 to your computer and use it in GitHub Desktop.
Save ardnew/8499598d06a54a86b94e6107abdd80c3 to your computer and use it in GitHub Desktop.
bash-completion for alias of full command line (with subcommands)
#!/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