move to new repo
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 569 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 339 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 117 KiB |
@@ -0,0 +1,6 @@
|
||||
#compdef -command-
|
||||
|
||||
local -P ret=1
|
||||
_autocd "$@" &&
|
||||
ret=0
|
||||
return ret
|
||||
@@ -0,0 +1,10 @@
|
||||
#autoload
|
||||
|
||||
local -Pi len=$(( ${@[(i)(-|--)]} - 1 ))
|
||||
(( len < $# )) &&
|
||||
return len
|
||||
|
||||
len=${@[(I)-*]}
|
||||
[[ $@[len] == -*[PSpsiIdJVXxrRWFMOAD] ]] &&
|
||||
(( len++ ))
|
||||
return len
|
||||
@@ -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 "$@"
|
||||
@@ -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
|
||||
@@ -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} ]]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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]"
|
||||
)"
|
||||
@@ -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
|
||||
@@ -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 ))
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/zsh
|
||||
|
||||
if [[ $RBUFFER == *$'\n'* ]]; then
|
||||
builtin zle down-line
|
||||
else
|
||||
builtin zle menu-select -w
|
||||
fi
|
||||
@@ -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} "$@"
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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=
|
||||
@@ -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]
|
||||
|
||||
%
|
||||
```
|
||||
@@ -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 '
|
||||
%
|
||||
```
|
||||
@@ -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
|
||||
@@ -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 "$@"
|
||||
}
|
||||