move to new repo

This commit is contained in:
tumillanino
2026-03-24 20:17:22 +11:00
parent 083b57c87b
commit 35bcba335a
1910 changed files with 161640 additions and 41 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@@ -0,0 +1,6 @@
#compdef -command-
local -P ret=1
_autocd "$@" &&
ret=0
return ret

View File

@@ -0,0 +1,10 @@
#autoload
local -Pi len=$(( ${@[(i)(-|--)]} - 1 ))
(( len < $# )) &&
return len
len=${@[(I)-*]}
[[ $@[len] == -*[PSpsiIdJVXxrRWFMOAD] ]] &&
(( len++ ))
return len

View File

@@ -0,0 +1,164 @@
#autoload
_autocomplete__history_lines() {
setopt localoptions multibyte
# Don't run more than once.
(( _matcher_num > 1 )) &&
return 1
local -P lbuffer='' rbuffer=''
(( CURRENT > 1 )) &&
lbuffer="${(j.[[:blank:]]##.)${(@b)words[1,CURRENT-1]}}[[:blank:]]##"
(( CURRENT < $#words[@] )) &&
rbuffer="[[:blank:]]##${(j.[[:blank:]]##.)${(@b)words[CURRENT+1,-1]}}"
lbuffer="$lbuffer${(b)QIPREFIX}"
rbuffer="${(b)QISUFFIX}$rbuffer"
local -P query=''
if [[ -n $words[CURRENT] ]]; then
local -Pa includes=( "${(@s..b)^words[CURRENT]}" )
local -Pa excludes=( "(|[^${(@s..b)^words[CURRENT]}\n;]#)" )
local -Pa tokens=( ${(@)excludes:^includes} )
query="((#l)$tokens[2]${(j..)tokens[3,-1]})"
local -P no_delim='[^\n;]#' pre='' post=''
if [[ -z $lbuffer ]]; then
pre='*'
else
pre=$no_delim
fi
if [[ -z $rbuffer ]]; then
post='*'
else
post=$no_delim
fi
query="(|$pre)$query$post"
else
query='()*'
fi
[[ $curcontext == *-incremental-* ]]
local -Pi is_incremental=$(( ! ? ))
# Non-incremental search potentially adds a lot of completions, which can be quite slow.
(( is_incremental )) ||
zle -R 'Loading...'
# Using fc is way faster than using $history.
local -P output="$( fc -lrm "$lbuffer$query$rbuffer" -1 1 2> /dev/null )"
# No results
[[ -z $output ]] &&
return 1
local -aU displays=( "${(f)output}" )
local -P numpat='[[:blank:]]#(<->)[*[:blank:]][[:blank:]]'
local -P groups="${(l:$(( 2 * $#words[CURRENT] ))::=0:):-}"
_comp_colors=(
"=(#b)${numpat}${lbuffer}(${query})${rbuffer}${rbuffer:+[[:blank:]]#}=2=2=0=0=30;103$groups"
"=(#b)${numpat}${lbuffer}(${query})*=2=2=0=30;103$groups"
"=(#b)${numpat}${lbuffer}(*)=2=2=0"
"=(#b)${numpat}*=0=2"
${(M)_comp_colors:#ma=*}
)
local -Pi excess= index= max= list_lines=
if (( is_incremental )); then
.autocomplete:async:list-choices:max-lines
(( list_lines = _autocomplete__max_lines ))
(( max = 16 * list_lines )) # Buffer for bubbling up more relevant results.
else
zstyle -s ":autocomplete:${curcontext}:" list-lines list_lines ||
(( list_lines = $LINES / 2 ))
(( max = $list_lines ))
fi
if [[ -o histfindnodups ]]; then
local -PaU uniques=()
local -Pa lines=()
local -Pi size=0
for index in {$#displays[@]..1}; do
uniques+=( ${displays[index]##$~numpat} )
(( $#uniques[@] > size )) &&
lines+=( "$displays[index]" )
(( size = $#uniques ))
(( size < max )) ||
break
done
displays=( "${(aO)lines[@]}" )
else
(( excess = $#displays[@] - max ))
(( excess > 0 )) &&
shift $excess displays
fi
local -P pop=
if (( is_incremental )); then
pop=-p
if [[ -z $words[CURRENT] ]]; then
displays=( ${(@aO)displays} )
else
local -a match=() mbegin=() mend=()
local -Pi num=0
# Fuzzy sort
for index in {1..$#displays[@]}; do
num=${(SM)${(M)displays[index]##$~numpat}##<->}
displays[index]=${history[$num]:/(#b)$~lbuffer($~query)$~rbuffer/$((
HISTNO - num + 64 * $#match[3] + 16 * mbegin[3] + 4 * $#match[1]
))}$'\0'$displays[index]
done
displays=( ${${(@n)displays}[@]##<->$'\0'} )
fi
fi
(( excess = $#displays[@] - list_lines ))
(( excess > 0 )) &&
shift $pop $excess displays
# To avoid wrapping, each completion should be one char less than terminal width.
displays=( ${(@mr:COLUMNS-1:)displays} )
local -Pa matches=()
for index in "${(MS)displays[@]##<->}"; do
matches+=( "${${history[$index]##$~lbuffer}%%$~rbuffer}" )
done
if [[ -z $matches ]]; then
return 1
fi
local -Pa suf=( -S '' )
if [[ $WIDGETSTYLE == *-select* && $#matches[@] > 1 ]] &&
zstyle -T ":autocomplete:${curcontext}:history-lines" add-semicolon; then
suf=( -S ';' -R _autocomplete__history_lines_suffix )
fi
local tag=history-lines
_comp_tags=" $tag"
local _comp_no_ignore=1
local -a expl=()
_description -2V $tag expl ''
builtin compadd "$suf[@]" -QU -ld displays "$expl[@]" -a matches
}
if is-at-least 5.9; then
_autocomplete__history_lines_suffix() {
if [[ SUFFIX_ACTIVE -ne 0 && $WIDGET != history-search-backward ]]; then
LBUFFER="$LBUFFER[1,SUFFIX_START]"
RBUFFER="$RBUFFER[SUFFIX_END,-1]"
fi
}
else
_autocomplete__history_lines_suffix() {
[[ $KEYS[-1] != $'\C-@' ]] && LBUFFER=$LBUFFER[1,-1-$1]
}
fi
_autocomplete__history_lines "$@"

View File

@@ -0,0 +1,38 @@
#autoload
zmodload -Fa zsh/parameter p:functions
# Don't run more than once.
(( _matcher_num > 1 )) &&
return 1
local -aU files
local -a expl expl_dirs expl_files displ
local -Pi ret i
local -P singular plural tag
.autocomplete:async:list-choices:max-lines
chpwd_recent_filehandler
files=( "$reply[@]" )
# -V: don't sort
_description -V recent-directories expl_dirs 'recent directory'
_description -V recent-files expl_files 'recent file'
_comp_tags+=' recent-directories recent-files'
ret=1
for (( i = 1 ; i <= $#files && compstate[list_lines] < _autocomplete__max_lines ; i++ )); do
displ=( "$files[i]" )
if [[ -d $files[i] ]]; then
expl=( "$expl_dirs[@]" )
else
expl=( "$expl_files[@]" )
fi
compadd "$expl[@]" -ld displ -P "${${displ[1]:h}%/}/" -fW "${${files[i]:h}%/}/" \
-- "$files[i]:t" &&
ret=0
done
return ret

View File

@@ -0,0 +1,15 @@
#autoload
local -Pa comptags=() spacetags=()
if [[ $compstate[old_list] == keep ]]; then
comptags=( $=_lastcomp[tags] )
else
comptags=( $=_comp_tags )
fi
comptags=( ${(u)comptags} )
local -a match mbegin mend
builtin zstyle -a ":autocomplete:$WIDGET:" add-space spacetags ||
spacetags=( executables aliases functions builtins reserved-words commands )
[[ -n ${comptags:*spacetags} ]]

View File

@@ -0,0 +1,9 @@
#autoload
local -a match=() mbegin=() mend=() # `zstyle` for some reason assigns these.
[[ $_completer == _expand ]] &&
return 1
[[ $WIDGET == *insert-unambiguous* ]] ||
builtin zstyle -t ":autocomplete:$curcontext" insert-unambiguous

View File

@@ -0,0 +1,43 @@
#autoload
[[ $_comp_tags == *\ (history-lines|recent-)* ]] &&
return 1
# Is not going to be correct.
[[ -v _autocomplete__partial_list ]] &&
return 1
# No need to update.
[[ $compstate[old_list] == keep ]] &&
return 1
# Doesn't make sense to show.
[[ compstate[nmatches] -lt 2 || $_completer == expand ]] &&
return 1
# Nothing to insert.
[[ -z $compstate[unambiguous] ]] &&
return 1
# Substring is already present.
local -P word=$IPREFIX$PREFIX$SUFFIX$ISUFFIX
[[ -n $word && $word == *$compstate[unambiguous]* ]] &&
return 1
local -P tag=unambiguous
_tags $tag
_tags ||
return
_requested $tag ||
return
# Retrieve highlight value.
local format
zstyle -s ":completion:${curcontext}:unambiguous" format format ||
format=$'%{\e[0;2m%}%Bcommon substring:%b %0F%11K%d%f%k'
zformat -F format "$format" "d:$compstate[unambiguous]"
builtin compadd -J "$tag" -x "$format"
false

View File

@@ -0,0 +1,694 @@
#!/bin/zsh
zmodload -F zsh/zpty b:zpty
zmodload -F zsh/parameter p:funcstack p:functions p:parameters
zmodload -F zsh/system b:sysopen p:sysparams
zmodload -F zsh/zselect b:zselect
zmodload -F zsh/terminfo b:echoti p:terminfo
zmodload -F zsh/zutil b:zparseopts
builtin autoload -RUz \
add-zle-hook-widget \
is-at-least
typeset -g ZSH_AUTOSUGGEST_USE_ASYNC=yes
typeset -g _autocomplete__overhead=0
${0}:precmd() {
[[ -v ZSH_AUTOSUGGEST_IGNORE_WIDGETS ]] &&
ZSH_AUTOSUGGEST_IGNORE_WIDGETS+=(
history-incremental-search-backward
recent-paths
.autocomplete:async:complete:fd-widget
)
# Start names with `.` to avoid getting wrapped by syntax highlighting.
builtin zle -N .autocomplete:async:pty:zle-widget
builtin zle -C .autocomplete:async:pty:completion-widget list-choices .autocomplete:async:pty:completion-widget
builtin zle -N .autocomplete:async:complete:fd-widget
builtin zle -N .autocomplete:async:wait:fd-widget
builtin zle -C ._list_choices list-choices .autocomplete:async:list-choices:completion-widget
builtin zle -N history-incremental-search-backward .autocomplete:async:toggle-context
builtin zle -N recent-paths .autocomplete:async:toggle-context
add-zle-hook-widget line-init .autocomplete:async:reset-context
add-zle-hook-widget line-pre-redraw .autocomplete:async:complete
add-zle-hook-widget line-finish .autocomplete:async:clear
add-zle-hook-widget isearch-update .autocomplete:async:isearch-update
add-zle-hook-widget isearch-exit .autocomplete:async:isearch-exit
}
.autocomplete:async:toggle-context() {
if [[ $curcontext == $WIDGET* ]]; then
unset curcontext
else
typeset -g curcontext=${WIDGET}:::
fi
zle .autocomplete:async:complete -w
}
.autocomplete:async:reset-context() {
.autocomplete:async:reset-state
typeset -g curcontext=
builtin zstyle -s :autocomplete: default-context curcontext
.autocomplete:async:complete
return 0
}
.autocomplete:async:isearch-update() {
typeset -gi _autocomplete__isearch=1
}
.autocomplete:async:isearch-exit() {
unset _autocomplete__isearch
}
.autocomplete:async:save-state() {
typeset -g \
_autocomplete__curcontext=$curcontext \
_autocomplete__lbuffer="$LBUFFER" \
_autocomplete__rbuffer="$RBUFFER"
}
.autocomplete:async:same-state() {
[[ -v _autocomplete__curcontext && $_autocomplete__curcontext == $curcontext &&
-v _autocomplete__lbuffer && $_autocomplete__lbuffer == $LBUFFER &&
-v _autocomplete__rbuffer && $_autocomplete__rbuffer == $RBUFFER ]]
}
.autocomplete:async:reset-state() {
unset \
_autocomplete__curcontext \
_autocomplete__lbuffer \
_autocomplete__rbuffer
}
.autocomplete:async:complete() {
if [[ -v _autocomplete__inserted ]]; then
unset _autocomplete__inserted
typeset -g curcontext=
builtin zstyle -s :autocomplete: default-context curcontext
fi
.autocomplete:async:save-state
.autocomplete__zle-flags ||
return 0
(( KEYS_QUEUED_COUNT || PENDING )) &&
return
[[ -v ZSH_AUTOSUGGEST_IGNORE_WIDGETS ]] && (( ZSH_AUTOSUGGEST_IGNORE_WIDGETS[(I)$LASTWIDGET] )) &&
unset POSTDISPLAY
# Don't get activated by asynchronous widgets.
[[ $LASTWIDGET == (autosuggest-suggest|.autocomplete:async:*:fd-widget) ]] &&
return 0
{
if (( REGION_ACTIVE )) ||
[[ -v _autocomplete__isearch && $LASTWIDGET == *(incremental|isearch)* ]]; then
builtin zle -Rc
return 0
fi
builtin zstyle -t ":autocomplete:${LASTWIDGET}:" ignore &&
return 0
local -Pa ignored=(
'_complete_help'
'(copy|insert)-*-word'
'describe-key-briefly'
'(|reverse-)menu-complete'
'what-cursor-position'
'where-is'
)
[[ ${LASTWIDGET##.} == (${(~j:|:)~ignored}) ]] &&
return 0
[[ $KEYS == ([\ -+*]|$'\e\t') ]] &&
builtin zle -Rc
# WORKAROUND: #549 Bug in zdharma/fast-syntax-highlighting.
[[ -v _FAST_MAIN_CACHE ]] &&
_zsh_highlight
typeset -ga _autocomplete__region_highlight=( "$region_highlight[@]" )
if [[ -v ZSH_AUTOSUGGEST_IGNORE_WIDGETS ]] &&
(( ZSH_AUTOSUGGEST_IGNORE_WIDGETS[(I)$LASTWIDGET] )); then
unset POSTDISPLAY
fi
.autocomplete:async:wait
}
return 0
}
.autocomplete:async:clear() {
unset curcontext _autocomplete__isearch
.autocomplete:async:reset-context
builtin zle -Rc
return 0
}
.autocomplete:async:wait() {
local fd=
sysopen -r -o cloexec -u fd <(
local -F seconds=
builtin zstyle -s :autocomplete: delay seconds ||
builtin zstyle -s :autocomplete: min-delay seconds ||
(( seconds = 0.05 ))
(( seconds = max( 0, seconds - _autocomplete__overhead ) ))
# Convert to 100ths of a second for `zselect -t`.
# WORKAROUND: #441 Directly using $(( [#10] … max( … ) )) leads to 0 in Zsh 5.9, as the result
# of max() gets converted to an integer _before_ being multiplied.
local -i timeout=$(( 100 * seconds ))
zselect -t $timeout
print
)
builtin zle -Fw "$fd" .autocomplete:async:wait:fd-widget
return 0
}
.autocomplete:async:wait:fd-widget() {
local -i fd=$1
{
builtin zle -F $fd # Unhook ourselves immediately, so we don't get called more than once.
if [[ -n $_autocomplete__zle_flags ]]; then
builtin zle -f $_autocomplete__zle_flags
[[ $_autocomplete__zle_flags == yank* ]] &&
return 0
fi
(( KEYS_QUEUED_COUNT || PENDING )) &&
return
{
.autocomplete:async:same-state &&
.autocomplete:async:start
}
} always {
exec {fd}<&-
}
return 0
}
.autocomplete:async:start() {
if [[ -v _autocomplete__async_fd && _autocomplete__async_fd -ge 10 ]]; then
# Unhook and close the previous fd. (If it no longer exists, then this will fail harmlessly.)
builtin zle -F "$_autocomplete__async_fd" 2>/dev/null
exec {_autocomplete__async_fd}<&- 2>/dev/null
fi
typeset -g _autocomplete__async_fd=
sysopen -r -o cloexec -u _autocomplete__async_fd <(
local +h PS4=$_autocomplete__ps4
.autocomplete:async:start:inner 2>>| $_autocomplete__log
)
builtin zle -Fw "$_autocomplete__async_fd" .autocomplete:async:complete:fd-widget
# WORKAROUND: https://github.com/zsh-users/zsh-autosuggestions/issues/364
# There's a weird bug in Zsh < 5.8, where ^C stops working unless we force a fork.
command true
}
.autocomplete:async:start:inner() {
{
typeset -F SECONDS=0
local -P hooks=( chpwd periodic precmd preexec zshaddhistory zshexit )
builtin unset ${^hooks}_functions &> /dev/null
$hooks[@] () { : }
local -P hook=
for hook in \
zle-{isearch-{exit,update},line-{pre-redraw,init,finish},history-line-set,keymap-select}
do
builtin zle -N $hook .autocomplete:async:pty:no-op
done
{
local REPLY=
zpty AUTOCOMPLETE .autocomplete:async:pty
local -Pi fd=$REPLY
zpty -w AUTOCOMPLETE $'\C-@'
local header=
zpty -r AUTOCOMPLETE header $'*\C-A'
local -a reply=()
local text=
local -F seconds=0.0
builtin zstyle -s ":autocomplete:${curcontext}" timeout seconds ||
(( seconds = 1.0 ))
(( seconds = max( 0, seconds - SECONDS ) ))
# Convert to 100ths of a second for `zselect -t`.
# WORKAROUND: #441 Directly using $(( [#10] … max( … ) )) leads to 0 in Zsh 5.9, as the result
# of max() gets converted to an integer _before_ being multiplied.
local -i timeout=$(( 100 * seconds ))
if zselect -rt $timeout "$fd"; then
zpty -r AUTOCOMPLETE text $'*\C-B'
else
# Press ^C twice: Once to abort completion, then once to abort the command line.
# Then exit the shell with ^D.
zpty -wn AUTOCOMPLETE $'\C-C\C-C\C-D'
fi
} always {
zpty -d AUTOCOMPLETE
}
} always {
# Always produce output, so we always reach the callback, so we can close
# the fd.
print -rNC1 -- "${text%$'\C-B'}"
}
}
.autocomplete:async:pty() {
typeset -g +h PS4=pty:$_autocomplete__ps4
# Force the shell to exit on timeout.
local -i seconds=
builtin zstyle -s ":autocomplete:${curcontext}" timeout seconds ||
seconds=1
TMOUT=$(( [#10] 1 + seconds ))
TRAPALRM() {
builtin exit
}
builtin bindkey $'\C-@' .autocomplete:async:pty:zle-widget
local __tmp__=
builtin vared __tmp__
} 2>>| $_autocomplete__log
.autocomplete:async:pty:no-op() {
:
}
.autocomplete:async:pty:zle-widget() {
setopt localoptions NO_banghist
local -a _autocomplete__comp_mesg=()
local -i _autocomplete__list_lines=0
local _autocomplete__mesg=
{
# The completion widget sometimes returns without calling its function. So, we need to print all
# our control characters here, to ensure we don't end up waiting endlessly to read them.
print -n -- '\C-A'
LBUFFER=$_autocomplete__lbuffer
RBUFFER=$_autocomplete__rbuffer
[[ -n $curcontext ]] &&
setopt $_autocomplete__ctxt_opts[@]
builtin zle .autocomplete:async:pty:completion-widget -w 2>>| $_autocomplete__log
} always {
print -rNC1 -- ${_autocomplete__list_lines:-0}$'\C-B'
builtin exit
}
} 2>>| $_autocomplete__log
.autocomplete:async:pty:completion-widget() {
setopt localoptions banghist
{
if ! .autocomplete:async:sufficient-input; then
return
fi
{
unfunction compadd 2> /dev/null
unset 'compstate[vared]'
.autocomplete:async:list-choices:main-complete
} always {
_autocomplete__list_lines=$compstate[list_lines]
}
}
} 2>>| $_autocomplete__log
.autocomplete:async:complete:fd-widget() {
setopt localoptions NO_banghist
local -i fd=$1
{
local +h -F SECONDS=0.0
{
builtin zle -F $fd # Unhook ourselves immediately, so we don't get called more than once.
if [[ -n $_autocomplete__zle_flags ]]; then
builtin zle -f $_autocomplete__zle_flags
[[ $_autocomplete__zle_flags == yank* ]] &&
return 0
fi
(( KEYS_QUEUED_COUNT || PENDING )) &&
return
.autocomplete:async:same-state ||
return 0
local -a reply=()
IFS=$'\0' read -rAu $fd
shift -p reply
(( SECONDS += reply[2] ))
} always {
exec {fd}<&-
[[ -v _autocomplete__async_fd && $_autocomplete__async_fd == $fd ]] &&
unset _autocomplete__async_fd
}
[[ -n $curcontext ]] &&
setopt $_autocomplete__ctxt_opts[@]
# If a widget can't be called, zle always returns true.
# Thus, we return false on purpose, so we can check if our widget got called.
local +h PS4=zle:$_autocomplete__ps4
if ! builtin zle ._list_choices -w "$reply[1]" 2>>| $_autocomplete__log; then
typeset -g _autocomplete__overhead=$SECONDS
typeset -g region_highlight=( "$_autocomplete__region_highlight[@]" )
# Need to call this here, because on line-pre-redraw, $POSTDISPLAY is empty.
[[ -v functions[_zsh_autosuggest_highlight_apply] ]] &&
_zsh_autosuggest_highlight_apply
# Refresh if and only if our widget got called. Otherwise, Zsh will crash (eventually).
builtin zle -R
fi
.autocomplete:async:reset-state
return 0
}
}
.autocomplete:async:sufficient-input() {
local min_input=
if ! builtin zstyle -s ":autocomplete:${curcontext}:" min-input min_input; then
if [[ -n $curcontext ]]; then
min_input=0
else
min_input=1
fi
fi
local ignored=
builtin zstyle -s ":autocomplete:${curcontext}:" ignored-input ignored
if (( ${#words[@]} == 1 && ${#words[CURRENT]} < min_input )) ||
[[ -n $ignored && $words[CURRENT] == $~ignored ]]; then
compstate[list]=
false
else
true
fi
}
.autocomplete:async:list-choices:completion-widget() {
local +h PS4=$_autocomplete__ps4
setopt localoptions banghist
if [[ $1 != <1-> ]]; then
compstate[list]=
return
fi
.autocomplete:async:sufficient-input ||
return 2
.autocomplete:async:list-choices:main-complete
# Workaround: In Zsh <= 5.9.0, comppostfuncs don't get called after completing subscripts.
unset MENUSELECT MENUMODE
compstate[insert]=
_lastcomp[insert]=
compstate[pattern_insert]=
_lastcomp[pattern_insert]=
if [[ -v _autocomplete__partial_list ]]; then
builtin compadd -J -last- -x '%F{0}%K{12}(MORE)%f%k'
_lastcomp[list_lines]=$compstate[list_lines]
fi
if [[ -n $compstate[exact_string] && -z $_lastcomp[tags] &&
compstate[nmatches] -eq 1 && compstate[list_lines] -eq 1 ]]; then
# WORKAROUND: _arguments adds $compstate[exact_string] as a completion w/out description.
compstate[list]=
_lastcomp[list]=
fi
return 2 # Don't return 1, to prevent beeping.
}
.autocomplete:async:list-choices:max-lines() {
local -Pi max_lines
builtin zstyle -s ":autocomplete:${curcontext}:" list-lines max_lines ||
max_lines=16
_autocomplete__max_lines=$(( min( max_lines, LINES - BUFFERLINES - 1 ) ))
}
.autocomplete:async:list-choices:main-complete() {
local -i _autocomplete__max_lines
case $curcontext in
*history-* )
setopt $_autocomplete__func_opts[@]
autocomplete:_main_complete:new - history-lines _autocomplete__history_lines
;;
recent-paths:* )
setopt $_autocomplete__func_opts[@]
autocomplete:_main_complete:new - recent-paths _autocomplete__recent_paths
;;
* )
{
() {
emulate -L zsh
setopt $_autocomplete__func_opts[@]
local curcontext=list-choices:::
.autocomplete:async:shadow compadd
autoload -Uz +X _describe
.autocomplete:async:shadow _describe
# functions -T compadd _describe _description
} "$@"
.autocomplete:async:list-choices:max-lines
autocomplete:_main_complete:new "$@"
} always {
unfunction compadd comptags 2> /dev/null
.autocomplete:async:unshadow compadd
.autocomplete:async:unshadow _describe
}
;;
esac
}
.autocomplete:async:shadow() {
[[ -v functions[$1] ]] &&
functions[autocomplete:async:${1}:old]="$functions[$1]"
functions[$1]="$functions[.autocomplete:async:$1]"
}
.autocomplete:async:unshadow() {
if [[ -v functions[autocomplete:async:${1}:old] ]]; then
functions[$1]="$functions[autocomplete:async:${1}:old]"
unfunction autocomplete:async:${1}:old
fi
}
.autocomplete:async:_describe() {
local -i _autocomplete__described_lines=1 # Assume we'll add a title.
autocomplete:async:_describe:old "$@"
}
.autocomplete:async:compadd() {
setopt localoptions multibyte
local -A _opts_=()
local -a _xopts_=() _displ_=() _matches_=()
local -P _displ_name_= _matches_name_=
zparseopts -A _opts_ -E -- D: E: O:
local -Pi _unused_lines_=$(( _autocomplete__max_lines - compstate[list_lines] ))
# If $_grp is not set, then _describe is adding completions in a normal way and we don't need to
# do all this.
if [[ -v _autocomplete__described_lines && -n $_grp ]]; then
# We cannot interfere when _describe is actually adding the completions or we risk breaking the
# layout.
if [[ -z $_opts_[-D] ]]; then
builtin compadd "$@"
return
fi
_displ_name_=$_opts_[-D]
_matches_name_=$_opts_[-O]
# We already ran out of space.
if [[ -v _autocomplete__partial_list ]]; then
set -A $_displ_name_
[[ -n $_matches_name_ ]] &&
set -A $_matches_name_
fi
builtin compadd "$@"
local -Pi _ret_=$?
local -i _ndisplay_=${(PA)#_displ_name_}
local -i _lines_left_for_describe_=$(( _unused_lines_ - _autocomplete__described_lines ))
# The number of lines that would be added is equal to the number of unique display strings.
if (( ${#${(u)${(PA)_displ_name_}[@]#*:}} > _lines_left_for_describe_ )); then
local -Pi _matches_to_remove=$(( _ndisplay_ - max( 0, _lines_left_for_describe_ ) ))
if (( _matches_to_remove < _ndisplay_ )); then
shift -p $_matches_to_remove $_displ_name_ $_matches_name_
else
set -A $_displ_name_
[[ -n $_matches_name_ ]] &&
set -A $_matches_name_
fi
_ndisplay_=${(PA)#_displ_name_}
.autocomplete:async:compadd:disable
fi
(( _autocomplete__described_lines += _ndisplay_ ))
return _ret_
fi
# We already ran out of space.
if [[ -v _autocomplete__partial_list ]]; then
[[ -n $_opts_[-D] ]] &&
set -A $_opts_[-D]
[[ -n $_opts_[-O] ]] &&
set -A $_opts_[-O]
return 1
fi
# If
if [[ -n $_opts_[-D]$_opts_[-O] ]]; then
builtin compadd "$@"
return
fi
local -i _old_total_lines=$compstate[list_lines]
# +: Keep all occurences. Otherwise, only last occurence is kept.
zparseopts -a _xopts_ -D -E -- X+: x+:
if (( $#_xopts_ )); then
# Use only the first one, because subsequent ones are ignored by compadd anyway.
local original="$_xopts_[2]"
local formatted="${(%g:ce:)original}"
# Trim text to one line and omit ANSI sequences to avoid crash in command substitution.
local ansi_start=${(M)formatted##($'\C-[['[;[:digit:]]#m)#}
local ansi_end=${(M)formatted%%($'\C-[['[;[:digit:]]#m)#}
local text=${${formatted#$ansi_start}%$ansi_end}
_xopts_=(
"$_xopts_[1]"
"${(mr:COLUMNS-1:)text%%[[:space:]]#}"
)
fi
local -Pi _total_new_lines_="$(
builtin compadd "$_xopts_[@]" "$@"
print -nr -- $(( $compstate[list_lines] - _old_total_lines ))
)"
if (( $#_xopts_ )); then
# Replace original text with trimmed text.
_xopts_[2]="${original/$text/$_xopts_[-1]}"
fi
# Everything fits.
if (( _total_new_lines_ + $compstate[list_lines] <= _autocomplete__max_lines )); then
builtin compadd "$_xopts_[@]" "$@"
return
fi
local -Pi _new_completion_lines_="$(
builtin compadd "$@"
print -nr -- $(( $compstate[list_lines] - _old_total_lines ))
)"
local -a _dopt_=()
zparseopts -a _dopt_ -D -E -- d: ld:
_displ_name_=$_dopt_[2]
# Collect all matching completions and their display strings (if any).
local -a _Dopt_=()
[[ -n $_displ_name_ ]] &&
_Dopt_=( -D $_displ_name_ )
builtin compadd -O _matches_ $_Dopt_ "$@"
# If we don't have an array with display strings, then create one.
if [[ -z $_displ_name_ ]]; then
_displ_name_=_displ_
_displ_=( "$_matches_[@]" )
_dopt_=( -d $_displ_name_ )
fi
local -Pi _nmatches_per_line_=$(( 1.0 * $#_matches_ / _new_completion_lines_ ))
# If we need more than one line per match, then make each match fit exactly one line.
if (( _nmatches_per_line_ < 1 )); then
# WORKAROUND: Zsh mistakenly treats display strings that are exactly $COLUMNS wide as not
# fitting on one line.
set -A $_displ_name_ ${(@mr:COLUMNS-1:)${(PA)_displ_name_}[@]//$'\n'/\n}
_dopt_=( -ld $_displ_name_ )
(( _nmatches_per_line_ = 1 ))
fi
local -Pi _new_heading_lines_=$(( _total_new_lines_ - _new_completion_lines_ ))
# Need to round this down _before_ subtracting it or it will effectively be rounded up.
local -Pi _nmatches_that_fit_=$((
( _unused_lines_ - _new_heading_lines_ ) * _nmatches_per_line_
))
local -Pi _nmatches_to_remove_=$(( $#_matches_ - max( 0, _nmatches_that_fit_ ) ))
if (( _nmatches_to_remove_ > 0 )); then
# If we're going to remove anything, then we need to make room for the `(MORE)` prompt.
(( _nmatches_to_remove_ = min( ++_nmatches_to_remove_, $#_matches_ ) ))
.autocomplete:async:compadd:disable
# Make sure we always add a headline, even when we don't add any matches, because a single
# '(MORE)' without other content doesn't make sense.
(( _nmatches_to_remove_ >= $#_matches_ )) && _xopts_[1]=-x
shift -p $_nmatches_to_remove_ _matches_ $_displ_name_
fi
_autocomplete__compadd_opts_len "$@"
builtin compadd "$_xopts_[@]" "$_dopt_[@]" -a "$@[1,?]" _matches_
}
.autocomplete:async:compadd:disable() {
typeset -g _autocomplete__partial_list=$curtag
comptags() { false } # Stop completion from trying more tags.
}

View File

@@ -0,0 +1,189 @@
#!/bin/zsh
zmodload -Fa zsh/files b:zf_rm
zmodload -F zsh/parameter p:funcstack p:functions
builtin autoload -Uz is-at-least
typeset -ga _autocomplete__compdef=()
compdef() {
typeset -ga _autocomplete__compdef=( $_autocomplete__compdef[@] "${(j: :)${(@q+)@}}" )
}
[[ -v functions[_bash_complete] ]] ||
_bash_complete compgen complete () {
unfunction _bash_complete compgen complete
builtin autoload +X -Uz bashcompinit
bashcompinit
bashcompinit() { : }
${(%):-%N} "$@"
}
${0}:precmd() {
emulate -L zsh \
# -x
setopt $_autocomplete__func_opts[@]
[[ -v CDPATH && -z $CDPATH ]] &&
unset CDPATH cdpath
# Decrease Oh My Zsh start-up time. See below.
local -Pa omzdump=()
[[ -v ZSH_COMPDUMP && -r $ZSH_COMPDUMP ]] &&
omzdump=( ${(f)"$( < $ZSH_COMPDUMP )"} )
typeset -g \
_comp_dumpfile=${_comp_dumpfile:-${ZSH_COMPDUMP:-${XDG_CACHE_HOME:-$HOME/.cache}/zsh/compdump}}
if [[ -v _comps[-command-] && $_comps[-command-] != _autocomplete__command ]]; then
zf_rm -f $_comp_dumpfile
else
# Check if our most recently modified completion function is newer than the comp dump file.
local -Pa newest=( ~autocomplete/Completions/_*~*.zwc(N-.omY1) )
if [[ $newest[1] -nt $_comp_dumpfile ]]; then
zf_rm -f $_comp_dumpfile
fi
fi
if [[ ! -v _comp_setup ]] || [[ ! -r $_comp_dumpfile ]]; then
unfunction compdef compinit 2> /dev/null
bindkey() { : }
{
builtin autoload +X -Uz compinit
local -a compargs=()
zstyle -a ':autocomplete::compinit' arguments compargs
compinit -d "$_comp_dumpfile" "$compargs[@]"
} always {
unfunction bindkey
}
bindkey '^Xh' _complete_help
# Prevent Oh My Zsh from deleting comp dump file.
(( ${#omzdump[@]} > 0 )) &&
tee -a "$ZSH_COMPDUMP" &> /dev/null <<EOF
$omzdump[-2]
$omzdump[-1]
EOF
fi
compinit() { : }
local -P args=
for args in "$_autocomplete__compdef[@]"; do
eval "compdef $args"
done
unset _autocomplete__compdef
(
local -a reply=()
local cache_dir=
if builtin zstyle -s ':completion:*' cache-path cache_dir; then
local -P src= bin=
for src in $cache_dir/*~**.zwc~**/.*(N-.); do
bin=$src.zwc
if [[ ! -e $bin || $bin -ot $src ]]; then
zcompile -Uz $src
fi
done
fi
) &|
# Workaround: Some other plugins rely on patching _main_complete, which can interfere with our completion.
.autocomplete__patch _main_complete
autocomplete:_main_complete:new() {
local -i _autocomplete__reserved_lines=0
local -Pi ret=1
unset _autocomplete__partial_list
compstate[insert]=automenu-unambiguous # To get `all-expansions` from _expand.
compstate[last_prompt]=yes # Completion doesn't stay on the same command line without this.
compstate[list]='list force packed' # Always use same defaults, regardless of widget.
unset 'compstate[vared]'
local +h -a compprefuncs=( autocomplete:_main_complete:new:pre "$compprefuncs[@]" )
local +h -a comppostfuncs=( autocomplete:_main_complete:new:post "$comppostfuncs[@]" )
# functions -T autocomplete:_main_complete:old
autocomplete:_main_complete:old "$@"
# functions +T _path_files
# print -nu2 ' after: '
# typeset -m compstate >&2
}
autocomplete:_main_complete:new:pre() {
unsetopt localtraps
trap -
TRAPINT() { # ^C
zle -M "${(F)funcfiletrace}"
zle -R
return 130
}
TRAPQUIT() { # ^\
zle -M "${(F)funcfiletrace}"
zle -R
return 131
}
}
autocomplete:_main_complete:new:post() {
[[ $WIDGET != _complete_help ]] &&
unfunction compadd 2> /dev/null
_autocomplete__unambiguous
compstate[list_max]=0
MENUSCROLL=0
}
.autocomplete__patch _complete
_complete() {
local -i nmatches=$compstate[nmatches]
PREFIX=$PREFIX$SUFFIX
SUFFIX=
autocomplete:_complete:old "$@"
# WORKAROUND: Some completion functions mistakenly don't return 0 when they have succeeded.
(( compstate[nmatches] > nmatches ))
}
##
# WORKAROUND: _approximate won't do corrections if there already is a function called 'compadd'.
#
.autocomplete__patch _approximate
_approximate() {
{
[[ -v functions[compadd] ]] &&
functions[autocomplete:compadd:old]="$functions[compadd]"
functions[compadd]="$functions[autocomplete:approximate:compadd]"
autocomplete:_approximate:old
} always {
unfunction compadd 2> /dev/null
if [[ -v functions[autocomplete:compadd:old] ]]; then
functions[compadd]="$functions[autocomplete:compadd:old]"
unfunction autocomplete:compadd:old
fi
}
}
autocomplete:approximate:compadd() {
local ppre="$argv[(I)-p]"
[[ ${argv[(I)-[a-zA-Z]#U[a-zA-Z]#]} -eq 0 &&
"${#:-$PREFIX$SUFFIX}" -le _comp_correct ]] && return
if [[ "$PREFIX" = \~* && ( ppre -eq 0 || "$argv[ppre+1]" != \~* ) ]]; then
PREFIX="~(#a${_comp_correct})${PREFIX[2,-1]}"
else
PREFIX="(#a${_comp_correct})$PREFIX"
fi
if [[ -v functions[autocomplete:compadd:old] ]]; then
autocomplete:compadd:old "$@"
else
builtin compadd "$@"
fi
}
}

View File

@@ -0,0 +1,159 @@
#!/bin/zsh
zmodload -Fa zsh/zutil b:zstyle
typeset -g ZLE_REMOVE_SUFFIX_CHARS=$' /;\n\r\t'
typeset -g ZLE_SPACE_SUFFIX_CHARS='|&<>-+'
builtin zstyle ':completion:*' use-cache yes
builtin zstyle -e ':completion:*' cache-path autocomplete:config:cache-path
autocomplete:config:cache-path() {
typeset -ga reply=( "${XDG_CACHE_HOME:-$HOME/.cache}/zsh/compcache" )
}
builtin zstyle ':completion:*' completer \
_expand _complete _complete:-fuzzy _correct _approximate _ignored
builtin zstyle -e ':completion:*' max-errors autocomplete:config:max-errors
autocomplete:config:max-errors() {
typeset -ga reply=( $(( min( 2, ( $#PREFIX + $#SUFFIX ) / 3 ) )) )
}
# Order of matchers matters: m should come before r, which should come before l.
# Otherwise, the results are not as expected.
local lower_to_upper='m:{[:lower:]-}={[:upper:]_}'
local any_before_dot='r:|[.]=**'
local any_before_any='r:|?=**'
local nonseparators_after_any_before_separator='r:?||[-_ \]=*'
local any_before_word='l:|=*'
local separator_after_any='l:?|=[-_ \]'
builtin zstyle ':completion:*' matcher-list \
"$lower_to_upper $any_before_dot"
builtin zstyle ':completion:*-fuzzy:*' matcher-list \
"$lower_to_upper $any_before_dot $any_before_word" \
"+$nonseparators_after_any_before_separator $separator_after_any" \
"$lower_to_upper $any_before_any"
local minus_at_beginning_to_plus='b:-=+'
builtin zstyle ':completion:*:options' matcher $minus_at_beginning_to_plus
builtin zstyle ':completion:*' prefix-needed yes
builtin zstyle ':completion:*:functions' ignored-patterns '*.*' '*:*' '+*'
builtin zstyle ':completion:*:users' ignored-patterns '_*'
builtin zstyle ':completion:*:widgets' ignored-patterns '*.*' '*:*'
builtin zstyle ':completion:*' single-ignored ''
builtin zstyle ':completion:*:expand-alias:*' complete yes
builtin zstyle ':completion:*:expand:*' tag-order 'expansions all-expansions' -
builtin zstyle ':completion:*:expand:*' accept-exact continue
builtin zstyle ':completion:*:expand:*' add-space no
builtin zstyle ':completion:*:expand:*' glob yes
builtin zstyle ':completion:*:expand:*' keep-prefix no # Needed for file type highlighting
builtin zstyle ':completion:*:expand:*' substitute yes
builtin zstyle ':completion:*:expand:*' subst-globs-only yes
builtin zstyle -e ':completion:*:-command-:*' tag-order autocomplete:config:tag-order:command
autocomplete:config:tag-order:command() {
if [[ $PREFIX == (|.|*/*) ]]; then
typeset -ga reply=( 'suffix-aliases (|*-)directories executables (|*-)files' - )
else
typeset -ga reply=( 'aliases suffix-aliases functions reserved-words builtins' )
if (( path[(I).] )); then
reply[1]+=' (|*-)directories executables (|*-)files commands'
else
reply[1]+=' commands (|*-)directories executables (|*-)files'
fi
fi
}
builtin zstyle ':completion:*:-tilde-:*' tag-order directory-stack named-directories
builtin zstyle ':completion:*:(approximate|correct):*' tag-order '! original' -
# Complete options rather than directory stack. You can get directory stack by typing `~-` (tilde plus dash).
builtin zstyle ':completion:*:cd:*' complete-options yes
builtin zstyle ':completion:*:cd:*' tag-order '! directory-stack' -
# Don't show the giant list of history lines.
builtin zstyle ':completion:*:fc:*' tag-order options -
builtin zstyle -e ':completion:*:git-*:*' tag-order 'autocomplete:config:tag-order:git "$@"'
autocomplete:config:tag-order:git() {
reply=()
(( compstate[nmatches] )) &&
reply=(
'! heads(|-*) *-remote remote-* blob-*'
-
)
}
# Complete only the tail of a path.
builtin zstyle ':completion:*' ignore-parents 'parent pwd directory'
builtin zstyle ':completion:*:paths' expand suffix
builtin zstyle ':completion:*:paths' list-suffixes yes
builtin zstyle ':completion:*:paths' special-dirs no
builtin zstyle ':completion:*' group-name ''
builtin zstyle ':completion:*:-command-:*' group-name commands
builtin zstyle ':completion:*:all-expansions' group-name 'expansion'
builtin zstyle ':completion:*' group-order \
expansions all-expansions options \
aliases suffix-aliases functions reserved-words builtins commands \
remotes hosts recent-branches commits \
local-directories directories executables
builtin zstyle ':completion:*' file-patterns '*(-/):directories:directory %p(#q^-/):globbed-files'
builtin zstyle -e ':completion:*:-command-:*' file-patterns autocomplete:config:file-patterns:command
autocomplete:config:file-patterns:command() {
[[ $PREFIX$SUFFIX != */* ]] &&
typeset -ga reply=( '*(-/):directories:directory ./*(-*^/):executables:"executable file"' )
}
builtin zstyle ':completion:*:(.|source):*' file-patterns \
'%p(#q-/):directories:directory %p~*.zwc(-.^*):globbed-files' '%p~*.zwc(-^/):globbed-files'
# Don't combine parameters with same values.
builtin zstyle ':completion:*:parameters' list-grouped no
builtin zstyle -e ':completion:*:-command-:*' format autocomplete:config:format command
builtin zstyle -e ':completion:*:descriptions' format autocomplete:config:format %d
builtin zstyle -e ':completion:*:all-expansions' format autocomplete:config:format expansion
autocomplete:config:format() {
reply=( $'%{\e[0;1;2m%}'$1$'%{\e[0m%}' )
}
builtin zstyle -e ':completion:*:all-expansions' format autocomplete:config:format expansion
builtin zstyle -e ':completion:*:expansions' format autocomplete:config:format '"globbed files"'
builtin zstyle -e ':completion:*:warnings' format autocomplete:config:format:warnings
autocomplete:config:format:warnings() {
[[ $CURRENT == 1 && -z $PREFIX$SUFFIX ]] ||
autocomplete:config:format 'no matching %d completions'
}
builtin zstyle ':completion:*:messages' format '%F{9}%d%f'
builtin zstyle ':completion:*:history-lines' format ''
builtin zstyle ':completion:*' auto-description '%d'
builtin zstyle ':completion:*:parameters' extra-verbose yes
builtin zstyle ':completion:*:default' select-prompt '%F{black}%K{12}line %l %p%f%k'
builtin zstyle ':completion:*' insert-sections yes
builtin zstyle ':completion:*' separate-sections yes
# Needed for _gnu_generic to prevent descriptions from getting cropped.
is-at-least 5.9 ||
builtin zstyle ':completion:*' command '- COLUMNS=999'
${0}:precmd() {
typeset -g _comp_setup="$_comp_setup"';
[[ $_comp_caller_options[globdots] == yes ]] && setopt globdots'
# Remove incompatible settings.
local -P key= setting=
for key in menu list-prompt; do
for setting in ${(f)"$( zstyle -L '*' $key )"}; do
eval "${setting/zstyle(| -e)/zstyle -d}"
done
done
builtin zstyle ':completion:*:*:*:*:default' menu no no-select
unset LISTPROMPT
}

View File

@@ -0,0 +1,64 @@
#!/bin/zsh
zmodload zsh/complist
zmodload -F zsh/terminfo p:terminfo
local -Pa prefix=( '\e'{\[,O} )
local -Pa key_up=( ${^prefix}A )
local -Pa key_down=( ${^prefix}B )
local -Pa key_alt_up=( '\e'$^key_up '\e[1;3A' )
local -Pa key_alt_down=( '\e'$^key_down '\e[1;3B' )
local -A main=() emacs=() vicmd=() menukeys=()
${0}:bind() {
local -P key= widget=$1 menuwidget=$2
emacs[$3]=$widget
menukeys[$3]=$menuwidget
vicmd[$4]=$widget
key="^[$4"
menukeys[$key]=$menuwidget
shift 4
for key; do
main[$key]=$widget
menukeys[$key]=$menuwidget
done
}
${0}:bind up-line-or-search up-history '^P' 'k' $key_up[@]
${0}:bind down-line-or-select down-history '^N' 'j' $key_down[@]
${0}:bind history-search-backward vi-backward-blank-word '\ep' '^P' $key_alt_up[@]
${0}:bind menu-select vi-forward-blank-word '\en' '^N' $key_alt_down[@]
${0}:bind history-incremental-search-backward history-incremental-search-backward '^R' '/'
${0}:bind menu-search history-incremental-search-forward '^S' '?'
local backtab=$terminfo[kcbt]
menukeys+=(
'\t' menu-complete
"$backtab" reverse-menu-complete
'^@' accept-and-hold
'^[v' accept-and-hold
'^_' undo
'^[u' undo
"$terminfo[kpp]" backward-word
"$terminfo[knp]" forward-word
)
main+=(
'\t' complete-word
"$backtab" expand-word
)
emacs+=(
'^X/' recent-paths
)
bindkey -M main "${(kv@)main}"
bindkey -M emacs "${(kv@)emacs}"
bindkey -M vicmd "${(kv@)vicmd}"
bindkey -M menuselect "${(kv@)menukeys}"
unfunction ${0}:bind

View File

@@ -0,0 +1,95 @@
#!/bin/zsh
zmodload -Fa zsh/files b:zf_mkdir b:zf_rm
zmodload -F zsh/parameter p:functions
zmodload -F zsh/system p:sysparams
zmodload -F zsh/zleparameter p:widgets
zmodload -Fa zsh/zutil b:zstyle
builtin autoload +X -Uz add-zsh-hook zmathfunc
zmathfunc
typeset -ga _autocomplete__ctxt_opts=( completealiases completeinword )
typeset -ga _autocomplete__mods=( compinit config widgets key-bindings recent-dirs async )
typeset -gU FPATH fpath=( ~autocomplete/Completions $fpath[@] )
local -P xdg_data_home=${XDG_DATA_HOME:-$HOME/.local/share}
local -P zsh_data_dir=$xdg_data_home/zsh
[[ -d $zsh_data_dir ]] ||
zf_mkdir -pm 0700 $zsh_data_dir
local -P old_logdir=$xdg_data_home/zsh-autocomplete/log
[[ -d $old_logdir ]] &&
zf_rm -fr -- $old_logdir
local -P logdir=${XDG_STATE_HOME:-$HOME/.local/state}/zsh-autocomplete/log
local -P bug=
for bug in ${logdir} ${logdir:h}; do
[[ -d $bug ]] ||
zf_rm -f $bug
done
zf_mkdir -p -- $logdir
hash -d autocomplete-log=$logdir
local -Pa older_than_a_week=( $logdir/*(Nmd+7) )
(( $#older_than_a_week[@] )) &&
zf_rm -f -- $older_than_a_week[@]
typeset -g _autocomplete__log=${logdir}/${(%):-%D{%F}}.log
typeset -g _autocomplete__ps4=$'%D{%T.%.} %e:%N:%I\t%? %(1_,%_ ,)'
local -P zsh_cache_dir=${XDG_CACHE_HOME:-$HOME/.cache}/zsh
[[ -d $zsh_cache_dir ]] ||
zf_mkdir -pm 0700 $zsh_cache_dir
local -P mod=
for mod in $_autocomplete__mods; do
builtin zstyle -T ":autocomplete:$mod" enabled &&
.autocomplete__$mod "$@"
unfunction .autocomplete__$mod
done
add-zsh-hook precmd ${0}:precmd
# Make sure we always run before Autosuggest, so we don't overwrite its default ignore list.
typeset -gaU precmd_functions=( ${0}:precmd $precmd_functions )
${0}:precmd() {
# WORKAROUND: For hook functions in Zsh 5.8, $0 can be something else than the function name.
0=${(%):-%N}
add-zsh-hook -d precmd $0
unfunction $0
() {
emulate -L zsh \
# -x
setopt $_autocomplete__func_opts[@]
# Workaround for https://www.zsh.org/mla/workers/2021/msg01310.html
if builtin zstyle -L zle-hook types > /dev/null; then
local -P hook=
for hook in \
zle-{isearch-{exit,update},line-{pre-redraw,init,finish},history-line-set,keymap-select}
do
[[ -v widgets[$hook] &&
$widgets[$hook] == user:_zsh_highlight_widget_orig-s*-r<->-$hook ]] &&
builtin zle -N $hook azhw:$hook
done
fi
}
local -P mod=
for mod in $_autocomplete__mods; do
mod=.autocomplete__${mod}:precmd
if [[ -v functions[$mod] ]]; then
$mod
unfunction $mod
fi
done
true
}

View File

@@ -0,0 +1,21 @@
#!/bin/zsh
zmodload -Fa zsh/files b:zf_mv b:zf_mkdir
zmodload -F zsh/parameter p:commands p:dirstack p:functions
${0}:precmd() {
setopt autopushd pushdignoredups # Set *global* shell options.
builtin autoload -Uz chpwd_recent_dirs chpwd_recent_filehandler
local __=
builtin zstyle -s :chpwd: recent-dirs-file __ ||
builtin zstyle ':chpwd:*' recent-dirs-file \
${XDG_DATA_HOME:-$HOME/.local/share}/zsh/chpwd-recent-dirs
builtin zstyle -s :chpwd: recent-dirs-max __ ||
builtin zstyle ':chpwd:*' recent-dirs-max 0
local -a reply=()
chpwd_recent_filehandler
dirstack=( "$reply[@]" )
add-zsh-hook chpwd chpwd_recent_dirs
}

View File

@@ -0,0 +1,47 @@
#!/bin/zsh
zmodload zsh/complist
typeset -ga _autocomplete__suggest_ignore_widgets=()
typeset -g ZSH_AUTOSUGGEST_MANUAL_REBIND=1
typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=.autosuggest-orig-
${0}:c() {
_autocomplete__suggest_ignore_widgets+=( $1 )
builtin zle -C "$1" "$2" .autocomplete__${3}__completion-widget
}
${0}:z() {
builtin zle -N "$1" .autocomplete__${2}__zle-widget
}
${0}:z up-line-or-search{,}
${0}:z down-line-or-select{,}
${0}:z history-search{-backward,}
${0}:precmd() {
emulate -L zsh \
# -x
setopt $_autocomplete__func_opts[@]
0=${0%:*}
# Create all completion widgets here, to avoid getting them wrapped by
# Autosuggest or Syntax Highlighting.
local -P tab_style=
for tab_style in complete-word menu-complete menu-select; do
${0}:c "$tab_style" "$tab_style" complete-word
done
${0}:c {,}reverse-menu-complete complete-word
${0}:c insert-unambiguous-or-complete {,}complete-word
${0}:c menu-search menu-select complete-word
${0}:c history-search-backward menu-select history-search
# Autosuggestions otherwise makes $WIDGETSTYLE disappear
[[ -v ZSH_AUTOSUGGEST_IGNORE_WIDGETS ]] &&
ZSH_AUTOSUGGEST_IGNORE_WIDGETS+=(
$_autocomplete__suggest_ignore_widgets
)
unset _autocomplete__suggest_ignore_widgets
unfunction ${0}:{c,z}
}

View File

@@ -0,0 +1,8 @@
#!/bin/zsh
zmodload -F zsh/parameter p:functions
functions[autocomplete:${1}:old]="$(
unfunction $1 2> /dev/null
builtin autoload +X -Uz $1
print -r -- "$functions[$1]"
)"

View File

@@ -0,0 +1,28 @@
#!/bin/zsh
emulate -L zsh \
# -x
setopt $_autocomplete__func_opts[@]
typeset -g _autocomplete__last_cutbuffer
typeset -g _autocomplete__zle_flags=
{
if (( YANK_ACTIVE )); then
_autocomplete__zle_flags=yank
(( YANK_END <= CURSOR )) &&
_autocomplete__zle_flags+=before
return 1 # Tell caller to abort.
fi
[[ $_autocomplete__last_cutbuffer != $CUTBUFFER ]] &&
_autocomplete__zle_flags=kill
return 0
} always {
[[ -n $_autocomplete__zle_flags ]] &&
builtin zle -f $_autocomplete__zle_flags
typeset -g _autocomplete__last_cutbuffer=$CUTBUFFER
}
return 0

View File

@@ -0,0 +1,36 @@
#!/bin/zsh
setopt localoptions banghist
zmodload -F zsh/terminfo p:terminfo
local context=${curcontext:-${WIDGET}:::}
unset curcontext
local +h curcontext=$context
local +h -a comppostfuncs=( .autocomplete__complete-word__post "$comppostfuncs[@]" )
local -a compargs=()
case $curcontext in
( history-incremental-search* )
compargs=( - history-lines _autocomplete__history_lines )
;;
( recent-paths:* )
compargs=( - recent-paths _autocomplete__recent_paths )
;;
( * )
if [[ $WIDGET == spell-word ]] ||
[[ -v _autocomplete__partial_list && $WIDGETSTYLE == (|*-)(list|menu)(|-*) ]] ||
{ [[ $_lastcomp[unambiguous] != (|$PREFIX$SUFFIX) ]] &&
_autocomplete__should_insert_unambiguous }; then
compstate[old_list]=
fi
;;
esac
if [[ -n $compstate[old_list] ]]; then
compstate[old_list]=keep
compargs=( - )
fi
autocomplete:_main_complete:new "$compargs[@]"
(( _lastcomp[nmatches] > 0 ))

View File

@@ -0,0 +1,69 @@
#autoload
local -a match=() mbegin=() mend=() # `zstyle` for some reason assigns these.
unset MENUMODE MENUSELECT
local -i nmatches
if [[ $compstate[old_list] == keep ]]; then
(( nmatches = _lastcomp[nmatches] ))
else
(( nmatches = compstate[nmatches] ))
fi
[[ $WIDGETSTYLE == (|*-)menu(|-*) ]]
local -i is_menu=$(( ! ? ))
if (( is_menu )); then
compstate[list]='list force'
else
compstate[list]=
zle -Rc
fi
if [[ $compstate[old_list] == keep && $_lastcomp[completer] == prefix ]] ||
[[ $_completer == prefix ]]; then
compstate[to_end]=
else
compstate[to_end]='match'
fi
{
compstate[insert]=
if [[ $compstate[unambiguous] != (|$PREFIX$SUFFIX) ]] && _autocomplete__should_insert_unambiguous
then
(( is_menu )) &&
compstate[insert]='automenu-'
compstate[insert]+='unambiguous'
return
fi
if (( is_menu )); then
if [[ $WIDGETSTYLE == (|*-)select(|-*) ]]; then
typeset -gi MENUSELECT=0
if [[ $WIDGET == (|*-)search(|-*) ]]; then
typeset -g MENUMODE=search-forward
fi
fi
compstate[insert]='menu:'
fi
if [[ $WIDGET == (|.)reverse-* || $WIDGETSTYLE == (|.)reverse-menu-complete ]]; then
compstate[insert]+='0'
else
compstate[insert]+='1'
fi
if (( ! is_menu )) && _autocomplete__should_add_space; then
compstate[insert]+=' '
fi
return 0
} always {
if [[ -n $compstate[insert] ]]; then
typeset -g _autocomplete__inserted
else
unset _autocomplete__inserted
fi
}

View File

@@ -0,0 +1,7 @@
#!/bin/zsh
if [[ $RBUFFER == *$'\n'* ]]; then
builtin zle down-line
else
builtin zle menu-select -w
fi

View File

@@ -0,0 +1,22 @@
#!/bin/zsh
local 0=${(%):-%N}
${0}() {
typeset -g curcontext=${WIDGET}:::
local +h -a comppostfuncs=( ${(%):-%N}:post "$comppostfuncs[@]" )
compstate[old_list]=
autocomplete:_main_complete:new - history-lines _autocomplete__history_lines
unset curcontext
(( compstate[nmatches] ))
}
${0}:post() {
typeset -gi MENUSELECT=0
compstate[insert]='menu:0'
if (( compstate[nmatches] < 2 )); then
compstate[list]=
fi
}
${0} "$@"

View File

@@ -0,0 +1,9 @@
#!/bin/zsh
if [[ $LBUFFER == *$'\n'* ]]; then
builtin zle up-line
return
fi
builtin zle history-search-backward -w
(( $_lastcomp[nmatches] )) # WORKAROUND: Widget always beeps otherwise.

View File

@@ -0,0 +1,19 @@
Copyright (c) 2020-2025 Marlon Richert
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,364 @@
# Autocomplete for Zsh
This plugin for Zsh adds real-time type-ahead autocompletion to your command line, similar to what
you find in desktop apps. While you type on the command line, available completions are listed
automatically; no need to press any keyboard shortcuts. Press <kbd>Tab</kbd> to insert the top
completion or <kbd></kbd> to select a different one.
Additional features:
* Out-of-the-box configuration of Zsh's completion system
* Multi-line history search
* Completion of recent directories
* Useful [keyboard shortcuts](#keyboard-shortcuts)
* Easy to [configure](#configuration)
> Enjoy using this software? [Become a sponsor!](https://github.com/sponsors/marlonrichert) 💝
## Requirements
Recommended:
* Tested to work with [Zsh](http://zsh.sourceforge.net) 5.8 and newer.
Minimum:
* Should theoretically work with Zsh 5.4, but I'm unable to test that.
## Installation & setup
> Note: In this manual, `%` represents the command line prompt. If you see it in front of a command,
> it means you should run it on the command line, not put it in a script.
First, install Autocomplete itself. Here are some way to do so:
* To use only releases (instead of the `main` branch), install `zsh-autocomplete` with a package
manager. As of this writing, this package is available through Homebrew, Nix, `pacman`, Plumage,
and (as `app-shells/zsh-autocomplete`) Portage.
* To always use the latest commit on the `main` branch, do one of the following:
* Install the AUR package [zsh-autocomplete-git](https://aur.archlinux.org/packages/zsh-autocomplete-git)<sup>AUR</sup> from the Arch User Repository (for example, using [yay](https://github.com/Jguer/yay), an AUR helper):
```sh
yay -S zsh-autocomplete-git
```
* Use a Zsh plugin manager to install `marlonrichert/zsh-autocomplete`. (If you don't have a
plugin manager yet, I recommend using [Znap](https://github.com/marlonrichert/zsh-snap).)
* Clone the repo directly:
```sh
% git clone --depth 1 -- https://github.com/marlonrichert/zsh-autocomplete.git
```
After installing, make the following modifications to your shell config:
* In your `.zshrc` file:
* Remove any calls to `compinit`.
* Add near the top, _before_ any calls to `compdef`:
```sh
source /path/to/zsh-autocomplete/zsh-autocomplete.plugin.zsh
```
* When using **Ubuntu,** add to your `.zshenv` file:
```sh
skip_global_compinit=1
```
* When using **Nix,** add to your `home.nix` file:
```
programs.zsh.enableCompletion = false;
```
Finally, restart your shell. Here's two ways to do so:
* Open a new tab or window in your terminal.
* Replace the current shell with a new one:
```sh
% exec zsh
```
### Updating
If you installed manually, run:
```sh
% git -C ~autocomplete pull
```
Otherwise, simply use your package manager or plugin manager's update mechanisms.
### Uninstalling
1. Revert the actions you took to [install](#installation).
1. Restart your shell.
## Keyboard shortcuts
| `main` | `emacs` | `vicmd` | On the command line | In the menus
| ---: | ---: | ---: | :--- | :---
| <kbd>Enter</kbd><br><kbd>Return</kbd> | | | | Exit menu text search or exit menu
| <kbd>Tab</kbd> | | | Insert first listed menu item | Next completion
| <kbd>Shift</kbd><kbd>Tab</kbd> | | | Expand the current word | Previous completion
| <kbd>↓</kbd> | <kbd>Ctrl</kbd><kbd>N</kbd> | <kbd>J</kbd> | Cursor down or enter completion menu | Change selection
| <kbd>↑</kbd> | <kbd>Ctrl</kbd><kbd>P</kbd> | <kbd>K</kbd> | Cursor up or enter [history menu](#history-menu) | Change selection
| <kbd>Alt</kbd><kbd>↓</kbd> | <kbd>Alt</kbd><kbd>N</kbd> | <kbd>Ctrl</kbd><kbd>N</kbd> | Enter completion menu | Next section
| <kbd>Alt</kbd><kbd>↑</kbd> | <kbd>Alt</kbd><kbd>P</kbd> | <kbd>Ctrl</kbd><kbd>P</kbd> | Enter history menu | Previous section
| <kbd>PgDn</kbd> | | | | Page down
| <kbd>PgUp</kbd> | | | | Page up
| | <kbd>Ctrl</kbd><kbd>X</kbd> <kbd>/</kbd> | | Toggle recent path search |
| | <kbd>Ctrl</kbd><kbd>R</kbd> | <kbd>/</kbd> | Toggle history search | Start menu text search or go to previous match
| | <kbd>Ctrl</kbd><kbd>S</kbd> | <kbd>?</kbd> | Start menu text search | Start menu text search or go to next match
| | <kbd>Ctrl</kbd><kbd>Space</kbd> | <kbd>V</kbd> | Toggle selection mode | Add another item
| | <kbd>Ctrl</kbd><kbd>-</kbd><br><kbd>Ctrl</kbd><kbd>/</kbd> | <kbd>U</kbd> | | Undo last item
| | <kbd>Ctrl</kbd><kbd>G</kbd> | | | Undo all added items
### Caveats
* `main` is whichever keymap was aliased to `main` when Autocomplete was sourced.
* By default, this is `emacs`.
* If you run `bindkey -v` _before_ sourcing Autocomplete, then `main` will be `viins` when
Autocomplete installs keybindings.
* Plugins or other scripts that you load _after_ loading Autocomplete may override these bindings.
If you find that some shortcuts don't work as expected, then you can fix them by
* changing the order in which you source your plugins or by
* running [`bindkey` commands](#reassign-keys) in your dotfiles _after_ you source your plugins.
* Depending on your terminal, not all keybindings might be available to you.
* Instead of <kbd>Alt</kbd>, your terminal might require you to press <kbd>Escape</kbd>,
<kbd>Option</kbd> or <kbd>Meta</kbd>.
* In the menus, the bindings listed under `vicmd` require you to press <kbd>Alt</kbd> for each,
instead of just once.
* The bindings listed under `emacs` and `vicmd` are always both active in the menus, no matter which
keymap you actually use. This is a limitation of Zsh.
* What any other keys do while you're in a menu depends on the keymap from which you opened the
menu. See the Zsh manual section on [menu
selection](https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#Menu-selection) for more info.
## Configuration
The following are the most commonly requested ways to configure Autocomplete's behavior. To use any
of these, add the code shown to your `.zshrc` file and modify it there, then restart you shell.
### Reassign keys
You can use [Zsh's `bindkey`
command](https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Zle-Builtins), _after_ loading
Autocomplete, to customize your keybindings. Below are some examples of what you can do with this.
#### Make <kbd>Tab</kbd> and <kbd>Shift</kbd><kbd>Tab</kbd> cycle completions on the command line
This makes <kbd>Tab</kbd> and <kbd>Shift</kbd><kbd>Tab</kbd>, when pressed on the command line,
cycle through listed completions, without changing what's listed in the menu:
```sh
bindkey '^I' menu-complete
bindkey "$terminfo[kcbt]" reverse-menu-complete
```
#### Make <kbd>Tab</kbd> and <kbd>Shift</kbd><kbd>Tab</kbd> go to the menu
This makes <kbd>Tab</kbd> and <kbd>Shift</kbd><kbd>Tab</kbd>, when pressed on the command line,
enter the menu instead of inserting a completion:
```sh
bindkey '^I' menu-select
bindkey "$terminfo[kcbt]" menu-select
```
#### Make <kbd>Tab</kbd> and <kbd>Shift</kbd><kbd>Tab</kbd> change the selection in the menu
This makes <kbd>Tab</kbd> and <kbd>Shift</kbd><kbd>Tab</kbd> move the selection in the menu right
and left, respectively, instead of exiting the menu:
```sh
bindkey -M menuselect '^I' menu-complete
bindkey -M menuselect "$terminfo[kcbt]" reverse-menu-complete
```
#### Make <kbd>←</kbd> and <kbd>→</kbd> always move the cursor on the command line
This makes <kbd>←</kbd> and <kbd>→</kbd> always move the cursor on the command line, even when you
are in the menu:
```sh
bindkey -M menuselect '^[[D' .backward-char '^[OD' .backward-char
bindkey -M menuselect '^[[C' .forward-char '^[OC' .forward-char
```
#### Make <kbd>Enter</kbd> always submit the command line
This makes <kbd>Enter</kbd> always submit the command line, even when you are in the menu:
```sh
bindkey -M menuselect '^M' .accept-line
```
#### Restore Zsh-default history shortcuts
This restores the default Zsh keybindings for history control:
```sh
bindkey -M emacs \
"^[p" .history-search-backward \
"^[n" .history-search-forward \
"^P" .up-line-or-history \
"^[OA" .up-line-or-history \
"^[[A" .up-line-or-history \
"^N" .down-line-or-history \
"^[OB" .down-line-or-history \
"^[[B" .down-line-or-history \
"^R" .history-incremental-search-backward \
"^S" .history-incremental-search-forward \
#
bindkey -a \
"^P" .up-history \
"^N" .down-history \
"k" .up-line-or-history \
"^[OA" .up-line-or-history \
"^[[A" .up-line-or-history \
"j" .down-line-or-history \
"^[OB" .down-line-or-history \
"^[[B" .down-line-or-history \
"/" .vi-history-search-backward \
"?" .vi-history-search-forward \
#
```
### Pass arguments to `compinit`
If necessary, you can let Autocomplete pass arguments to `compinit` as follows:
```sh
zstyle '*:compinit' arguments -D -i -u -C -w
```
### First insert the common substring
You can make any completion widget first insert the longest sequence of characters
that will complete to all completions shown, if any, before inserting actual completions:
```zsh
# all Tab widgets
zstyle ':autocomplete:*complete*:*' insert-unambiguous yes
# all history widgets
zstyle ':autocomplete:*history*:*' insert-unambiguous yes
# ^S
zstyle ':autocomplete:menu-search:*' insert-unambiguous yes
```
#### Insert prefix instead of substring
When using the above, if you want each widget to first try to insert only the longest _prefix_ that
will complete to all completions shown, if any, then add the following:
```zsh
zstyle ':completion:*:*' matcher-list 'm:{[:lower:]-}={[:upper:]_}' '+r:|[.]=**'
```
Note, though, that this will also slightly change what completions are listed initially. This is a
limitation of the underlying implementation in Zsh.
### Customize common substring message
You can customize the way the common substring is presented. The following sets the presentation to
the default:
```zsh
builtin zstyle ':autocomplete:*:unambiguous' format \
$'%{\e[0;2m%}%Bcommon substring:%b %0F%11K%d%f%k'
```
`%d` will be replaced with the common substring. Additionally, the following [Zsh prompt escape
sequences](https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html#Visual-effects) are
supported for adding visual effects:
* `%B`: bold
* `%F`: foreground color
* `%K`: background color
* `%S`: `terminfo` "standout"
* `%U`: underline
* `%{...%}`: arbitrary [ANSI escape
sequence](https://en.wikipedia.org/wiki/ANSI_escape_code#Select_Graphic_Rendition_parameters)
### Make <kbd>Enter</kbd> submit the command line straight from the menu
By default, pressing <kbd>Enter</kbd> in the menu search exits the search and
pressing it otherwise in the menu exits the menu. If you instead want to make
<kbd>Enter</kbd> _always_ submit the command line, use the following:
```zsh
bindkey -M menuselect '\r' .accept-line
```
### Add or don't add a space after certain completions
When inserting a completion, a space is added after certain types of
completions. The default list is as follows:
```zsh
zstyle ':autocomplete:*' add-space \
executables aliases functions builtins reserved-words commands
```
Modifying this list will change when a space is inserted. If you change the
list to `'*'`, a space is always inserted. If you put no elements in the list,
then a space is never inserted.
### Don't add a semicolon after history completions
By default, Autocomplete adds a semicolon to each history line to allow adding another line with
<kbd>Ctrl</kbd><kbd>Space</kbd>. You can deactivate this feature as follows:
```zsh
zstyle ':autocomplete:*' add-semicolon no
```
### Start each command line in history search mode
This will make Autocomplete behave as if you pressed <kbd>Ctrl</kbd><kbd>R</kbd> at the start of
each new command line:
```zsh
zstyle ':autocomplete:*' default-context history-incremental-search-backward
```
### Wait with autocompletion until typing stops for a certain amount of seconds
Normally, Autocomplete fetches completions after you stop typing for about 0.05 seconds. You can
change this as follows:
```zsh
zstyle ':autocomplete:*' delay 0.1 # seconds (float)
```
### Wait longer before timing out autocompletion
Slow autocompletion can make the command line hang. Therefore, by default, Autocomplete waits at
most 1 second for completion to finish. You can change this value as follows:
```zsh
zstyle ':autocomplete:*' timeout 2.0 # seconds (float)
```
Note, though, that increasing this value can make your command line feel less responsive.
### Wait for a minimum amount of input
To suppress autocompletion until a minimum number of characters have been typed:
```zsh
zstyle ':autocomplete:*' min-input 3
```
### Don't show completions if the current word matches a pattern
For example, this will stop completions from showing whenever the current word consists of two or
more dots:
```zsh
zstyle ':autocomplete:*' ignored-input '..##'
```
## Change the max number of lines shown
By default, Autocomplete lets the history menu fill half of the screen, and limits all real-time
listings to a maximum of 16 lines. You can change these limits as follows:
```zsh
# Note: -e lets you specify a dynamically generated value.
# Override default for all listings
# $LINES is the number of lines that fit on screen.
zstyle -e ':autocomplete:*:*' list-lines 'reply=( $(( LINES / 3 )) )'
# Override for recent path search only
zstyle ':autocomplete:recent-paths:*' list-lines 10
# Override for history search only
zstyle ':autocomplete:history-incremental-search-backward:*' list-lines 8
# Override for history menu only
zstyle ':autocomplete:history-search-backward:*' list-lines 2000
```
Note that for all real-time listings, the maximum number of lines is additionally capped to the
number of lines that fit on screen. However, there is no such limit for the history menu. If that
generates more lines than fit on screen, you can simply use <kbd>PgUp</kbd> and <kbd>PgDn</kbd> to
scroll through the excess lines. (Note: On some terminals, you have to additionally hold
<kbd>Shift</kbd> or, otherwise, it will scroll the terminal buffer instead.)
### Use a custom backend for recent directories/files
Autocomplete by default uses [`cdr`](
https://zsh.sourceforge.io/Doc/Release/User-Contributions.html#Recent-Directories)
to keeping track of and list recent directories (but not files). Override the following two
functions to supply Autocomplete with recent directories/files from any source that you like:
```zsh
# This function should populate an array $reply with a list of absolute paths. Path completions are
# listed in the same order as in this array.
chpwd_recent_filehandler() {
reply=( '/first/recent/dir' '/recent/file' '/second/recent/dir' )
}
# Called whenever you change dirs, to give you a chance to write the new dir to file.
# NOTE: If you override the function above, then you are *required* to override this one, too. Can
# be left empty, though.
chpwd_recent_dirs() {}
```
### Auto-include recent directories
Instead of having to press a keyboard shortcut, you can automatically include recent directories
whenever directories are listed:
```zsh
# Show recent dirs unless the current word is empty or equal to an existing directory.
zstyle -e ':completion:*:directories' fake '
[[ -z $PREFIX$SUFFIX || -d $PREFIX$SUFFIX ]] ||
+autocomplete:recent-directories
'
zstyle ':completion:*:directories' sort no
```
## Troubleshooting
Try the steps in the
[bug report template](.github/ISSUE_TEMPLATE/bug-report.md).
## Author & License
See the [LICENSE](LICENSE) file for details.

View File

@@ -0,0 +1,7 @@
setopt localoptions extendedglob clobber NO_aliases localloops pipefail NO_shortloops NO_unset
zmodload zsh/param/private
autoload -Uz zmathfunc && zmathfunc
builtin autoload -UWz $PWD/{Completions,Functions}/**/[_.]autocomplete(__|:)*~*.zwc(DN-.:P)
typeset -gA compstate=() _lastcomp=()
typeset -ga compargs=() comppostfuncs=() comptags=()
typeset -g WIDGET= WIDGETSTYLE= context= curcontext=

View File

@@ -0,0 +1,30 @@
Setup:
```zsh
% source Tests/__init__.zsh
% autocomplete:_main_complete:new() {}
%
```
`menu-` widgets should not keep a partial list.
```zsh
% compstate[old_list]=yes
% typeset -g _autocomplete__partial_list
% WIDGETSTYLE=menu-complete
% _autocomplete__should_insert_unambiguous() { false }
% .autocomplete__complete-word__completion-widget
% print -r -- $compstate[old_list]
%
```
You cannot insert unambiguous using only an old list.
```zsh
% compstate[old_list]=yes
% unset _autocomplete__partial_list
% _lastcomp[unambiguous]=foo
% _autocomplete__should_insert_unambiguous() { true }
% .autocomplete__complete-word__completion-widget
% print -r -- $compstate[old_list]
%
```

View File

@@ -0,0 +1,95 @@
Setup:
```zsh
% source Tests/__init__.zsh
%
```
Default completion widget inserts first match.
```zsh
% compstate[old_list]=keep
% _lastcomp[nmatches]=2
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]}
1
%
```
`menu-` widgets should cycle between completions.
```zsh
% WIDGETSTYLE=menu-complete
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
menu:1 0
%
```
`menu-select` widgets should enter the menu.
```zsh
% WIDGETSTYLE=menu-select
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
menu:1 1
%
```
`menu-select` widgets with `search` in the name should start a full-text search.
```zsh
% WIDGET=incremental-history-search-forward
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
menu:1 1 search-forward
%
```
`reverse-menu-complete` widgets should select the last match.
```zsh
% WIDGETSTYLE=reverse-menu-complete
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]}
menu:0
% WIDGETSTYLE=
%
```
Widgets with `reverse` in the name should do so, too.
```zsh
% WIDGET=reverse-complete
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]}
0
% WIDGET=
%
```
Widgets can be configured to inset `unambiguous` instead.
```zsh
% zstyle ':autocomplete:*' insert-unambiguous yes
% compstate[old_list]=
% compstate[nmatches]=2
% compstate[unambiguous]=foo
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
unambiguous 0
%
```
Menu widgets should prefix `unambiguous` with `automenu-`.
```zsh
% WIDGETSTYLE=menu-select
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
automenu-unambiguous 0
% compstate[unambiguous]=
% WIDGETSTYLE=
%
```
Certain completions should be suffixed with a space.
```zsh
% zstyle ':autocomplete:*' add-space foo bar
% _comp_tags='foo bar'
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]}
'1 '
%
```

View File

@@ -0,0 +1,10 @@
#!/bin/zsh -f
cd $( git rev-parse --show-toplevel )
git --version
print =zsh
typeset -p1 VENDOR OSTYPE ZSH_VERSION ZSH_PATCHLEVEL
env -i HOME=$( mktemp -d ) PATH=$PATH FPATH=$FPATH zsh -f -- \
clitest/clitest --list-run --progress dot --prompt '%' --color always \
-- $PWD/Tests/*.md

View File

@@ -0,0 +1,21 @@
#!/bin/zsh
unsetopt listbeep
() {
zmodload -F zsh/parameter p:funcfiletrace
zmodload zsh/param/private
typeset -ga _autocomplete__func_opts=(
localoptions extendedglob clobber
NO_aliases evallineno localloops pipefail NO_shortloops NO_unset warncreateglobal
)
setopt $_autocomplete__func_opts[@]
typeset -ga _autocomplete__funcfiletrace=( $funcfiletrace )
local basedir=${${(%):-%x}:P:h}
hash -d autocomplete=$basedir zsh-autocomplete=$basedir
builtin autoload +X -Uz ~autocomplete/Functions/**/.autocomplete__*~*.zwc(D-:)
.autocomplete__main "$@"
}