Skip to content

Instantly share code, notes, and snippets.

@hatsusato
Last active August 19, 2021 15:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hatsusato/ec5a0d8851ef7715911934cd4fd60d2b to your computer and use it in GitHub Desktop.
Save hatsusato/ec5a0d8851ef7715911934cd4fd60d2b to your computer and use it in GitHub Desktop.
#!/bin/bash
# USAGE: _alias_complete_reuse REF CMD
# use the same completion function as REF for CMD
_alias_complete_reuse() {
(($# == 2)) || return
complete -p "$1" &>/dev/null || __load_completion "$1"
$(complete -p "$1" 2>/dev/null || echo false) "$2"
}
# USAGE: _alias_complete_decompose STRING ARRAY
# decompose STRING into ARRAY of single character or quoted string
_alias_complete_decompose() {
local c q=
local -i i
local -a chars
for ((i=0; i<${#1}; i++)); do
c=${1:i:1}
if [[ $c == [\\] ]]; then
c=${1:i:2}
let i++ && :
fi
if [[ $q ]]; then
[[ $c == $q ]] && q=
chars[-1]+=$c
else
[[ $c == [\"\'] ]] && q=$c
chars+=("$c")
fi
done
local "$2" && _upvars -a${#chars[@]} "$2" "${chars[@]}"
}
# USAGE: _alias_complete_split STRING ARRAY
# split STRING into ARRAY in the same way as COMP_LINE into COMP_WORDS
_alias_complete_split() {
local w prev= next
local -a chars words=()
_alias_complete_decompose "$1" chars
for w in "${chars[@]}"; do
case $w in
["$IFS"]) prev=; continue;;
[\"\']) next=normal;;
[';|&(']) prev=; words=(); continue;;
["$COMP_WORDBREAKS"]) next=break;;
*) next=normal;;
esac
[[ $prev == $next ]] || words+=('')
words[-1]+=$w
prev=$next
done
local "$2" && _upvars -a${#words[@]} "$2" "${words[@]}"
}
# USAGE: _alias_complete_expand
# expand alias and reset COMP_{POINT,CWORD,LINE,WORDS} variables
_alias_complete_expand() {
local cmd=${COMP_WORDS[0]} alias line
local -a words args
local -i point cword
[[ -v BASH_ALIASES[$cmd] ]] || return 0
alias=${BASH_ALIASES[$cmd]}
_alias_complete_split "$alias" words
let point=$COMP_POINT+${#alias}-${#cmd} && :
let cword=$COMP_CWORD+${#words[@]}-1 && :
line=${COMP_LINE/#$cmd/$alias}
words+=("${COMP_WORDS[@]:1}")
args+=(-v COMP_POINT $point)
args+=(-v COMP_CWORD $cword)
args+=(-v COMP_LINE "$line")
args+=(-a${#words[@]} COMP_WORDS "${words[@]}")
local COMP_{POINT,CWORD,LINE,WORDS} && _upvars "${args[@]}"
}
# USAGE: _alias_complete_emulate CMD
# invoke completion function for CMD
_alias_complete_emulate() {
local cmd=$1 comp func comm
local -a reply compopt=(compopt) generate=(compgen) filter=(compgen)
if ! comp=$(complete -p "$cmd" 2>/dev/null); then
cmd=${cmd%%*/}
comp=$(complete -p "$cmd" 2>/dev/null) || return
fi
set -- $comp
[[ ${1-} == complete && ${@: -1} == $cmd ]] || return
shift
while (($# > 1)); do
case $1 in
-[abcdefgjksuv]) generate+=($1); shift 1;;
-o) compopt+=($1 "$2"); shift 2;;
-[AGW]) generate+=($1 "$2"); shift 2;;
-F) func=$2; shift 2;;
-C) comm=$(eval echo "$2"); shift 2;;
-[XPS]) filter+=($1 "$2"); shift 2;;
*) echo "ERROR: invalid complete: $comp" >&2; return 1;;
esac
done
local cur prev words cword
_init_completion || return
readarray -t reply < <("${generate[@]}" -- "$cur")
set -- "$cmd" "$cur" "$prev"
COMPREPLY=()
[[ -v func ]] && "$func" "$@"
reply+=("${COMPREPLY[@]}")
[[ -v comm ]] && readarray -O ${#reply[@]} -t reply < <("$comm" "$@")
readarray -t COMPREPLY < <(IFS=$'\n'; "${filter[@]}" -W "${reply[*]}")
((${#compopt[@]} > 1)) && "${compopt[@]}"
return 0
}
# USAGE: complete -F _alias_complete -- CMD
# perform completion after expanding alias
_alias_complete() {
local cmd comp
_alias_complete_expand
cmd=${COMP_WORDS[0]}
comp=$(complete -p "$cmd" 2>/dev/null) || _alias_complete_loader "$cmd"
[[ $comp == *' -F _alias_complete '* ]] && complete -F _minimal -- "$cmd"
_alias_complete_emulate "$cmd"
}
# USAGE: complete -D -F _alias_complete_loader
# completion loader for _alias_complete
_alias_complete_loader() {
local cmd=${1:-_EmptycmD_}
__load_completion "$cmd" && return 124
complete -F _alias_complete -- "$cmd" && return 124
} && complete -D -F _alias_complete_loader
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment