Skip to content

Instantly share code, notes, and snippets.

@GeneralD
Created July 23, 2025 16:12
Show Gist options
  • Select an option

  • Save GeneralD/b920684a214d6bca59840f1afe16aa9a to your computer and use it in GitHub Desktop.

Select an option

Save GeneralD/b920684a214d6bca59840f1afe16aa9a to your computer and use it in GitHub Desktop.
# skim-tab: Replace zsh's default completion selection menu with skim!
# Based on fzf-tab by Aloxaf
# temporarily change options
'builtin' 'local' '-a' '_stb_opts'
[[ ! -o 'aliases' ]] || _stb_opts+=('aliases')
[[ ! -o 'sh_glob' ]] || _stb_opts+=('sh_glob')
[[ ! -o 'no_brace_expand' ]] || _stb_opts+=('no_brace_expand')
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
# disable aliases
typeset _stb_aliases="$(builtin alias -Lm '[^+]*')"
builtin unalias -m '[^+]*'
# Hook compadd to capture completions
-stb-compadd() {
# parse all options
local -A apre hpre dscrs _oad _mesg
local -a isfile _opts __ expl
zparseopts -a _opts P:=apre p:=hpre d:=dscrs X+:=expl O:=_oad A:=_oad D:=_oad f=isfile \
i: S: s: I: x:=_mesg r: R: W: F: M+: E: q e Q n U C \
J:=__ V:=__ a=__ l=__ k=__ o::=__ 1=__ 2=__
# store $curcontext for further usage
_stb_curcontext=${curcontext#:}
# just delegate and leave if any of -O, -A or -D are given or skim-tab is not enabled
if (( $#_oad != 0 || ! IN_SKIM_TAB )); then
builtin compadd "$@"
return
fi
# store matches in $__hits and descriptions in $__dscr
local -a __hits __dscr
if (( $#dscrs == 1 )); then
__dscr=( "${(@P)${(v)dscrs}}" )
fi
builtin compadd -A __hits -D __dscr "$@"
local ret=$?
if (( $#__hits == 0 )); then
return $ret
fi
# only store the first `-X`
expl=$expl[2]
# keep order of group description
[[ -n $expl ]] && _stb_groups+=$expl
# store these values in _stb_compcap
local -a keys=(apre hpre PREFIX SUFFIX IPREFIX ISUFFIX)
local key expanded __tmp_value=$'<\0>' # placeholder
for key in $keys; do
expanded=${(P)key}
if [[ -n $expanded ]]; then
__tmp_value+=$'\0'$key$'\0'$expanded
fi
done
if [[ -n $expl ]]; then
# store group index
__tmp_value+=$'\0group\0'$_stb_groups[(ie)$expl]
fi
if [[ -n $isfile ]]; then
# NOTE: need a extra ${} here or ~ expansion won't work
__tmp_value+=$'\0realdir\0'${${(Qe)~${:-$IPREFIX$hpre}}}
fi
_opts+=("${(@kv)apre}" "${(@kv)hpre}" $isfile)
__tmp_value+=$'\0args\0'${(pj:\1:)_opts}
# dscr - the string to show to users
# word - the string to be inserted
local dscr word i
for i in {1..$#__hits}; do
word=$__hits[i] dscr=$__dscr[i]
if [[ -n $dscr ]]; then
dscr=${dscr//$'\n'}
elif [[ -n $word ]]; then
dscr=$word
fi
_stb_compcap+=$dscr$'\2'$__tmp_value$'\0word\0'$word
done
# tell zsh that the match is successful
builtin compadd "$@"
}
-stb-complete() {
local -Ua _stb_groups
local choice choices _stb_curcontext bs=$'\2' nul=$'\0'
local ret=0
# must run with user options; don't move `emulate -L zsh` above this line
COLUMNS=500 _stb__main_complete "$@" || ret=$?
emulate -L zsh -o extended_glob
local _stb_complist=()
# Generate completion list from captured completions
if (( $#_stb_compcap == 0 )); then
return 1
fi
local i
local -A seen_words
for i in {1..$#_stb_compcap}; do
local display_text="${_stb_compcap[i]%$bs*}"
# Extract the actual word from the stored data
local -A v=("${(@0)${_stb_compcap[i]#*$bs}}")
local word="$v[word]"
# Only add if we haven't seen this word before
if [[ -z ${seen_words[$word]} ]]; then
_stb_complist+="$display_text"
seen_words[$word]=1
fi
done
case $#_stb_complist in
0) return 1;;
1)
choices=("${_stb_complist[1]}")
;;
*)
if [[ $compstate[insert] == *"unambiguous" ]] \
&& [[ -n $compstate[unambiguous] ]] \
&& [[ "$compstate[unambiguous]" != "$compstate[quote]$IPREFIX$PREFIX$compstate[quote]" ]]; then
compstate[list]=
compstate[insert]=unambiguous
_stb_finish=1
return 0
fi
# Use skim to select from completions with --no-hscroll
local -a skim_args=(
--no-hscroll
--no-sort
)
choices=("${(@f)"$(builtin print -rl -- $_stb_complist | sk "${skim_args[@]}")"}")
ret=$?
;;
esac
# Store choices for application
_stb_choices=("${(@)choices}")
compstate[list]=
compstate[insert]=
return $ret
}
_stb-apply() {
local choice bs=$'\2'
for choice in "$_stb_choices[@]"; do
[[ -n $choice ]] || continue
local -A v=("${(@0)${_stb_compcap[(r)${(b)choice}$bs*]#*$bs}}")
local -a args=("${(@ps:\1:)v[args]}")
[[ -z $args[1] ]] && args=() # don't pass an empty string
IPREFIX=${v[IPREFIX]-} PREFIX=${v[PREFIX]-} SUFFIX=${v[SUFFIX]-} ISUFFIX=${v[ISUFFIX]-}
builtin compadd "${args[@]:--Q}" -Q -- "$v[word]"
done
compstate[list]=
if (( $#_stb_choices == 1 )); then
compstate[insert]='1'
[[ $RBUFFER == ' '* ]] || compstate[insert]+=' '
elif (( $#_stb_choices > 1 )); then
compstate[insert]='all'
fi
}
skim-tab-complete() {
local _stb_choices=() _stb_compcap=() _stb_finish=0 ret=0
local IN_SKIM_TAB=1
# Hide cursor during completion to prevent display issues
echoti civis >/dev/tty 2>/dev/null
{
# Call original completion widget which will trigger our hooked _main_complete
zle .stb-orig-$_stb_orig_widget || ret=$?
# Apply the selected choices if completion didn't finish early
if (( ! ret && ! _stb_finish )); then
zle _stb-apply || ret=$?
fi
} always {
# Always restore cursor and redisplay line
echoti cnorm >/dev/tty 2>/dev/null
zle .reset-prompt
zle .redisplay
}
return $ret
}
disable-skim-tab() {
emulate -L zsh -o extended_glob
(( $+_stb_orig_widget )) || return 0
bindkey '^I' $_stb_orig_widget
unset _stb_orig_widget
# unhook compadd
unfunction compadd 2>/dev/null
functions[_main_complete]=$functions[_stb__main_complete]
}
enable-skim-tab() {
emulate -L zsh -o extended_glob
(( ! $+_stb_orig_widget )) || disable-skim-tab
typeset -g _stb_orig_widget="${${$(builtin bindkey '^I')##* }:-expand-or-complete}"
if (( ! $+widgets[.stb-orig-$_stb_orig_widget] )); then
zle -A $_stb_orig_widget .stb-orig-$_stb_orig_widget
fi
bindkey -M emacs '^I' skim-tab-complete
bindkey -M viins '^I' skim-tab-complete
# make sure we can copy _main_complete
autoload +X -Uz _main_complete
# hook compadd
functions[compadd]=$functions[-stb-compadd]
# hook _main_complete to trigger skim-tab
functions[_stb__main_complete]=$functions[_main_complete]
function _main_complete() { -stb-complete "$@" }
}
toggle-skim-tab() {
emulate -L zsh -o extended_glob
if (( $+_stb_orig_widget )); then
disable-skim-tab
else
enable-skim-tab
fi
}
# Create ZLE widgets
zle -N skim-tab-complete
zle -N toggle-skim-tab
# Register as a completion widget for proper completion state handling
zle -C _stb-apply complete-word _stb-apply
# Enable skim-tab by default
enable-skim-tab
# restore aliases
eval "$_stb_aliases"
builtin unset _stb_aliases
# restore options
(( ${#_stb_opts} )) && setopt ${_stb_opts[@]}
'builtin' 'unset' '_stb_opts'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment