🔧 Massive update to personal gentoo configs

This commit is contained in:
tomit4 2025-12-03 02:44:11 -08:00
parent 505f6363ab
commit 6686861b86
497 changed files with 29421 additions and 7191 deletions

418
gentoo/dotfiles/.aliases Normal file
View file

@ -0,0 +1,418 @@
# Custom Aliases
alias cdls="source mycd"
#alias {les,less,more}="moar -wrap -no-statusbar -style friendly" # -no-linenumbers"
alias {rg,grep}="rg -i --colors 'match:fg:blue' --colors 'line:fg:blue'" # -N
alias {drag,rdrag}="ripdrag"
alias dusty="dust -i -v 'node_modules'"
#alias lf="ranger"
alias cp="cp -i"
alias df='df -h'
alias free='free -mh'
alias dmesg="doas dmesg -H"
alias kernel="uname -r"
alias mkcd='{ IFS= read -r d && mkdir "$d" && cd "$d"; } <<<'
alias cpwd='pwd | xclip -sel clip'
alias docs="cd ~/Documents && ls"
alias desk="cd ~/Desktop && ls"
alias down="cd ~/Downloads && ls"
alias pics="cd ~/Pictures && ls"
alias pods="cd ~/Podcasts && ls"
alias music="cd ~/Music && ls"
alias vids="cd ~/Videos && ls"
alias mov="cd ~/Videos/mov/ && ls"
alias {sand,sandbox}="cd ~/sandbox && ls"
alias games="cd ~/.local/share/Steam/steamapps/common && ls"
alias notes="cd ~/Documents/notes && ls"
alias scripts="cd ~/scripts && ls"
alias books="cd ~/Documents/books && ls"
alias config="cd ~/.config && ls"
alias cache="cd ~/.cache && ls"
#alias svs="cd /etc/runit/sv && ls"
alias offline="cd ~/Documents/offline_notes && ls"
# alias vpnup="doas sv up wireguard"
# alias vpndown="doas sv down wireguard"
alias pihole="docker exec -it pihole-unbound pihole"
alias obs="gobble flatpak run com.obsproject.Studio"
alias Code="cd ~/Documents/Code && ls"
alias graphics="cd ~/Documents/Graphic_Design && ls"
alias Math="cd ~/Documents/Math && ls"
alias cplus="cd ~/Documents/Code/cpp && ls"
alias js="cd ~/Documents/Code/javascript && ls"
alias py="cd ~/Documents/Code/python && ls"
#alias {pysh,py-sh}="ipython"
#alias phpd="cd ~/Documents/Code/php && ls"
alias golang="cd ~/Documents/Code/golang && ls"
alias {rust,rustlang}="cd ~/Documents/Code/rust && ls"
alias {leet,leetcode}="cd ~/Documents/Code/leetcode/ && ls"
alias {neet,neetcode}="cd ~/Documents/Code/leetcode/neetcode_roadmap/ && ls"
alias neetvids="cd ~/Videos/yt/educational/dsa/leetcode_explanations/neetcode_roadmap/ && ls"
# alias zsaves="cd ~/Zomboid/Saves/Sandbox/ && ls"
# alias zsaves="cd ~/Games/umu/umu-default/drive_c/users/steamuser/Zomboid/Saves/Sandbox && ls"
# alias zmods="cd ~/Games/umu/umu-default/drive_c/users/steamuser/Zomboid/mods && ls"
alias zsaves="cd ~/.wine/drive_c/users/brian/Zomboid/Saves/Sandbox/ && ls"
alias zmods="cd ~/.wine/drive_c/users/brian/Zomboid/mods/ && ls"
alias logs="cd /var/log && ls"
alias llama="cd ~/.local/share/llama.cpp && ./llama.sh ; cd"
alias wtuts="cd ~/Documents/Code/web_tutorials && ls"
alias btuts="cd ~/Documents/Code/book_tutorials && ls"
alias fonts="fc-list"
alias locate="locate -i"
alias bat="bat --theme Nord"
alias ls="eza --icons"
alias li="eza -1 --icons"
alias ll="eza -lih --icons"
alias {lsdir,lsd}="ls -t -1" # list only the directories
alias lshw="doas lshw -short | less" # list hardware
alias {hw,hwinfo}="hwinfo | less" # extensive info on hardware devices
alias dmid="doas dmidecode | less"
alias of="onefetch -o yaml | yq -C"
alias mkdir='mkdir -pv'
alias dict='def'
alias {:q,ex,esc}="exit"
alias :w="touch"
alias digg="doggo"
alias dff="/usr/bin/df -h && duf"
alias {his,hist}="history"
alias mnt="doas mount /dev/sda1 /mnt/media/usb-drive && cd /mnt/media/usb-drive && ls"
alias umnt="doas umount /mnt/media/usb-drive"
alias {imnt,iphone}="ifuse /home/brian/iphone && cd /home/brian/iphone && ls"
alias iumnt="doas umount /home/brian/iphone"
alias kmnt="doas mount /dev/sda /mnt/media/kobo/ && cd /mnt/media/kobo/.kobo && ls"
alias kumnt="doas umount /dev/sda /mnt/media/kobo"
# alias pixel="cd /run/user/1000/gvfs/mtp:host=Google_Pixel_7a_34211JEHN03088/Internal\ shared\ storage && ls"
alias pixel="cd /mnt/media/pixel/Internal\ shared\ storage && ls"
alias pmnt="aft-mtp-mount /mnt/media/pixel && cd /mnt/media/pixel/Internal\ shared\ storage && ls"
alias pumnt="fusermount3 -uz /mnt/media/pixel"
alias pikoshi="cd ~/Documents/Code/group_projects/pikoshi/ && ls"
alias rm="newrm -vi"
alias calendar="calcurse"
alias cal="newcal"
# simple tool for math, see https://github.com/metadelta/mdlt
# alias math="mdlt simplify"
alias mv="mv -v"
alias ln="ln -i"
alias {doasedit,sudoedit}="doas rnano"
alias clean_cache="doas paccache -r && doas rm -r /home/brian/.cache/*"
alias shred="doas shred -uzv" # shreds the file and overwrites it with 1s and 0s, shows progress
alias indelible="doas chattr +i" # makes a file impossible to delete (except with sudo)
alias diff=difft
alias cdiff="colordiff"
# alias kfonts="kitty +list-fonts"
# alias {kdiff,kitten,kd}="kitty +kitten diff"
alias fzfi="rg --files --hidden --follow --no-ignore-vcs -g '!{node_modules,.git}' | fzf"
alias bluepill="neo-matrix --bold=2 -D --color=cyan --density=0.75 --charset=katakana --speed=12 --fullwidth --screensaver"
alias moo="cowsay moo"
# alias btop="kitty -o font_size=12 -o background_opacity=0.50 -e btop; fullscreen enable"
alias tshift='doas timeshift --delete && notify-send "  Previous Backup Deleted!" && doas timeshift --create && notify-send "  New Backup Created!" && doas timeshift --list'
alias btop="bst -e btop"
# alias gimp="gimp --no-splash"
alias krita="gobble flatpak run org.kde.krita --nosplash"
#alias onlyoffice="onlyoffice-desktopeditors"
# alias toonz="opentoonz"
alias {sqlite,sql}="sqlite3"
alias {sqcipher,sqc}="sqlcipher"
alias sqlite-cli="litecli"
alias sq-cli="litecli" # doesn't work with sqlcipher
# alias view="kitty +kitten icat"
alias view="gobble sxiv"
alias view-w="img2sixel"
# alias csview="csvlook"
alias viewer="feh -F -Z -x"
alias mupdf='gobble mupdf -r 204'
alias dkpdf='gobble mupdf -I -r 204'
alias sign='gobble xournalpp'
alias keys="gobble keepassxc $KDBX"
alias {jscrl,jsoncurl,jcrl}="jscurl"
alias kevents="xev -event keyboard"
alias sk="screenkey"
# alias word="wordgrinder"
alias spreadsheet="sc-im"
alias pvctrl="pavucontrol"
alias open="xdg-open"
alias nc="nc -v"
alias dump="doas tcpdump"
alias {wireshark-cli,wshark}="tshark"
alias wireshark="gobble wireshark -k -i wlp6s0"
alias route="traceroute"
alias listen="ss -a -t -p"
alias {lh,lighthouse}="lighthouse --view"
alias {pdf2,pdf2txt}="pdftotext"
alias {pdfg,greppdf}="pdfgrep"
alias tree="tree -ad -L 3 --prune -C -I 'node_modules|.git'"
alias {vi,vim,nv,nvi}="nvim"
alias {vfz,nvfz,nvim-fzf,fzf-nvim}='nvim "$(fzf)"'
alias {cproj,mproj,myproj}="cd '$cproj' && ls"
alias blog="cd ~/Documents/Code/blog/ && ls"
# alias rsm="doas rsm"
alias updatedb="doas updatedb"
alias iftop-w="doas iftop -i wlan0 -P"
alias wmon="wavemon"
# usage p0f -i wlan0
alias p0f="doas p0f"
alias vnstat-w="vnstat -i wlan0 --json | jq"
alias {bluetooth,bt}="bluetoothctl"
alias {wi,ssid}="doas iw wlan0 scan | grep SSID"
# NOTE: Find wpa_supplicant and dhcpcd ways to do these NetworkManager commands:
# alias wifi-list="connmanctl services"
#alias wifi-list="nmcli device wifi list"
# alias wifi-scan="connmanctl scan wifi"
# alias wifi-connect="connmanctl connect wifi_34cff6c9b922_44495a5a_managed_psk"
#alias wifi-connect='nmcli device wifi connect "${SSID}" password "${PASSWD}"'
# alias wifi-disconnect="connmanctl disconnect wifi_34cff6c9b922_44495a5a_managed_psk"
#alias wifi-disconnect="nmcli device disconnect wlan0"
#alias wifi-up="doas ifconfig wlan0 up"
#alias wifi-down="doas ifconfig wlan0 down"
alias lxapp="lxappearance"
alias hd="ncdu"
# one of our most usful aliases (useful in conjunction with yank, awk, etc.):
alias {xsel,catx}="xclip -sel clip" # useful with < or | redirection operators
# wayland has an equivalent as well:
alias {wsel,catw}="wl-copy" # useful with | redirection, i.e. echo "hi there" | catw
alias catp="xclip -out -selection clipboard; echo"
alias mouse="mousepad"
alias xclear="echo '' | xclip && xclip -selection clipboard /dev/null"
# alias diff="diff -wy"
alias exifread="exiftool"
alias exifrm="exiftool -all="
alias {html,html_pp}="hxnormalize -e"
alias {ltrans,trans,translate}="ltranslate"
alias skey='screenkey --position fixed --geometry $(slop)'
alias birth="stat / | grep 'Birth' | sed 's/Birth: //g' | cut -b 2-11"
# interfaces with asciinema.org for creating simple video tutorials on cli programs
alias ascii="asciinema"
alias mpv="gobble mpv --sub-font-size=40"
alias mpvl="gobble mpv --sub-font-size=40 --loop-file=inf"
alias mpva='/usr/bin/mpv "${1}" --no-video'
alias mpval='/usr/bin/mpv "${1}" --no-video --loop-file=inf'
alias mmpv='gobble mpv --fs --panscan=1.0'
# alias {qvid,qv4}="gobble qv4l2" # better amount of controls for webcam
# alias {gvid,gv4}="gobble guvcview" # set to 50hz
# bare bones, just views webcam:
alias {wcam,webcam}="gobble mpv --demuxer-lavf-o=video_size=1920x1080,input_format=mjpeg av://v4l2:/dev/video0 --profile=low-latency"
# use: record /path/to/file.mp3
# NOTE: instead of arecord (ALSA), find a way to use pw-record (via pipewire)
# alias record="arecord -vv --format=cd --file-type raw | lame -r - "
#alias {hp,headphones}="pactl set-default-sink alsa_output.usb-FIIO_FiiO_USB_DAC-E10-01.analog-stereo"
#alias {sp,speakers}="pactl set-default-sink alsa_output.pci-0000_0a_00.4.analog-stereo"
#alias dspeakers="pactl set-default-sink alsa_output.pci-0000_08_00.1.hdmi-stereo-extra1"
# laptop only
#alias lapspeakers="pactl set-default-sink alsa_output.pci-0000_00_1f.3.analog-stereo"
alias pulse="pamixer --set-volume"
alias {mu,mt,mute}="pamixer -t"
# laptop only currently, records video/audio, simply name filename .mp4 after command:
alias r_vid="ffmpeg -f v4l2 -framerate 24 -video_size 640x480 -i /dev/video0 -itsoffset 00:00:01.0 -f alsa -i default -c:v libx264 -preset ultrafast -c:a aac"
# for smaller downloads
alias ytdl='yt-dlp --sponsorblock-remove all --write-auto-sub -t mp4'
alias ytdl-w='yt-dlp "best*" --sponsorblock-remove all --write-auto-sub'
# for better resolutions
# alias ytdl-w='yt-dlp -f webm "bestvideo\*+bestaudio" --sponsorblock-remove all --write-auto-sub'
# for audio only
alias ytdl-a="yt-dlp --sponsorblock-remove all -x --audio-format mp3"
# alias thinkorswim="cd /home/brian/thinkorswim && java -jar launcher.jar && cd"
alias {cl,cls}="clear"
alias shutdown="shutdown.sh"
alias reboot="reboot.sh"
alias curltime="curl -w \"@$HOME/Documents/notes/curl-format.txt\" -o /dev/null -s"
#source ~/.zip_code
alias inxi-w="inxi --weather "${ZIPCODE}""
alias inxi-i="doas inxi -Fxzm"
alias pf="pfetch"
alias ff="fastfetch"
alias has="dependencycheck"
# alias ipaddr="curl ifconfig.me/all.json | jq"
alias hwclock="doas hwclock"
alias kill9="killall -9" # kills process by name, e.g. kill9 cmus
alias ports="netstat -tunlp"
# alias npm_upgrade="doas npm install --location=global npm@latest"
alias npm-l="npm list --global"
alias npm-s="npm start"
alias npm-t="npm test"
alias npm-o="npm outdated"
alias npm-u="npm update"
alias cargo-l="cargo install --list"
alias cargo-u="cargo install-update -a" # requires cargo-update crate
# alias {pacquery,pacq}="pacman -Q"
# alias {pacsearch,pacs}="pacman -Ss"
alias find-empty='find . -depth -empty'
alias rmdir-a='find . -depth -empty -delete'
alias paclogs="bat /var/log/pacman.log"
alias npm_list="find /usr/lib/node_modules -maxdepth 1 | sed -r 's/^\/usr\/lib\/node_modules\///g'"
# useful alias for recursively testing .test.js files on save
# alias jester="jest --watchAll"
# generate uuids using native bash
alias uuid="uuidgen"
alias kuuid="cat /proc/sys/kernel/random/uuid"
alias duuid="dbus-uuidgen"
# generate unique ids from cli, example for no special characters:
# alias manoid="nanoid --alphabet 'abcdefghijklmnopqrstuvwxyz' --size 40"
alias nanoid="npx nanoid"
# alias uglifyhtml="html-minifier --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --remove-tag-whitespace --use-short-doctype --minify-css true --minify-js true"
# autoprefixes css files
# requires installation:
# npm install -g postcss postcss-cli autoprefixer
alias {prefix,autoprefixer}="npx postcss *.css --use autoprefixer -d build/"
# informs on dependency updates
alias taze="npx taze"
# alias pip_upgrade="python -m pip install --upgrade pip"
alias rust_upgrade="rustup update"
alias nb="newsboat"
alias profane="profanity"
alias {gmail,email,mail,mutt,nmut,nmutt}="neomutt"
alias {mnb,nb-m}="cd ~/.config/newsboat && /usr/bin/cp $HOME/.config/newsboat/my_urls $HOME/.config/newsboat/urls && newsboat"
alias {ynb,nb-y}="/usr/bin/cp $HOME/.config/newsboat/yt_urls $HOME/.config/newsboat/urls && newsboat"
alias {rnb,nb-r}="cd ~/.config/newsboat && /usr/bin/cp $HOME/.config/newsboat/reddit_urls $HOME/.config/newsboat/urls && newsboat"
alias {tootnb,mastnb}="cd ~/.config/newsboat && /usr/bin/cp $HOME/.config/newsboat/mastodon_urls $HOME/.config/newsboat/urls && newsboat"
alias rt-rs="doas rm -r watch session && mkdir watch session && rm ~/Downloads/*.meta"
alias slink="streamlink"
#alias {epub,fbreader}="gobble FBReader"
alias scrot="scrot --count --delay"
alias grip="grip 6969"
alias sudoku="sku"
# alias rbd="checkrebuild -v"
# pip list and pip freeze command list pip packages
# poetry virtual env
alias venv='source ".venv/bin/activate"'
alias {uvenv,dvenv}="deactivate"
#Particularly useful commands more to remember than to use as aliases:
# alias optimalbufsize= 'stat -c "%o"' # followed by filename will give you the optimal read/write BUFSIZE for a file
alias {rcrm,rrm}="doas xargs -d\\n rm -r -v" # when piped after locate or find command, recursively removes outputted files
alias {rlast,dlast}="sed -i '$ d'" # removes last line of file
alias shchk="shellcheck"
# get top process eating memory
alias psmem="ps auxf | sort -nr -k 4 | head -5"
# get top process eating cpu
alias pscpu="ps auxf | sort -nr -k 3 | head -5"
# alias {say,speak,espeak}="espeak -v female5"
alias {say,speak,espeak}="speak-ng"
# useful bash commands
# grep, sed, awk, tr, sort, seq, paste, uniq
# git aliases
alias git-a="git add"
alias git-c="git commit -m"
alias git-p="git push"
alias {git-\#,git\#}="git rev-list --count --all"
# taken from Luke Smith, pushes to all remotes
alias git-pa="git remote | xargs -L1 git push --all"
# undo local file changes and keep your last commit
alias git-r="git reset --hard"
# undo local file changes and remove your last commit
alias git-rrh="git reset --hard HEAD^"
# keep local file changes and remove your last commit
alias git-rrs="git reset --soft HEAD^"
alias git-s="git status"
alias git-ss="git status short"
alias git-d="git diff"
alias {git-b,git-br}="git branch"
alias git-bl="git blame"
# for use with notes directory only
alias git-lo="git log --oneline --follow"
# alias {git-l,git-log}="git log --graph --format='format:%C(yellow)%h%C(reset) %s %C(magenta)%cr%C(reset)%C(auto)%d%C(reset)'"
alias {git-l,git-log}="git log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)' --all"
# more to just remember than to be used
alias git-stsh="git stash"
alias git-stsh-a="git stash apply"
alias git-stsh-d="git stash drop"
alias gitmojis="bat $HOME/Documents/notes/gitmoji.txt"
alias {vimhelp,helpvim,vimtips}="bat $HOME/Documents/notes/nvim_custom_keys.txt"
alias {nvim-c,cleanvim}="nvim --clean"
# tmux multiplexer session aliases
alias tmux-ls="tmux list--sessions"
alias {tmux-attach,tmux-a}="tmux a -t"
alias {tmux-kill,tmux-k}="tmux kill-ses -t"
# zellij multiplexer sesssion
# alias zj="zellij"
# programming specific aliases
alias rc="rustc"
alias cc="cc -Wall -Werror -Wextra -Wpedantic"
alias cprogrammingtags="ctags"
alias vgrind="valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes -s"
# game shortcuts
# alias gback="cp -r ~/.wine/drive_c/users/brian/AppData/Local/CD\ Projekt\ Red/ ~/game_save_backups/"
# alias {cbpunk,cyberpunk}="steam steam://rungameid/1091500 &"
# alias {twd,thewalkingdead}="steam steam://rungameid/1449690 &"
# alias {cbmods,cybermods}="cd ~/.local/share/Steam/steamapps/common/Cyberpunk\ 2077/archive/pc/mod && ls"
# docker specific Aliases
alias docker-ls="docker container ls -a && docker images"
alias docker-rm-c="docker container rm"
alias docker-rm-i="docker image rm"
alias {docker-k,docker-kill}="docker kill"
alias docker-exec="docker exec -it" # use with container ID and bash
# bookmarks
alias apnews='f(){ links https://apnews.com/"$1" ;}; f' # call using 'us' or 'world' as second arg
alias npr="links https://text.npr.org"
alias pbs="links https://lite.pbs.org"
# brings up oregon public broadcasting
alias radio='/usr/bin/mpv --no-video "https://ais-sa3.cdnstream1.com/2440_128.aac/playlist.m3u8?listeningSessionID=64c429fd0062f502_803653_uuxYz9V9_NjYuODUuODkuMzA6ODA\!_0000000vz1k\&downloadSessionID=0\&listenerId=esTrackblock0801483\&aw_0_1st.playerid=esPlayer&aw_0_1st.skey=1691381053"'
alias {gp,gopherpedia}="lynx gopher://gopherpedia.com:70"
alias gopher="lynx https://gopher.floodgap.com/gopher/gw.lite"
alias {ddg,duckduckgo}="links https://duckduckgo.com/lite"
alias bangs="links ~/Documents/notes/ddg_bangs.html"
# alias bangs="links https://duckduckgo.com/bang_lite.html"
alias wikipedia="links https://wikipedia.org"
alias hackernews="links https://news.ycombinator.com/"
# sustainable clothing brands (shoes and underwear)
# alias nothingnew="librewolf https://nothingnew.com &"
# alias wamaundies="librewolf https://wamaunderwear.com &"
# Kiwix (offline wikis)
alias kiwixdl="links download.kiwix.org"
# Anarchism
alias itsgoingdown="links https://itsgoingdown.org/"
alias puget="links https://pugetsoundanarchists.org/"
alias {theanarchistlibrary,anarchlib}="links https://theanarchistlibrary.org/"
# Programming Resources
# CS Basics
# Blogs
# Unix Sheikh, very good info regarding Unix and Linux
alias unixsheikh="links https://unixsheikh.com"
# A reddit like forum similar also to hackernews
alias lobsters="links https://lobste.rs"
# Check Tor
alias checktor="links https://check.torproject.org"
# display related aliases
alias brighten="xrandr --output HDMI-0 --brightness"
alias unred="redshift -x"
# run the following two aliases every time you update the kernels and nvidiadkms
# alias {grub_update,update_grub}="doas grub-mkconfig -o /boot/grub/grub.cfg"
# alias regen_initramfs="doas mkinitcpio -P"
# alias mpl="mplayer -fs"
# alias {mloop,mploop}="mplayer -fs -loop 0"
alias sd="sdir"
alias unu="upnup"
source $HOME/.linodes/.ips
source $HOME/.k_hidden
source $HOME/.n_hidden
alias "${scomm}"='cd "${secdir}" && mmpv * && home'
alias "${scomm2}"='mmpv ${secvid} && home'
alias "${scomm3}"='cd "${secdir2}" && mmpv * && home'
# GENTOO
alias wlop="doas watch -cn 10 genlop -ci"
alias portage="cd /etc/portage && ls"
alias portq="qlist -I"
alias depends="equery g"
alias {usedit,useedit}="doas nvim /etc/portage/package.use/package.use"
alias unmask="doas nvim /etc/portage/package.accept_keywords/package.unmask"

View file

@ -0,0 +1,4 @@
export XDG_CACHE_HOME="${HOME}/.cache"
export XDG_CONFIG_HOME="${HOME}/.config"
export XDG_DATA_HOME="${HOME}/.local/share"
export XDG_STATE_HOME="${HOME}/.local/state"

View file

@ -0,0 +1,30 @@
# /etc/skel/.bash_profile
# This file is sourced by bash for login shells. The following line
# runs your .bashrc and is recommended by the bash info pages.
if [[ -f ~/.bashrc ]] ; then
. ~/.bashrc
fi
# if test -z "${XDG_RUNTIME_DIR}"; then
# export XDG_RUNTIME_DIR=/tmp/xdg/"${UID}" -xdg-runtime-dir
# if ! test -d "${XDG_RUNTIME_DIR}"; then
# mkdir -p "${XDG_RUNTIME_DIR}"
# chmod 0700 "${XDG_RUNTIME_DIR}"
# fi
# fi
if test -z "${XDG_RUNTIME_DIR}"; then
export XDG_RUNTIME_DIR=/run/user/${UID}
fi
if test -d "${XDG_RUNTIME_DIR}"; then
perms="$(stat -c '%a %u' "${XDG_RUNTIME_DIR}")"
if [[ "${perms}" != "700 ${UID}" ]]; then
export -n XDG_RUNTIME_DIR
echo "WARNING! XDG_RUNTIME_DIR has incorrect permissions"
fi
else
mkdir -p "${XDG_RUNTIME_DIR}"
chmod 0700 "${XDG_RUNTIME_DIR}"
fi

235
gentoo/dotfiles/.bashrc Normal file
View file

@ -0,0 +1,235 @@
#!/bin/bash
# Changing over to ZSH for now, bashrc is kept here as emergency shell/posterity
#
# to change default shell, use chsh -s $(which bash)
# ~/.bashrc
#
# history -c && history -w && hash -r
# create custom env variables:
# export cproj="$HOME/Documents/Code/web_tutorials/advanced_programming_in_the_UNIX_environment"
export cproj="$HOME/Documents/Code/bash/bgit"
# custom BROWSER env for ddgr:
export BROWSER=links
export EDITOR=nvim # enable vi keybindings in terminal and terminal applications
set -o vi
# set -o noclobber
export PF_INFO="ascii os kernel pkgs shell wm editor"
# but keep ctrl l as the clear screen command bind -m vi-command 'Control-l: clear-screen'
bind -m vi-insert 'Control-l: clear-screen'
# Colorizes man pages
export GROFF_NO_SGR=1
export LESS_TERMCAP_mb=$'\e[1;34m'
export LESS_TERMCAP_md=$'\e[1;34m'
export LESS_TERMCAP_me=$'\e[0m'
export LESS_TERMCAP_se=$'\e[0m'
export LESS_TERMCAP_so=$'\e[01;33m'
export LESS_TERMCAP_ue=$'\e[0m'
export LESS_TERMCAP_us=$'\e[1;4;33m'
# clears the history completely upon loading bash
cat /dev/null >~/.bash_history && history -c && hash -r
[[ $- != *i* ]] && return
colors() {
local fgc bgc vals seq0
printf "Color escapes are %s\n" '\e[${value};...;${value}m'
printf "Values 30..37 are \e[33mforeground colors\e[m\n"
printf "Values 40..47 are \e[43mbackground colors\e[m\n"
printf "Value 1 gives a \e[1mbold-faced look\e[m\n\n"
# foreground colors
for fgc in {30..37}; do
# background colors
for bgc in {40..47}; do
fgc=${fgc#37} # white
bgc=${bgc#40} # black
vals="${fgc:+$fgc;}${bgc}"
vals=${vals%%;}
seq0="${vals:+\e[${vals}m}"
printf " %-9s" "${seq0:-(default)}"
printf " ${seq0}TEXT\e[m"
printf " \e[${vals:+${vals+$vals;}}1mBOLD\e[m"
done
echo
echo
done
}
[ -r /usr/share/bash-completion/bash_completion ] && . /usr/share/bash-completion/bash_completion
# Change the window title of X terminals
case ${TERM} in
xterm* | rxvt* | Eterm* | aterm | kterm | gnome* | interix | konsole*)
PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME%%.*}:${PWD/#$HOME/\~}\007" '
;;
screen*)
PROMPT_COMMAND='echo -ne "\033_${USER}@${HOSTNAME%%.*}:${PWD/#$HOME/\~}\033\\" '
;;
esac
use_color=true
# Set colorful PS1 only on colorful terminals.
# dircolors --print-database uses its own built-in database
# instead of using /etc/DIR_COLORS. Try to use the external file
# first to take advantage of user additions. Use internal bash
# globbing instead of external grep binary.
safe_term=${TERM//[^[:alnum:]]/?} # sanitize TERM
match_lhs=""
[[ -f ~/.dir_colors ]] && match_lhs="${match_lhs}$(<~/.dir_colors)"
[[ -f /etc/DIR_COLORS ]] && match_lhs="${match_lhs}$(</etc/DIR_COLORS)"
[[ -z ${match_lhs} ]] &&
type -P dircolors >/dev/null &&
match_lhs=$(dircolors --print-database)
[[ $'\n'${match_lhs} == *$'\n'"TERM "${safe_term}* ]] && use_color=true
if ${use_color}; then
# Enable colors for ls, etc. Prefer ~/.dir_colors #64489
if type -P dircolors >/dev/null; then
if [[ -f ~/.dir_colors ]]; then
eval $(dircolors -b ~/.dir_colors)
elif [[ -f /etc/DIR_COLORS ]]; then
eval $(dircolors -b /etc/DIR_COLORS)
fi
fi
if [[ ${EUID} == 0 ]]; then
PS2='\[\033[01;31m\][\h\[\033[01;36m\] \W\[\033[01;31m\]]\$\[\033[00m\] '
else
PS1='\[\033[01;32m\][\u@\h\[\033[01;37m\] \W\[\033[01;32m\]]\$\[\033[00m\] '
fi
alias ls='ls --color=auto'
alias grep='grep --colour=auto'
alias egrep='egrep --colour=auto'
alias fgrep='fgrep --colour=auto'
else
if [[ ${EUID} == 0 ]]; then
# show root@ when we don't have colors
PS1='\u@\h \W \$ '
else
PS1='\u@\h \w \$ '
fi
fi
unset use_color safe_term match_lhs sh
alias cp="cp -i" # confirm before overwriting something
alias df='df -h' # human-readable sizes
alias free='free -m' # show sizes in MB
alias np='nano -w PKGBUILD'
alias more=less
xhost +local:root >/dev/null 2>&1
complete -cf sudo
# Bash won't get SIGWINCH if another process is in the foreground.
# Enable checkwinsize so that bash will check the terminal size when
# it regains control. #65623
# http://cnswww.cns.cwru.edu/~chet/bash/FAQ (E11)
shopt -s checkwinsize
# No need to type cd when changing directories.
shopt -s autocd
# No need to have capitalization correct
shopt -s cdspell
shopt -s expand_aliases
# export QT_SELECT=4
# Enable history appending instead of overwriting. #139609
shopt -s histappend
#
# # ex - archive extractor
# # usage: ex <file>
ex() {
if [ -f $1 ]; then
case $1 in
*.tar.bz2) tar xjf $1 ;;
*.tar.gz) tar xzf $1 ;;
*.bz2) bunzip2 $1 ;;
*.rar) unrar x $1 ;; *.gz) gunzip $1 ;;
*.tar) tar xf $1 ;;
*.tbz2) tar xjf $1 ;;
*.tgz) tar xzf $1 ;;
*.zip) unzip $1 ;;
*.Z) uncompress $1 ;;
*.7z) 7z x $1 ;;
*) echo "'$1' cannot be extracted via ex()" ;;
esac
else
echo "'$1' is not a valid file"
fi
}
# Formats history command with dates/times
HISTTIMEFORMAT="%d/%m/%y %T "
# Displays current git branch if there is any and also styles bash prompt
# export PS1='\[\033[01;34m\][\u@\h\[\033[01;37m\] \W\[\033[01;34m\]]\$\033[01;34m\] $(git branch 2>/dev/null | grep '^*' | colrm 1 2)\n\033[01;34m└─>\033[37m '
export PS1='\[\033[01;34m\][\[\033[01;37m\] \W\[\033[01;34m\]]\$\033[01;34m\] $(git branch 2>/dev/null | grep '^*' | colrm 1 2)\n\033[01;34m└─>\033[37m '
# Configure thefuck
eval "$(thefuck --alias)"
export PATH="$PATH:/home/brian/scripts:/home/brian/.local/bin:/home/brian/.local/share/nvim/lsp_servers:/home/brian/.cargo/bin:/home/brian/go/bin"
export GREP_COLORS='ms=01;34'
# icons-in-terminal
# source ~/.local/share/icons-in-terminal/icons_bash.sh
# BEGIN_KITTY_SHELL_INTEGRATION
if test -n "$KITTY_INSTALLATION_DIR" -a -e "$KITTY_INSTALLATION_DIR/shell-integration/bash/kitty.bash"; then source "$KITTY_INSTALLATION_DIR/shell-integration/bash/kitty.bash"; fi
# END_KITTY_SHELL_INTEGRATION
alias home="xclear && cd /home/brian && clear && history -c && hash -r"
# Various source files (do not place at end of zshrc/bashrc)
source ~/.aliases
source ~/.gh_pat
source ~/.ssid_passwd
source /home/brian/.bash_completions/typer.sh
# Use bash-completion, if available
[[ $PS1 && -f /usr/share/bash-completion/bash_completion ]] &&
source /usr/share/bash-completion/bash_completion
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
# Sets Up Node Version Manager
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
# Set up TTY colors
# pulled form https://github.com/lewisacidic/nord-tty
if [ "$TERM" = "linux" ]; then
echo -en \\e]P02E3440
echo -en \\e]P1BF616A
echo -en \\e]P2A3BE8C
echo -en \\e]P3EBCB8B
echo -en \\e]P481A1C1
echo -en \\e]P5B48EAD
echo -en \\e]P688C0D0
echo -en \\e]P7E5E9F0
echo -en \\e]P84C566A
echo -en \\e]P9BF616A
echo -en \\e]PAA3BE8C
echo -en \\e]PBEBCB8B
echo -en \\e]PCB48EAD
echo -en \\e]PD8FBCBB
echo -en \\e]PEECEFF4
clear
fi
[ -f /opt/miniconda3/etc/profile.d/conda.sh ] && source /opt/miniconda3/etc/profile.d/conda.sh
export CUDA_HOME=/opt/miniconda3/condabin/conda

92
gentoo/dotfiles/.xinitrc Normal file
View file

@ -0,0 +1,92 @@
#!/bin/sh
# This is an ~/.xinitrc example file
userresources=$HOME/.Xresources
usermodmap=$HOME/.Xmodmap
sysresources=/etc/X11/xinit/.Xresources
sysmodmap=/etc/X11/xinit/.Xmodmap
# merge in defaults and keymaps
if [ -f $sysresources ]; then
xrdb -merge $sysresources
fi
if [ -f $sysmodmap ]; then
xmodmap $sysmodmap
fi
if [ -f "$userresources" ]; then
xrdb -merge "$userresources"
fi
if [ -f "$usermodmap" ]; then
xmodmap "$usermodmap"
fi
# start some nice programs
if [ -d /etc/X11/xinit/xinitrc.d ]; then
for f in /etc/X11/xinit/xinitrc.d/?*.sh; do
[ -x "$f" ] && . "$f"
done
unset f
fi
# force XDG_SESSION_TYPE to be x11
export XDG_SESSION_TYPE=x11
export QT_QPA_PLATFORMTHEME=qt5ct
# export XDG_DATA_DIRS=
# Start compositor
# picom blah blah ...
#Desktop
xrandr --output HDMI-0 --brightness 1.1 & #--set TearFree on &
#Laptop
# brillo -S 70
# start redshift
# redshift -O 5000 &
picom --config /home/brian/.config/picom/picom.conf & # --experimental-backends &
# GTK relies on this for icon themes
# export GTK_ICON_THEME=Papirus-Dark
# Start applets, daemons, etc.
# diskie
# nm-applet
dunst &
xbanish &
xsetroot -cursor_name left_ptr &
# espanso start --unmanaged &
# pulseaudio --start &
# enables AppArmor's aa-notify by default (supposed to be a runit script...)
# aa-notify --user $USER -p -s 1 -w 60 -f /var/log/audit/audit.log
# Notification for Laptop Battery
# echo 'export DBUS_SESSION_BUS_ADDRESS='$DBUS_SESSION_BUS_ADDRESS > /tmp/dsba &
# chmod +x /tmp/dsba &
# /tmp/dsba &
# /home/brian/scripts/batnotify &
#
if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
eval "$(dbus-launch --sh-syntax --exit-with-session)"
fi
# Start pipewire
# /usr/bin/pipewire &
# /usr/bin/pipewire-pulse &
# /usr/bin/wireplumber &
# Start i3wm
# exec i3
# redshift -O 5000 &
#feh --bg-fill /home/brian/Pictures/leaves.jpg &
# Start bspwm
pgrep -x sxhkd >/dev/null || sxhkd -c /home/brian/.config/sxhkd/sxhkdrc &
# exec bspwm -c /home/brian/.config/bspwm/bspwmrc
exec dbus-launch --sh-syntax --exit-with-session bspwm -c /home/brian/.config/bspwm/bspwmrc

14
gentoo/dotfiles/.zprofile Normal file
View file

@ -0,0 +1,14 @@
# Ensure XDG_RUNTIME_DIR points to the correct location
if [ -z "$XDG_RUNTIME_DIR" ]; then
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
# create if it doesn't exist
[ ! -d "$XDG_RUNTIME_DIR" ] && mkdir -p "$XDG_RUNTIME_DIR" && chmod 0700 "$XDG_RUNTIME_DIR"
fi
# Start dbus session if it's not already running
if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
eval "$(dbus-launch --sh-syntax --exit-with-session)"
fi
[[ -z $DISPLAY && $(tty) = /dev/tty1 ]] && exec startx

View file

@ -0,0 +1,48 @@
---
name: Bug report
about: Always include a minimal, reproducible test case. If I cannot reproduce it, then I cannot fix it!
title: ''
labels: type::bug report
assignees: ''
---
## Environment
<!-- Run the code below and share the output. -->
```zsh
% typeset -p1 VENDOR OSTYPE ZSH_PATCHLEVEL _autocomplete__funcfiletrace
<output>
% git -C ~autocomplete log --oneline -n1
<output>
```
* Operating system: <!-- for example, 'macOS Ventura 13.4' or 'Ubuntu 22.04.2 LTS' -->
* Terminal emulator: <!-- for example, 'Terminal.app' or 'Konsole' -->
## Steps to reproduce
<!-- Update the code below to create a test case that demonstrates the problem
in an isolated environment. -->
```zsh
% cd $(mktemp -d)
% git clone --depth 1 -- https://github.com/marlonrichert/zsh-autocomplete.git
<output>
% > .zshrc <<EOF
setopt interactivecomments transientrprompt
PS1='%# '
PS2=
RPS2='%^'
source $PWD/zsh-autocomplete/zsh-autocomplete.plugin.zsh
EOF
% env -i HOME=$PWD PATH=$PATH TERM=$TERM ${TERMINFO:+TERMINFO=$TERMINFO} zsh -d
% <inputs>
<output>
```
<details>
<summary>Contents of <code>~autocomplete-log/YYYY-MM-DD.log</code> (click to expand)</summary>
<pre>
<!-- Replace this line with the contents of the above log file. -->
</pre>
</details>

View file

@ -0,0 +1 @@
blank_issues_enabled: false

View file

@ -0,0 +1,32 @@
---
name: Feature request
about: If this template doesn't suit your needs, then please open a Discussion instead.
title: ''
labels: type::feature request
assignees: ''
---
## What do you want?
<!-- Give a brief summary of the feature. -->
## Why do you want this?
<!-- Explain what you would use this for or how this would enhance your experience. -->
## Who else would benefit from this?
<!-- Describe what kind of users would enjoy this feature. -->
## How should it work?
<!-- Fill out the steps below. -->
Given the following situation:
* <!-- State the circumstances under which this feature would apply. -->
*
When I perform the following steps:
1. <!-- List what actions you would take in the situation described above. -->
1. <!-- Numbering is automatic. -->
Then I expect the following to happen:
* <!-- Describe how you would expect the system to behave in response to your actions above. -->
*

View file

@ -0,0 +1,20 @@
name: 'Lock Threads'
on:
schedule:
- cron: '30 5,17 * * *'
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2
with:
issue-lock-inactive-days: '90'

View file

@ -0,0 +1,25 @@
name: test
on:
push:
pull_request:
workflow_dispatch:
jobs:
test-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
- run: ./run-tests.zsh
shell: zsh {0}
test-ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
- run: sudo apt install zsh
- run: ./run-tests.zsh
shell: zsh {0}

View file

@ -0,0 +1,3 @@
[submodule "clitest"]
path = clitest
url = https://github.com/aureliojargas/clitest.git

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,156 @@
#autoload
_autocomplete__history_lines() {
# 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=( ${(@r: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 ]]; then
# Enable multi-select.
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
}
_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
}
_autocomplete__history_lines "$@"

View file

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

View file

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

View file

@ -0,0 +1,45 @@
#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] < 2 )) &&
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
typeset -g _autocomplete__unambiguous="$compstate[unambiguous]"
# 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,679 @@
#!/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
}
# function -T
.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
}
# function -t
.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_delay = max( 0, seconds_delay - _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
}
# function -T
.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.
exec {fd}<&-
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
}
}
return 0
}
# function -T
.autocomplete:async:start() {
local fd=
sysopen -r -o cloexec -u fd <(
local +h PS4=$_autocomplete__ps4
.autocomplete:async:start:inner 2>>| $_autocomplete__log
)
builtin zle -Fw "$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
}
# function -T
.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'}"
}
}
# function -T
.autocomplete:async:pty() {
local +h PS4=$_autocomplete__ps4
# Make sure this shell dies after it times out.
local -F seconds=
builtin zstyle -s ":autocomplete:${curcontext}" timeout seconds ||
seconds=0.5
TMOUT=$(( [#10] 1 + seconds ))
# WORKAROUND: #654 Inside the PTY, formatting can lead to incorrect list trimming.
eval ${${${"$( zstyle -L '*' format )"}//zstyle /zstyle -d }// -e / }
builtin bindkey $'\C-@' .autocomplete:async:pty:zle-widget
local __tmp__=
builtin vared __tmp__
} 2>>| $_autocomplete__log
.autocomplete:async:pty:no-op() {
:
}
# function -T
.autocomplete:async:pty:zle-widget() {
local +h PS4=$_autocomplete__ps4
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[@]
local +h PS4=$_autocomplete__ps4
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
# function -T
.autocomplete:async:pty:completion-widget() {
local +h PS4=$_autocomplete__ps4
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
}
# function -T
.autocomplete:async:complete:fd-widget() {
setopt localoptions NO_banghist
{
local +h -F SECONDS=0.0
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 ||
return 0
local -a reply=()
IFS=$'\0' read -rAu $fd
shift -p reply
(( SECONDS += reply[2] ))
} always {
exec {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=$_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
compstate[insert]=
compstate[old_list]=
compstate[pattern_insert]=
.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
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() {
local -A _opts_=()
local -a _xopts_=() _displ_=() _matches_=()
local -P _displ_name_= _matches_name_=
zparseopts -A _opts_ -E -- D: E: O: X:=_xopts_ x:=_xopts_
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]
# Need to remove prompt escape codes or compadd might crash.
local -Pi _total_new_lines_="$(
zparseopts -a _xopts_ -D -E -- X: x:
builtin compadd "$@"
print -nr -- $(( $compstate[list_lines] - _old_total_lines ))
)"
local -Pi _new_completion_lines_="$(
zparseopts -a _xopts_ -D -E -- X: x:
builtin compadd "$@"
print -nr -- $(( $compstate[list_lines] - _old_total_lines ))
)"
local -Pi _new_heading_lines_=$(( _total_new_lines_ - _new_completion_lines_ ))
# Everything fits.
if (( _total_new_lines_ + $compstate[list_lines] <= _autocomplete__max_lines )); then
builtin compadd "$@"
return
fi
# We're apparently trying to add a message that spans more than one line, but not any completions.
if (( _new_heading_lines_ > 1 && _new_completion_lines_ < 1 )); then
.autocomplete:async:compadd:disable
return 1
fi
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_ ${(@r:COLUMNS-1:)${(PA)_displ_name_}[@]//$'\n'/\n}
_dopt_=( -ld $_displ_name_ )
(( _nmatches_per_line_ = 1 ))
fi
# 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_++ ))
if (( _nmatches_to_remove_ < $#_matches_ )); then
shift -p $_nmatches_to_remove_ _matches_ $_displ_name_
else
set -A _matches_
set -A $_displ_name_
fi
.autocomplete:async:compadd:disable
fi
_autocomplete__compadd_opts_len "$@"
builtin compadd "$_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,181 @@
#!/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
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 _autocomplete__unambiguous
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 rows' # `_setup` doesn't work well for this.
unset 'compstate[vared]'
local +h -a compprefuncs=( autocomplete:_main_complete:new:pre "$compprefuncs[@]" )
local +h -a comppostfuncs=( autocomplete:_main_complete:new:post "$comppostfuncs[@]" )
autocomplete:_main_complete:old "$@"
}
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,94 @@
#!/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
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,40 @@
#!/bin/zsh
zmodload -Fa zsh/files b:zf_mv b:zf_mkdir
zmodload -F zsh/parameter p:commands p:dirstack p:functions
${0}:precmd() {
[[ -v functions[+autocomplete:recent-directories] ]] &&
return
setopt autopushd pushdignoredups # Set *global* shell options.
builtin autoload -RUz 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
if ! (( $#dirstack[@] )); then
local -aU reply=()
chpwd_recent_filehandler
dirstack=( ${^reply[@]:#$PWD}(N-/) )
fi
+autocomplete:recent-directories:save() {
chpwd_recent_filehandler $PWD $dirstack[@]
}
add-zsh-hook chpwd +autocomplete:recent-directories:save
+autocomplete:recent-directories() {
typeset -ga reply
# Don't complete /, ~, $PWD or its children, or nonexistent dirs.
reply=( ${^dirstack[@]:#([/~]|$PWD(|/[^/]#))}(N) )
[[ -n $1 ]] &&
reply=( ${(M)reply:#(#l)*${(~j:*:)${(s::)1}}*} )
(( $#reply[@] ))
}
}

View file

@ -0,0 +1,46 @@
#!/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
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,27 @@
#!/bin/zsh
emulate -L zsh
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,25 @@
#!/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[@]" )
if [[ -z $compstate[old_list] && $curcontext == history-incremental-search* ]]; then
autocomplete:_main_complete:new - history-lines _autocomplete__history_lines
elif [[ -z $compstate[old_list] && $curcontext == recent-paths:* ]]; then
autocomplete:_main_complete:new - recent-paths _autocomplete__recent_paths
elif [[ -z $compstate[old_list] ]] ||
[[ -v _autocomplete__partial_list && $WIDGETSTYLE == (|*-)(list|menu)(|-*) ]] ||
_autocomplete__should_insert_unambiguous; then
compstate[old_list]=
autocomplete:_main_complete:new
else
compstate[old_list]=keep
autocomplete:_main_complete:new -
fi
[[ $_lastcomp[nmatches] -gt 0 && -n $compstate[insert] ]]

View file

@ -0,0 +1,65 @@
#autoload
local -a match=() mbegin=() mend=() # `zstyle` for some reason assigns these.
unset MENUMODE MENUSELECT
if [[ $WIDGETSTYLE != (|*-)menu(|-*) ]]; then
compstate[list]=
zle -Rc
fi
if [[ $_completer == _prefix ]]; then
compstate[to_end]=
else
compstate[to_end]='always'
fi
{
compstate[insert]=
if _autocomplete__should_insert_unambiguous; then
if [[ $WIDGETSTYLE == (|*-)menu(|-*) ]]; then
compstate[insert]='automenu-'
fi
compstate[insert]+='unambiguous'
unset _autocomplete__unambiguous
return
fi
if [[ $WIDGETSTYLE == (|*-)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
local -Pa comptags=()
if [[ $compstate[old_list] == keep ]]; then
comptags=( $=_lastcomp[tags] )
else
comptags=( $=_comp_tags )
fi
local -a spacetags=()
builtin zstyle -a ":autocomplete:$WIDGET:" add-space spacetags ||
spacetags=( executables aliases functions builtins reserved-words commands )
[[ -n ${comptags:*spacetags} ]] &&
compstate[insert]+=' '
} always {
unset _autocomplete__inserted
[[ -n $compstate[insert] ]] &&
typeset -g _autocomplete__inserted
}

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
(( _lastcomp[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,7 @@
#!/bin/zsh
if [[ $LBUFFER == *$'\n'* ]]; then
builtin zle up-line
else
builtin zle history-search-backward -w
fi

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,347 @@
# 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:
* Use `pacman` to install `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.
### 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
Autocomplete comes with its own backend for keeping track of and listing recent directories (which
uses part of
[`cdr`](https://zsh.sourceforge.io/Doc/Release/User-Contributions.html#Recent-Directories) under the
hood). However, you can override this and supply Autocomplete with recent directories from any
source that you like. To do so, define a function like this:
```sh
+autocomplete:recent-directories() {
<code>
typeset -ga reply=( <any number of absolute paths> )
}
```
#### Add a backend for recent files
Out of the box, Autocomplete doesn't track or offer recent files. However, it will do so if you add
a backend for it:
```sh
+autocomplete:recent-files() {
<code>
typeset -ga reply=( <any number of absolute paths> )
}
```
## 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,4 @@
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)

View file

@ -0,0 +1,106 @@
Setup:
```zsh
% source Tests/__init__.zsh
% typeset -gA compstate=() _lastcomp=()
% typeset -ga comptags=()
% typeset -g curcontext=
% zstyle ':autocomplete:*' add-space 'FOO' 'BAR'
% zstyle ':autocomplete:*' insert-unambiguous yes
%
```
If there is a common substring, insert it (if enabled).
```zsh
% compstate[old_list]=keep _lastcomp[tags]=
% compstate[nmatches]=0 _lastcomp[nmatches]=1
% WIDGETSTYLE= WIDGET=
% _autocomplete__unambiguous=FOO
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
unambiguous 0
%
```
When using a menu widget, add automenu.
```zsh
% compstate[old_list]=keep _lastcomp[tags]=
% compstate[nmatches]=0 _lastcomp[nmatches]=1
% WIDGETSTYLE=menu-select WIDGET=
% _autocomplete__unambiguous=FOO
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
automenu-unambiguous 0
%
```
Default inserts first match.
```zsh
% compstate[old_list]=keep _lastcomp[tags]=
% compstate[nmatches]=0 _lastcomp[nmatches]=2
% WIDGETSTYLE= WIDGET=
% _autocomplete__unambiguous=
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]}
1
%
```
Add a space for certain tags.
```zsh
% compstate[old_list]=keep _lastcomp[tags]='BAR BAZ'
% compstate[nmatches]=0 _lastcomp[nmatches]=1
% WIDGETSTYLE= WIDGET=
% _autocomplete__unambiguous=
% comptags=( BAR ) .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]}
'1 '
%
```
`menu-` widgets insert `menu:`.
```zsh
% compstate[old_list]=keep _lastcomp[tags]='BAR BAZ'
% compstate[nmatches]=0 _lastcomp[nmatches]=2
% WIDGETSTYLE=menu-complete WIDGET=
% _autocomplete__unambiguous=
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
'menu:1 ' 0
%
```
`menu-select` widgets add `MENUSELECT`
```zsh
% compstate[old_list]=keep _lastcomp[tags]='BAR BAZ'
% compstate[nmatches]=0 _lastcomp[nmatches]=2
% WIDGETSTYLE=menu-select WIDGET=
% _autocomplete__unambiguous=
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
'menu:1 ' 1
%
```
`menu-select` widgets with `search` in name add `MENUMODE=search-forward`.
```zsh
% compstate[old_list]=keep _lastcomp[tags]='BAR BAZ'
% compstate[nmatches]=0 _lastcomp[nmatches]=2
% WIDGETSTYLE=menu-select WIDGET=incremental-history-search-forward
% _autocomplete__unambiguous=
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]} $+MENUSELECT $MENUMODE
'menu:1 ' 1 search-forward
%
```
Reverse inserts the last match.
```zsh
% compstate[old_list]=keep _lastcomp[tags]='BAR BAZ'
% compstate[nmatches]=0 _lastcomp[nmatches]=2
% WIDGETSTYLE=reverse-menu-complete WIDGET=
% _autocomplete__unambiguous=
% .autocomplete__complete-word__post
% print -r -- ${(q+)compstate[insert]}
'menu:0 '
%
```

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 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 "$@"
}

View file

@ -0,0 +1,18 @@
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = tab
indent_size = 4
[*.md]
indent_style = space
[*.rb]
indent_style = space
indent_size = 2
[*.yml]
indent_style = space
indent_size = 2

View file

@ -0,0 +1,36 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
### Describe the bug
<!-- A clear and concise description of what the bug is. -->
### To Reproduce
Steps to reproduce the behavior:
<!-- If you are not able to reproduce it by running `zsh -df` and sourcing the plugin manually, it means there that the issue is caused by something in your local config file(s). Temporarily comment out or remove sections of your config and restart `zsh` until you narrow down exactly what is causing the issue. -->
```sh
% zsh -df
% source path/to/zsh-autosuggestions.zsh
% ... # what do you do to reproduce?
```
### Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
### Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->
### Desktop
- OS + distribution: <!-- e.g. Arch Linux 2019.07.01 -->
- Zsh version: <!-- `echo $ZSH_VERSION` -->
- Plugin version: <!-- or git commit hash if installed via git -->
### Additional context
<!-- Add any other context about the problem here. -->

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
### Is your feature request related to a problem? Please describe.
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
### Describe the solution you'd like
<!-- A clear and concise description of what you want to happen. -->
### Describe alternatives you've considered
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
### Additional context
<!-- Add any other context or screenshots about the feature request here. -->

View file

@ -0,0 +1,51 @@
on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
IMAGE_CACHE_PATH: /tmp/.image-cache
IMAGE_CACHE_NAME: zsh-autosuggestions-test
jobs:
determine-versions:
runs-on: ubuntu-22.04
outputs:
versions: ${{ steps.set-versions.outputs.versions }}
steps:
- uses: actions/checkout@v3
- id: set-versions
run: |
echo "versions=$(
grep "^[^#]" ZSH_VERSIONS \
| sed -E 's/(^|$)/"/g' \
| paste -sd ',' - \
| sed -e 's/^/[/' -e 's/$/]/'
)" >> $GITHUB_OUTPUT
test:
needs: determine-versions
runs-on: ubuntu-22.04
strategy:
matrix:
version: ${{ fromJson(needs.determine-versions.outputs.versions) }}
steps:
- uses: actions/checkout@v3
- name: Docker image cache
id: image-cache
uses: actions/cache@v3
with:
path: ${{ env.IMAGE_CACHE_PATH }}
key: image-cache-${{ matrix.version }}-${{ hashFiles('Dockerfile', 'install_test_zsh.sh', 'Gemfile.lock') }}
- name: Load cached docker image if available
if: ${{ steps.image-cache.outputs.cache-hit }}
run: gunzip < $IMAGE_CACHE_PATH/$IMAGE_CACHE_NAME.tar.gz | docker load
- name: Build the docker image if necessary
if: ${{ !steps.image-cache.outputs.cache-hit }}
run: |
docker build --build-arg TEST_ZSH_VERSION=${{ matrix.version }} -t $IMAGE_CACHE_NAME .
mkdir -p $IMAGE_CACHE_PATH
docker save $IMAGE_CACHE_NAME | gzip > $IMAGE_CACHE_PATH/$IMAGE_CACHE_NAME.tar.gz
- name: Run the tests
run: |
docker run --rm \
-v $PWD:/zsh-autosuggestions \
$IMAGE_CACHE_NAME \
make test

View file

@ -0,0 +1,3 @@
--color
--require spec_helper
--format documentation

View file

@ -0,0 +1,30 @@
# Rails:
# Enabled: true
AllCops:
TargetRubyVersion: 2.3
Include:
- '**/Rakefile'
- '**/config.ru'
- '**/Gemfile'
Metrics/LineLength:
Max: 120
Style/Documentation:
Enabled: false
Style/DotPosition:
EnforcedStyle: trailing
Style/FrozenStringLiteralComment:
Enabled: false
Style/Lambda:
Enabled: false
Style/MultilineMethodCallIndentation:
EnforcedStyle: indented
Style/TrailingUnderscoreVariable:
Enabled: false

View file

@ -0,0 +1 @@
2.5.3

View file

@ -0,0 +1,124 @@
# Changelog
## v0.7.1
- Clear POSTDISPLAY instead of unsetting (#634)
- Always reset async file descriptor after consuming it (#630)
- Always use builtin `exec` (#628)
- Add `history-beginning-search-*-end` widgets to clear widget list (#619)
- Switch CI from Circle CI to GitHub Actions
## v0.7.0
- Enable asynchronous mode by default (#498)
- No longer wrap user widgets starting with `autosuggest-` prefix (#496)
- Fix a bug wrapping widgets that modify the buffer (#541)
## v0.6.4
- Fix `vi-forward-char` triggering a bell when using it to accept a suggestion (#488)
- New configuration option to skip completion suggestions when buffer matches a pattern (#487)
- New configuration option to ignore history entries matching a pattern (#456)
## v0.6.3
- Fixed bug moving cursor to end of buffer after accepting suggestion (#453)
## v0.6.2
- Fixed bug deleting the last character in the buffer in vi mode (#450)
- Degrade gracefully when user doesn't have `zsh/system` module installed (#447)
## v0.6.1
- Fixed bug occurring when `_complete` had been aliased (#443)
## v0.6.0
- Added `completion` suggestion strategy powered by completion system (#111)
- Allow setting `ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE` to an empty string (#422)
- Don't fetch suggestions after copy-earlier-word (#439)
- Allow users to unignore zle-\* widgets (e.g. zle-line-init) (#432)
## v0.5.2
- Allow disabling automatic widget re-binding for better performance (#418)
- Fix async suggestions when `SH_WORD_SPLIT` is set
- Refactor async mode to use process substitution instead of zpty (#417)
## v0.5.1
- Speed up widget rebinding (#413)
- Clean up global variable creations (#403)
- Respect user's set options when running original widget (#402)
## v0.5.0
- Don't overwrite config with default values (#335)
- Support fallback strategies by supplying array to suggestion config var
- Rename "default" suggestion strategy to "history" to name it based on what it actually does
- Reset opts in some functions affected by `GLOB_SUBST` (#334)
- Support widgets starting with dashes (ex: `-a-widget`) (#337)
- Skip async tests in zsh versions less than 5.0.8 because of reliability issues
- Fix handling of newline + carriage return in async pty (#333)
## v0.4.3
- Avoid bell when accepting suggestions with `autosuggest-accept` (#228)
- Don't fetch suggestions after [up,down]-line-or-beginning-search (#227, #241)
- We are now running CI against new 5.5.1 version
- Fix partial-accept in vi mode (#188)
- Fix suggestion disappearing on fast movement after switching to `vicmd` mode (#290)
- Fix issue rotating through kill ring with `yank-pop` (#301)
- Fix issue creating new pty for async mode when previous pty is not properly cleaned up (#249)
## v0.4.2
- Fix bug in zsh versions older than 5.0.8 (#296)
- Officially support back to zsh v4.3.11
## v0.4.1
- Switch to [[ and (( conditionals instead of [ (#257)
- Avoid warnnestedvar warnings with `typeset -g` (#275)
- Replace tabs with spaces in yaml (#268)
- Clean up and fix escaping of special characters (#267)
- Add `emacs-forward-word` to default list of partial accept widgets (#246)
## v0.4.0
- High-level integration tests using RSpec and tmux
- Add continuous integration with Circle CI
- Experimental support for asynchronous suggestions (#170)
- Fix problems with multi-line suggestions (#225)
- Optimize case where manually typing in suggestion
- Avoid wrapping any zle-\* widgets (#206)
- Remove support for deprecated options from v0.0.x
- Handle history entries that begin with dashes
- Gracefully handle being sourced multiple times (#126)
- Add enable/disable/toggle widgets to disable/enable suggestions (#219)
## v0.3.3
- Switch from $history array to fc builtin for better performance with large HISTFILEs (#164)
- Fix tilde handling when extended_glob is set (#168)
- Add config option for maximum buffer length to fetch suggestions for (#178)
- Add config option for list of widgets to ignore (#184)
- Don't fetch a new suggestion unless a modification widget actually modifies the buffer (#183)
## v0.3.2
- Test runner now supports running specific tests and choosing zsh binary
- Return code from original widget is now correctly passed through (#135)
- Add `vi-add-eol` to list of accept widgets (#143)
- Escapes widget names within evals to fix problems with irregular widget names (#152)
- Plugin now clears suggestion while within a completion menu (#149)
- .plugin file no longer relies on symbolic link support, fixing issues on Windows (#156)
## v0.3.1
- Fixes issue with `vi-next-char` not accepting suggestion (#137).
- Fixes global variable warning when WARN_CREATE_GLOBAL option enabled (#133).
- Split out a separate test file for each widget.
## v0.3.0
- Adds `autosuggest-execute` widget (PR #124).
- Adds concept of suggestion "strategies" for different ways of fetching suggestions.
- Adds "match_prev_cmd" strategy (PR #131).
- Uses git submodules for testing dependencies.
- Lots of test cleanup.
- Various bug fixes for zsh 5.0.x and `sh_word_split` option.
## v0.2.17
Start of changelog.

View file

@ -0,0 +1 @@
Fish-like fast/unobtrusive autosuggestions for zsh.

View file

@ -0,0 +1,21 @@
FROM ruby:2.5.3-alpine
ARG TEST_ZSH_VERSION
RUN : "${TEST_ZSH_VERSION:?}"
RUN apk add --no-cache autoconf
RUN apk add --no-cache libtool
RUN apk add --no-cache libcap-dev
RUN apk add --no-cache pcre-dev
RUN apk add --no-cache curl
RUN apk add --no-cache build-base
RUN apk add --no-cache ncurses-dev
RUN apk add --no-cache tmux
WORKDIR /zsh-autosuggestions
ADD install_test_zsh.sh ./
RUN ./install_test_zsh.sh
ADD Gemfile Gemfile.lock ./
RUN bundle install

View file

@ -0,0 +1,5 @@
source 'https://rubygems.org'
gem 'rspec'
gem 'rspec-wait'
gem 'pry-byebug'

View file

@ -0,0 +1,41 @@
GEM
remote: https://rubygems.org/
specs:
byebug (9.0.5)
coderay (1.1.1)
diff-lcs (1.3)
method_source (0.8.2)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
pry-byebug (3.4.0)
byebug (~> 9.0)
pry (~> 0.10)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.4)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
rspec-wait (0.0.9)
rspec (>= 3, < 4)
slop (3.6.0)
PLATFORMS
ruby
DEPENDENCIES
pry-byebug
rspec
rspec-wait
BUNDLED WITH
1.13.6

View file

@ -0,0 +1,82 @@
# Installation
* [Packages](#packages)
* [Antigen](#antigen)
* [Oh My Zsh](#oh-my-zsh)
* [HomeBrew](#homebrew)
* [Manual](#manual-git-clone)
## Packages
| System | Package |
| ------------- | ------------- |
| Alpine Linux | [zsh-autosuggestions](https://pkgs.alpinelinux.org/packages?name=zsh-autosuggestions) |
| Debian / Ubuntu | [zsh-autosuggestions OBS repository](https://software.opensuse.org/download.html?project=shells%3Azsh-users%3Azsh-autosuggestions&package=zsh-autosuggestions) |
| Fedora / CentOS / RHEL / Scientific Linux | [zsh-autosuggestions OBS repository](https://software.opensuse.org/download.html?project=shells%3Azsh-users%3Azsh-autosuggestions&package=zsh-autosuggestions) |
| OpenSUSE / SLE | [zsh-autosuggestions OBS repository](https://software.opensuse.org/download.html?project=shells%3Azsh-users%3Azsh-autosuggestions&package=zsh-autosuggestions) |
| Arch Linux / Manjaro / Antergos / Hyperbola | [zsh-autosuggestions](https://www.archlinux.org/packages/zsh-autosuggestions), [zsh-autosuggestions-git](https://aur.archlinux.org/packages/zsh-autosuggestions-git) |
| NixOS | [zsh-autosuggestions](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/zs/zsh-autosuggestions/package.nix) |
| Void Linux | [zsh-autosuggestions](https://github.com/void-linux/void-packages/blob/master/srcpkgs/zsh-autosuggestions/template) |
| Mac OS | [homebrew](https://formulae.brew.sh/formula/zsh-autosuggestions) |
| NetBSD | [pkgsrc](http://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/shells/zsh-autosuggestions/README.html) |
| FreeBSD | [pkg](https://cgit.freebsd.org/ports/tree/shells/zsh-autosuggestions) |
## Antigen
1. Add the following to your `.zshrc`:
```sh
antigen bundle zsh-users/zsh-autosuggestions
```
2. Start a new terminal session.
## Oh My Zsh
1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`)
```sh
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
```
2. Add the plugin to the list of plugins for Oh My Zsh to load (inside `~/.zshrc`):
```sh
plugins=(
# other plugins...
zsh-autosuggestions
)
```
3. Start a new terminal session.
## Homebrew
1. Install command:
```sh
brew install zsh-autosuggestions
```
2. To activate the autosuggestions, add the following at the end of your .zshrc:
```sh
source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh
```
3. Start a new terminal session.
## Manual (Git Clone)
1. Clone this repository somewhere on your machine. This guide will assume `~/.zsh/zsh-autosuggestions`.
```sh
git clone https://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestions
```
2. Add the following to your `.zshrc`:
```sh
source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh
```
3. Start a new terminal session.

View file

@ -0,0 +1,23 @@
Copyright (c) 2013 Thiago de Arruda
Copyright (c) 2016-2021 Eric Freese
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,35 @@
SRC_DIR := ./src
SRC_FILES := \
$(SRC_DIR)/config.zsh \
$(SRC_DIR)/util.zsh \
$(SRC_DIR)/bind.zsh \
$(SRC_DIR)/highlight.zsh \
$(SRC_DIR)/widgets.zsh \
$(SRC_DIR)/strategies/*.zsh \
$(SRC_DIR)/fetch.zsh \
$(SRC_DIR)/async.zsh \
$(SRC_DIR)/start.zsh
HEADER_FILES := \
DESCRIPTION \
URL \
VERSION \
LICENSE
PLUGIN_TARGET := zsh-autosuggestions.zsh
all: $(PLUGIN_TARGET)
$(PLUGIN_TARGET): $(HEADER_FILES) $(SRC_FILES)
cat $(HEADER_FILES) | sed -e 's/^/# /g' > $@
cat $(SRC_FILES) >> $@
.PHONY: clean
clean:
rm $(PLUGIN_TARGET)
.PHONY: test
test: all
@test -n "$$TEST_ZSH_BIN" && echo "Testing zsh binary: $(TEST_ZSH_BIN)" || true
bundle exec rspec $(TESTS)

View file

@ -0,0 +1,188 @@
# zsh-autosuggestions
_[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._
It suggests commands as you type based on history and completions.
Requirements: Zsh v4.3.11 or later
[![Chat on Gitter](https://img.shields.io/gitter/room/zsh-users/zsh-autosuggestions.svg)](https://gitter.im/zsh-users/zsh-autosuggestions)
<a href="https://asciinema.org/a/37390" target="_blank"><img src="https://asciinema.org/a/37390.png" width="400" /></a>
## Installation
See [INSTALL.md](INSTALL.md).
## Usage
As you type commands, you will see a completion offered after the cursor in a muted gray color. This color can be changed by setting the `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` variable. See [configuration](#configuration).
If you press the <kbd></kbd> key (`forward-char` widget) or <kbd>End</kbd> (`end-of-line` widget) with the cursor at the end of the buffer, it will accept the suggestion, replacing the contents of the command line buffer with the suggestion.
If you invoke the `forward-word` widget, it will partially accept the suggestion up to the point that the cursor moves to.
## Configuration
You may want to override the default global config variables. Default values of these variables can be found [here](src/config.zsh).
**Note:** If you are using Oh My Zsh, you can put this configuration in a file in the `$ZSH_CUSTOM` directory. See their comments on [overriding internals](https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals).
### Suggestion Highlight Style
Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion is shown with. The default is `fg=8`, which will set the foreground color to color 8 from the [256-color palette](https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg). If your terminal only supports 8 colors, you will need to use a number between 0 and 7.
Background color can also be set, and the suggestion can be styled bold, underlined, or standout. For example, this would show suggestions with bold, underlined, pink text on a cyan background:
```sh
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#ff00ff,bg=cyan,bold,underline"
```
For more info, read the Character Highlighting section of the zsh manual: `man zshzle` or [online](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting).
**Note:** Some iTerm2 users have reported [not being able to see the suggestions](https://github.com/zsh-users/zsh-autosuggestions/issues/416#issuecomment-486516333). If this affects you, the problem is likely caused by incorrect color settings. In order to correct this, go into iTerm2's setting, navigate to profile > colors and make sure that the colors for Basic Colors > Background and ANSI Colors > Bright Black are **different**.
### Suggestion Strategy
`ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. The strategies in the array are tried successively until a suggestion is found. There are currently three built-in strategies to choose from:
- `history`: Chooses the most recent match from history.
- `completion`: Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` module, which is included with zsh since 4.0.1)
- `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`.
For example, setting `ZSH_AUTOSUGGEST_STRATEGY=(history completion)` will first try to find a suggestion from your history, but, if it can't find a match, will find a suggestion from the completion engine.
### Widget Mapping
This plugin works by triggering custom behavior when certain [zle widgets](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets) are invoked. You can add and remove widgets from these arrays to change the behavior of this plugin:
- `ZSH_AUTOSUGGEST_CLEAR_WIDGETS`: Widgets in this array will clear the suggestion when invoked.
- `ZSH_AUTOSUGGEST_ACCEPT_WIDGETS`: Widgets in this array will accept the suggestion when invoked.
- `ZSH_AUTOSUGGEST_EXECUTE_WIDGETS`: Widgets in this array will execute the suggestion when invoked.
- `ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS`: Widgets in this array will partially accept the suggestion when invoked.
- `ZSH_AUTOSUGGEST_IGNORE_WIDGETS`: Widgets in this array will not trigger any custom behavior.
Widgets that modify the buffer and are not found in any of these arrays will fetch a new suggestion after they are invoked.
**Note:** A widget shouldn't belong to more than one of the above arrays.
### Disabling suggestion for large buffers
Set `ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE` to an integer value to disable autosuggestion for large buffers. The default is unset, which means that autosuggestion will be tried for any buffer size. Recommended value is 20.
This can be useful when pasting large amount of text in the terminal, to avoid triggering autosuggestion for strings that are too long.
### Asynchronous Mode
Suggestions are fetched asynchronously by default in zsh versions 5.0.8 and greater. To disable asynchronous suggestions and fetch them synchronously instead, `unset ZSH_AUTOSUGGEST_USE_ASYNC` after sourcing the plugin.
Alternatively, if you are using a version of zsh older than 5.0.8 and want to enable asynchronous mode, set the `ZSH_AUTOSUGGEST_USE_ASYNC` variable after sourcing the plugin (it can be set to anything). Note that there is [a bug](https://github.com/zsh-users/zsh-autosuggestions/issues/364#issuecomment-481423232) in versions of zsh older than 5.0.8 where <kbd>ctrl</kbd> + <kbd>c</kbd> will fail to reset the prompt immediately after fetching a suggestion asynchronously.
### Disabling automatic widget re-binding
Set `ZSH_AUTOSUGGEST_MANUAL_REBIND` (it can be set to anything) to disable automatic widget re-binding on each precmd. This can be a big boost to performance, but you'll need to handle re-binding yourself if any of the widget lists change or if you or another plugin wrap any of the autosuggest widgets. To re-bind widgets, run `_zsh_autosuggest_bind_widgets`.
### Ignoring history suggestions that match a pattern
Set `ZSH_AUTOSUGGEST_HISTORY_IGNORE` to a [glob pattern](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Operators) to prevent offering suggestions for history entries that match the pattern. For example, set it to `"cd *"` to never suggest any `cd` commands from history. Or set to `"?(#c50,)"` to never suggest anything 50 characters or longer.
**Note:** This only affects the `history` and `match_prev_cmd` suggestion strategies.
### Skipping completion suggestions for certain cases
Set `ZSH_AUTOSUGGEST_COMPLETION_IGNORE` to a [glob pattern](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Operators) to prevent offering completion suggestions when the buffer matches that pattern. For example, set it to `"git *"` to disable completion suggestions for git subcommands.
**Note:** This only affects the `completion` suggestion strategy.
### Key Bindings
This plugin provides a few widgets that you can use with `bindkey`:
1. `autosuggest-accept`: Accepts the current suggestion.
2. `autosuggest-execute`: Accepts and executes the current suggestion.
3. `autosuggest-clear`: Clears the current suggestion.
4. `autosuggest-fetch`: Fetches a suggestion (works even when suggestions are disabled).
5. `autosuggest-disable`: Disables suggestions.
6. `autosuggest-enable`: Re-enables suggestions.
7. `autosuggest-toggle`: Toggles between enabled/disabled suggestions.
For example, this would bind <kbd>ctrl</kbd> + <kbd>space</kbd> to accept the current suggestion.
```sh
bindkey '^ ' autosuggest-accept
```
## Troubleshooting
If you have a problem, please search through [the list of issues on GitHub](https://github.com/zsh-users/zsh-autosuggestions/issues?q=) to see if someone else has already reported it.
### Reporting an Issue
Before reporting an issue, please try temporarily disabling sections of your configuration and other plugins that may be conflicting with this plugin to isolate the problem.
When reporting an issue, please include:
- The smallest, simplest `.zshrc` configuration that will reproduce the problem. See [this comment](https://github.com/zsh-users/zsh-autosuggestions/issues/102#issuecomment-180944764) for a good example of what this means.
- The version of zsh you're using (`zsh --version`)
- Which operating system you're running
## Uninstallation
1. Remove the code referencing this plugin from `~/.zshrc`.
2. Remove the git repository from your hard drive
```sh
rm -rf ~/.zsh/zsh-autosuggestions # Or wherever you installed
```
## Development
### Build Process
Edit the source files in `src/`. Run `make` to build `zsh-autosuggestions.zsh` from those source files.
### Pull Requests
Pull requests are welcome! If you send a pull request, please:
- Request to merge into the `develop` branch (*NOT* `master`)
- Match the existing coding conventions.
- Include helpful comments to keep the barrier-to-entry low for people new to the project.
- Write tests that cover your code as much as possible.
### Testing
Tests are written in ruby using the [`rspec`](http://rspec.info/) framework. They use [`tmux`](https://tmux.github.io/) to drive a pseudoterminal, sending simulated keystrokes and making assertions on the terminal content.
Test files live in `spec/`. To run the tests, run `make test`. To run a specific test, run `TESTS=spec/some_spec.rb make test`. You can also specify a `zsh` binary to use by setting the `TEST_ZSH_BIN` environment variable (ex: `TEST_ZSH_BIN=/bin/zsh make test`).
It's possible to run the tests for any supported version of zsh in a Docker image by building an image from the provided Dockerfile. To build the docker image for a specific version of zsh (where `<version>` below is substituted with the contents of a line from the [`ZSH_VERSIONS`](ZSH_VERSIONS) file), run:
```sh
docker build --build-arg TEST_ZSH_VERSION=<version> -t zsh-autosuggestions-test .
```
After building the image, run the tests via:
```sh
docker run -it -v $PWD:/zsh-autosuggestions zsh-autosuggestions-test make test
```
## License
This project is licensed under [MIT license](http://opensource.org/licenses/MIT).
For the full text of the license, see the [LICENSE](LICENSE) file.

View file

@ -0,0 +1 @@
https://github.com/zsh-users/zsh-autosuggestions

View file

@ -0,0 +1 @@
v0.7.1

View file

@ -0,0 +1,14 @@
# Zsh releases to run tests against
# See https://github.com/zsh-users/zsh/releases
4.3.11
5.0.2
5.0.8
5.1.1
5.2
5.3.1
5.4.2
5.5.1
5.6.2
5.7.1
5.8.1
5.9

View file

@ -0,0 +1,23 @@
#!/bin/sh
set -ex
mkdir zsh-build
cd zsh-build
curl -L https://api.github.com/repos/zsh-users/zsh/tarball/zsh-$TEST_ZSH_VERSION | tar xz --strip=1
./Util/preconfig
./configure --enable-pcre \
--enable-cap \
--enable-multibyte \
--with-term-lib='ncursesw tinfo' \
--with-tcsetpgrp
make install.bin
make install.modules
make install.fns
cd ..
rm -rf zsh-build

View file

@ -0,0 +1,70 @@
context 'with asynchronous suggestions enabled' do
let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] }
describe '`up-line-or-beginning-search`' do
let(:before_sourcing) do
-> do
session.
run_command('autoload -U up-line-or-beginning-search').
run_command('zle -N up-line-or-beginning-search').
send_string('bindkey "').
send_keys('C-v').send_keys('up').
send_string('" up-line-or-beginning-search').
send_keys('enter')
end
end
it 'should show previous history entries' do
with_history(
'echo foo',
'echo bar',
'echo baz'
) do
session.clear_screen
3.times { session.send_keys('up') }
wait_for { session.content }.to eq("echo foo")
end
end
end
describe '`copy-earlier-word`' do
let(:before_sourcing) do
-> do
session.
run_command('autoload -Uz copy-earlier-word').
run_command('zle -N copy-earlier-word').
run_command('bindkey "^N" copy-earlier-word')
end
end
it 'should cycle through previous words in the buffer' do
session.clear_screen
session.send_string('foo bar baz')
sleep 0.5
session.send_keys('C-n')
wait_for { session.content }.to eq('foo bar bazbaz')
session.send_keys('C-n')
wait_for { session.content }.to eq('foo bar bazbar')
session.send_keys('C-n')
wait_for { session.content }.to eq('foo bar bazfoo')
end
end
describe 'pressing ^C after fetching a suggestion' do
before do
skip 'Workaround does not work below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
end
it 'terminates the prompt and begins a new one' do
session.send_keys('e')
sleep 0.5
session.send_keys('C-c')
sleep 0.5
session.send_keys('echo')
wait_for { session.content }.to eq("e\necho")
end
end
end

View file

@ -0,0 +1,14 @@
describe 'with `AUTO_CD` option set' do
let(:after_sourcing) do
-> {
session.run_command('setopt AUTO_CD')
session.run_command('autoload compinit && compinit')
}
end
it 'directory names are still completed' do
session.send_string('sr')
session.send_keys('C-i')
wait_for { session.content }.to eq('src/')
end
end

View file

@ -0,0 +1,43 @@
describe 'pasting using bracketed-paste-magic' do
let(:before_sourcing) do
-> do
session.
run_command('autoload -Uz bracketed-paste-magic').
run_command('zle -N bracketed-paste bracketed-paste-magic')
end
end
context 'with suggestions disabled while pasting' do
before do
session.
run_command('bpm_init() { zle autosuggest-disable }').
run_command('bpm_finish() { zle autosuggest-enable }').
run_command('zstyle :bracketed-paste-magic paste-init bpm_init').
run_command('zstyle :bracketed-paste-magic paste-finish bpm_finish')
end
it 'does not show an incorrect suggestion' do
with_history('echo hello') do
session.paste_string("echo #{'a' * 60}")
sleep 1
expect(session.content).to eq("echo #{'a' * 60}")
end
end
end
context 'with `bracketed-paste` added to the list of widgets that clear the suggestion' do
let(:options) { ['ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(bracketed-paste)'] }
it 'does not retain an old suggestion' do
with_history ('echo foo') do
session.send_string('echo ')
wait_for { session.content }.to eq('echo foo')
session.paste_string('bar')
wait_for { session.content }.to eq('echo bar')
session.send_keys('C-a') # Any cursor movement works
sleep 1
expect(session.content).to eq('echo bar')
end
end
end
end

View file

@ -0,0 +1,14 @@
describe 'a running zpty command' do
let(:before_sourcing) { -> { session.run_command('zmodload zsh/zpty && zpty -b kitty cat') } }
context 'when using `completion` strategy' do
let(:options) { ["ZSH_AUTOSUGGEST_STRATEGY=completion"] }
it 'is not affected' do
session.send_keys('a').send_keys('C-h')
session.run_command('zpty -t kitty; echo $?')
wait_for { session.content }.to end_with("\n0")
end
end
end

View file

@ -0,0 +1,12 @@
describe 'with `GLOB_SUBST` option set' do
let(:after_sourcing) do
-> {
session.run_command('setopt GLOB_SUBST')
}
end
it 'error messages are not printed' do
session.send_string('[[')
wait_for { session.content }.to eq('[[')
end
end

View file

@ -0,0 +1,13 @@
describe 'rebinding [' do
context 'initialized before sourcing the plugin' do
before do
session.run_command("function [ { $commands[\\[] \"$@\" }")
session.clear_screen
end
it 'executes the custom behavior and the built-in behavior' do
session.send_string('asdf')
wait_for { session.content }.to eq('asdf')
end
end
end

View file

@ -0,0 +1,80 @@
describe 'when using vi mode' do
let(:before_sourcing) do
-> do
session.run_command('bindkey -v')
end
end
describe 'moving the cursor after exiting insert mode' do
it 'should not clear the current suggestion' do
with_history('foobar foo') do
session.
send_string('foo').
send_keys('escape').
send_keys('h')
wait_for { session.content }.to eq('foobar foo')
end
end
end
describe '`vi-forward-word-end`' do
it 'should accept through the end of the current word' do
with_history('foobar foo') do
session.
send_string('foo').
send_keys('escape').
send_keys('e'). # vi-forward-word-end
send_keys('a'). # vi-add-next
send_string('baz')
wait_for { session.content }.to eq('foobarbaz')
end
end
end
describe '`vi-forward-word`' do
it 'should accept through the first character of the next word' do
with_history('foobar foo') do
session.
send_string('foo').
send_keys('escape').
send_keys('w'). # vi-forward-word
send_keys('a'). # vi-add-next
send_string('az')
wait_for { session.content }.to eq('foobar faz')
end
end
end
describe '`vi-find-next-char`' do
it 'should accept through the next occurrence of the character' do
with_history('foobar foo') do
session.
send_string('foo').
send_keys('escape').
send_keys('f'). # vi-find-next-char
send_keys('o').
send_keys('a'). # vi-add-next
send_string('b')
wait_for { session.content }.to eq('foobar fob')
end
end
end
describe '`vi-delete`' do
it 'should be able to remove the last character in the buffer' do
skip 'deleting last char did not work below zsh version 5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
session.
send_string('echo foo').
send_keys('escape').
send_keys('d').
send_keys('l')
wait_for { session.content }.to eq('echo fo')
end
end
end

View file

@ -0,0 +1,39 @@
describe 'a wrapped widget' do
let(:widget) { 'backward-delete-char' }
context 'initialized before sourcing the plugin' do
let(:before_sourcing) do
-> do
session.
run_command("_orig_#{widget}() { zle .#{widget} }").
run_command("zle -N orig-#{widget} _orig_#{widget}").
run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }").
run_command("zle -N #{widget} #{widget}-magic")
end
end
it 'executes the custom behavior and the built-in behavior' do
with_history('foobar', 'foodar') do
session.send_string('food').send_keys('C-h')
wait_for { session.content }.to eq('foobar')
end
end
end
context 'initialized after sourcing the plugin' do
before do
session.
run_command("zle -N orig-#{widget} ${widgets[#{widget}]#*:}").
run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }").
run_command("zle -N #{widget} #{widget}-magic").
clear_screen
end
it 'executes the custom behavior and the built-in behavior' do
with_history('foobar', 'foodar') do
session.send_string('food').send_keys('C-h')
wait_for { session.content }.to eq('foobar')
end
end
end
end

View file

@ -0,0 +1,24 @@
describe 'using `zle -U`' do
let(:before_sourcing) do
-> do
session.
run_command('_zsh_autosuggest_strategy_test() { sleep 1; _zsh_autosuggest_strategy_history "$1" }').
run_command('foo() { zle -U - "echo hello" }; zle -N foo; bindkey ^B foo')
end
end
let(:options) { ['unset ZSH_AUTOSUGGEST_USE_ASYNC', 'ZSH_AUTOSUGGEST_STRATEGY=test'] }
# TODO: This is only possible with the $KEYS_QUEUED_COUNT widget parameter, coming soon...
xit 'does not fetch a suggestion for every inserted character' do
session.send_keys('C-b')
wait_for { session.content }.to eq('echo hello')
end
it 'shows a suggestion when the widget completes' do
with_history('echo hello world') do
session.send_keys('C-b')
wait_for { session.content(esc_seqs: true) }.to match(/\Aecho hello\e\[[0-9]+m world/)
end
end
end

View file

@ -0,0 +1,23 @@
context 'with some items in the kill ring' do
before do
session.
send_string('echo foo').
send_keys('C-u').
send_string('echo bar').
send_keys('C-u')
end
describe '`yank-pop`' do
it 'should cycle through all items in the kill ring' do
session.send_keys('C-y')
wait_for { session.content }.to eq('echo bar')
session.send_keys('escape').send_keys('y')
wait_for { session.content }.to eq('echo foo')
session.send_keys('escape').send_keys('y')
wait_for { session.content }.to eq('echo bar')
end
end
end

View file

@ -0,0 +1,17 @@
context 'with zle-line-init unignored' do
let(:after_sourcing) do
-> do
session.
run_command('setopt extendedglob').
run_command('ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(${(@)ZSH_AUTOSUGGEST_IGNORE_WIDGETS:#zle-\*} zle-\^line-init)').
run_command('zle-line-init() { BUFFER="echo" }')
end
end
it 'should fetch a suggestion on each line initialization' do
with_history('echo foo') do
session.run_command('zle -N zle-line-init')
wait_for { session.content }.to end_with('echo foo')
end
end
end

View file

@ -0,0 +1,8 @@
describe 'a multi-line suggestion' do
it 'should be displayed on multiple lines' do
with_history("echo \"\n\"") do
session.send_keys('e')
wait_for { session.content }.to eq("echo \"\n\"")
end
end
end

View file

@ -0,0 +1,30 @@
describe 'a suggestion' do
let(:term_opts) { { width: 200 } }
let(:long_command) { "echo #{'a' * 100}" }
around do |example|
with_history(long_command) { example.run }
end
it 'is provided for any buffer length' do
session.send_string(long_command[0...-1])
wait_for { session.content }.to eq(long_command)
end
context 'when ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE is specified' do
let(:buffer_max_size) { 10 }
let(:options) { ["ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=#{buffer_max_size}"] }
it 'is provided when the buffer is shorter than the specified length' do
session.send_string(long_command[0...(buffer_max_size - 1)])
wait_for { session.content }.to eq(long_command)
end
it 'is provided when the buffer is equal to the specified length' do
session.send_string(long_command[0...(buffer_max_size)])
wait_for { session.content }.to eq(long_command)
end
it 'is not provided when the buffer is longer than the specified length'
end
end

View file

@ -0,0 +1,7 @@
describe 'a displayed suggestion' do
it 'is shown in the default style'
describe 'when ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE is set to a zle_highlight string' do
it 'is shown in the specified style'
end
end

View file

@ -0,0 +1,7 @@
describe 'an original zle widget' do
context 'is accessible with the default prefix'
context 'when ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX is set' do
it 'is accessible with the specified prefix'
end
end

View file

@ -0,0 +1,55 @@
describe 'a suggestion for a given prefix' do
let(:history_strategy) { '_zsh_autosuggest_strategy_history() { suggestion="history" }' }
let(:foobar_strategy) { '_zsh_autosuggest_strategy_foobar() { [[ "foobar baz" = $1* ]] && suggestion="foobar baz" }' }
let(:foobaz_strategy) { '_zsh_autosuggest_strategy_foobaz() { [[ "foobaz bar" = $1* ]] && suggestion="foobaz bar" }' }
let(:after_sourcing) do
-> do
session.run_command(history_strategy)
end
end
it 'by default is determined by calling the `history` strategy function' do
session.send_string('h')
wait_for { session.content }.to eq('history')
end
context 'when ZSH_AUTOSUGGEST_STRATEGY is set to an array' do
let(:after_sourcing) do
-> do
session.
run_command(foobar_strategy).
run_command(foobaz_strategy).
run_command('ZSH_AUTOSUGGEST_STRATEGY=(foobar foobaz)')
end
end
it 'is determined by the first strategy function to return a suggestion' do
session.send_string('foo')
wait_for { session.content }.to eq('foobar baz')
session.send_string('baz')
wait_for { session.content }.to eq('foobaz bar')
end
end
context 'when ZSH_AUTOSUGGEST_STRATEGY is set to a string' do
let(:after_sourcing) do
-> do
session.
run_command(foobar_strategy).
run_command(foobaz_strategy).
run_command('ZSH_AUTOSUGGEST_STRATEGY="foobar foobaz"')
end
end
it 'is determined by the first strategy function to return a suggestion' do
session.send_string('foo')
wait_for { session.content }.to eq('foobar baz')
session.send_string('baz')
wait_for { session.content }.to eq('foobaz bar')
end
end
end

View file

@ -0,0 +1,121 @@
describe 'a zle widget' do
let(:widget) { 'my-widget' }
let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } }
context 'when added to ZSH_AUTOSUGGEST_ACCEPT_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})"] }
it 'accepts the suggestion and moves the cursor to the end of the buffer when invoked' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content(esc_seqs: true) }.to eq('echo hello')
wait_for { session.cursor }.to eq([10, 0])
end
end
end
context 'when added to ZSH_AUTOSUGGEST_CLEAR_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(#{widget})"] }
it 'clears the suggestion when invoked' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content }.to eq('e')
end
end
end
context 'when added to ZSH_AUTOSUGGEST_EXECUTE_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_EXECUTE_WIDGETS+=(#{widget})"] }
it 'executes the suggestion when invoked' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content }.to end_with("\nhello")
end
end
end
context 'when added to ZSH_AUTOSUGGEST_IGNORE_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(#{widget})"] }
it 'should not be wrapped with an autosuggest widget' do
session.run_command("echo $widgets[#{widget}]")
wait_for { session.content }.to end_with("\nuser:#{widget}")
end
end
context 'that moves the cursor forward' do
before { session.run_command("#{widget}() { zle forward-char }") }
context 'when added to ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(#{widget})"] }
it 'accepts the suggestion as far as the cursor is moved when invoked' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to start_with('echo hello')
session.send_keys('C-b')
wait_for { session.content(esc_seqs: true) }.to match(/\Aec\e\[[0-9]+mho hello/)
end
end
end
end
context 'that modifies the buffer' do
before { session.run_command("#{widget}() { BUFFER=\"foo\" }") }
context 'when not added to any of the widget lists' do
it 'modifies the buffer and fetches a new suggestion' do
with_history('foobar') do
session.send_keys('C-b')
wait_for { session.content }.to eq('foobar')
end
end
end
end
end
describe 'a modification to the widget lists' do
let(:widget) { 'my-widget' }
let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } }
before { session.run_command("ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})") }
it 'takes effect on the next cmd line' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content(esc_seqs: true) }.to eq('echo hello')
end
end
context 'when manual rebind is enabled' do
let(:options) { ["ZSH_AUTOSUGGEST_MANUAL_REBIND=true"] }
it 'does not take effect until bind command is re-run' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
sleep 1
expect(session.content(esc_seqs: true)).not_to eq('echo hello')
session.send_keys('C-c')
session.run_command('_zsh_autosuggest_bind_widgets').clear_screen
wait_for { session.content }.to eq('')
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content(esc_seqs: true) }.to eq('echo hello')
end
end
end
end

View file

@ -0,0 +1,54 @@
require 'pry'
require 'rspec/wait'
require 'terminal_session'
require 'tempfile'
RSpec.shared_context 'terminal session' do
let(:term_opts) { {} }
let(:session) { TerminalSession.new(term_opts) }
let(:before_sourcing) { -> {} }
let(:after_sourcing) { -> {} }
let(:options) { [] }
around do |example|
before_sourcing.call
session.run_command(['source zsh-autosuggestions.zsh', *options].join('; '))
after_sourcing.call
session.clear_screen
example.run
session.destroy
end
def with_history(*commands, &block)
Tempfile.create do |f|
f.write(commands.map{|c| c.gsub("\n", "\\\n")}.join("\n"))
f.flush
session.run_command('fc -p')
session.run_command("fc -R #{f.path}")
session.clear_screen
yield block
session.send_keys('C-c')
session.run_command('fc -P')
end
end
end
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.wait_timeout = 2
config.include_context 'terminal session'
end

View file

@ -0,0 +1,72 @@
describe 'the `completion` suggestion strategy' do
let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=completion'] }
let(:before_sourcing) do
-> do
session.
run_command('autoload compinit && compinit').
run_command('_foo() { compadd bar; compadd bat }').
run_command('_num() { compadd two; compadd three }').
run_command('compdef _foo baz').
run_command('compdef _num one')
end
end
it 'suggests the first completion result' do
session.send_string('baz ')
wait_for { session.content }.to eq('baz bar')
end
it 'does not add extra carriage returns when prefix has a line feed' do
skip '`stty` does not work inside zpty below zsh version 5.0.3' if session.zsh_version < Gem::Version.new('5.0.3')
session.send_string('baz \\').send_keys('C-v', 'C-j')
wait_for { session.content }.to eq("baz \\\nbar")
end
context 'when `_complete` is aliased' do
let(:before_sourcing) do
-> do
session.
run_command('autoload compinit && compinit').
run_command('_foo() { compadd bar; compadd bat }').
run_command('compdef _foo baz').
run_command('alias _complete=_complete')
end
end
it 'suggests the first completion result' do
session.send_string('baz ')
wait_for { session.content }.to eq('baz bar')
end
end
context 'when ZSH_AUTOSUGGEST_COMPLETION_IGNORE is set to a pattern' do
let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=completion', 'ZSH_AUTOSUGGEST_COMPLETION_IGNORE="one *"'] }
it 'makes suggestions when the buffer does not match the pattern' do
session.send_string('baz ')
wait_for { session.content }.to eq('baz bar')
end
it 'does not make suggestions when the buffer matches the pattern' do
session.send_string('one t')
sleep 1
expect(session.content).to eq('one t')
end
end
context 'when async mode is enabled' do
let(:options) { ['ZSH_AUTOSUGGEST_USE_ASYNC=true', 'ZSH_AUTOSUGGEST_STRATEGY=completion'] }
it 'suggests the first completion result' do
session.send_string('baz ')
wait_for { session.content }.to eq('baz bar')
end
it 'does not add extra carriage returns when prefix has a line feed' do
skip '`stty` does not work inside zpty below zsh version 5.0.3' if session.zsh_version < Gem::Version.new('5.0.3')
session.send_string('baz \\').send_keys('C-v', 'C-j')
wait_for { session.content }.to eq("baz \\\nbar")
end
end
end

View file

@ -0,0 +1,23 @@
require 'strategies/special_characters_helper'
describe 'the `history` suggestion strategy' do
it 'suggests the last matching history entry' do
with_history('ls foo', 'ls bar', 'echo baz') do
session.send_string('ls')
wait_for { session.content }.to eq('ls bar')
end
end
context 'when ZSH_AUTOSUGGEST_HISTORY_IGNORE is set to a pattern' do
let(:options) { ['ZSH_AUTOSUGGEST_HISTORY_IGNORE="* bar"'] }
it 'does not make suggestions that match the pattern' do
with_history('ls foo', 'ls bar', 'echo baz') do
session.send_string('ls')
wait_for { session.content }.to eq('ls foo')
end
end
end
include_examples 'special characters'
end

View file

@ -0,0 +1,34 @@
require 'strategies/special_characters_helper'
describe 'the `match_prev_cmd` strategy' do
let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd'] }
let(:history) { [
'echo what',
'ls foo',
'echo what',
'ls bar',
'ls baz',
'echo what'
] }
it 'suggests the last matching history entry after the previous command' do
with_history(*history) do
session.send_string('ls')
wait_for { session.content }.to eq('ls bar')
end
end
context 'when ZSH_AUTOSUGGEST_HISTORY_IGNORE is set to a pattern' do
let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd', 'ZSH_AUTOSUGGEST_HISTORY_IGNORE="* bar"'] }
it 'does not make suggestions that match the pattern' do
with_history(*history) do
session.send_string('ls')
wait_for { session.content }.to eq('ls foo')
end
end
end
include_examples 'special characters'
end

View file

@ -0,0 +1,75 @@
shared_examples 'special characters' do
describe 'a special character in the buffer should be treated like any other character' do
it 'asterisk' do
with_history('echo "hello*"', 'echo "hello."') do
session.send_string('echo "hello*')
wait_for { session.content }.to eq('echo "hello*"')
end
end
it 'question mark' do
with_history('echo "hello?"', 'echo "hello."') do
session.send_string('echo "hello?')
wait_for { session.content }.to eq('echo "hello?"')
end
end
it 'backslash' do
with_history('echo "hello\nworld"') do
session.send_string('echo "hello\\')
wait_for { session.content }.to eq('echo "hello\nworld"')
end
end
it 'double backslash' do
with_history('echo "\\\\"') do
session.send_string('echo "\\\\')
wait_for { session.content }.to eq('echo "\\\\"')
end
end
it 'tilde' do
with_history('echo ~/foo') do
session.send_string('echo ~')
wait_for { session.content }.to eq('echo ~/foo')
end
end
it 'parentheses' do
with_history('echo "$(ls foo)"') do
session.send_string('echo "$(')
wait_for { session.content }.to eq('echo "$(ls foo)"')
end
end
it 'square bracket' do
with_history('echo "$history[123]"') do
session.send_string('echo "$history[')
wait_for { session.content }.to eq('echo "$history[123]"')
session.send_string('123]')
wait_for { session.content }.to eq('echo "$history[123]"')
end
end
it 'octothorpe' do
with_history('echo "#yolo"') do
session.send_string('echo "#')
wait_for { session.content }.to eq('echo "#yolo"')
end
end
it 'caret' do
with_history('echo "^A"', 'echo "^B"') do
session.send_string('echo "^A')
wait_for { session.content }.to eq('echo "^A"')
end
end
it 'dash' do
with_history('-foo() {}') do
session.send_string('-')
wait_for { session.content }.to eq('-foo() {}')
end
end
end
end

View file

@ -0,0 +1,99 @@
require 'securerandom'
class TerminalSession
ZSH_BIN = ENV['TEST_ZSH_BIN'] || 'zsh'
def initialize(opts = {})
opts = {
width: 80,
height: 24,
prompt: '',
term: 'xterm-256color',
zsh_bin: ZSH_BIN
}.merge(opts)
@opts = opts
cmd="PS1=\"#{opts[:prompt]}\" TERM=#{opts[:term]} #{ZSH_BIN} -f"
tmux_command("new-session -d -x #{opts[:width]} -y #{opts[:height]} '#{cmd}'")
end
def zsh_version
@zsh_version ||= Gem::Version.new(`#{ZSH_BIN} -c 'echo -n $ZSH_VERSION'`)
end
def tmux_socket_name
@tmux_socket_name ||= SecureRandom.hex(6)
end
def run_command(command)
send_string(command)
send_keys('enter')
self
end
def send_string(str)
tmux_command("send-keys -t 0 -l -- '#{str.gsub("'", "\\'")}'")
self
end
def send_keys(*keys)
tmux_command("send-keys -t 0 #{keys.join(' ')}")
self
end
def paste_string(str)
tmux_command("set-buffer -- '#{str}'")
tmux_command("paste-buffer -dpr -t 0")
self
end
def content(esc_seqs: false)
cmd = 'capture-pane -p -t 0'
cmd += ' -e' if esc_seqs
tmux_command(cmd).strip
end
def clear_screen
send_keys('C-l')
i = 0
until content == opts[:prompt] || i > 20 do
sleep(0.1)
i = i + 1
end
self
end
def destroy
tmux_command('kill-session')
end
def cursor
tmux_command("display-message -t 0 -p '\#{cursor_x},\#{cursor_y}'").
strip.
split(',').
map(&:to_i)
end
def attach!
tmux_command('attach-session')
end
private
attr_reader :opts
def tmux_command(cmd)
out = `tmux -u -L #{tmux_socket_name} #{cmd}`
raise("tmux error running: '#{cmd}'") unless $?.success?
out
end
end

View file

@ -0,0 +1,19 @@
describe 'the `autosuggest-disable` widget' do
before do
session.run_command('bindkey ^B autosuggest-disable')
end
it 'disables suggestions and clears the suggestion' do
with_history('echo hello') do
session.send_string('echo')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content }.to eq('echo')
session.send_string(' h')
sleep 1
expect(session.content).to eq('echo h')
end
end
end

View file

@ -0,0 +1,42 @@
describe 'the `autosuggest-enable` widget' do
before do
session.
run_command('typeset -g _ZSH_AUTOSUGGEST_DISABLED').
run_command('bindkey ^B autosuggest-enable')
end
it 'enables suggestions and fetches a suggestion' do
with_history('echo hello') do
session.send_string('e')
sleep 1
expect(session.content).to eq('e')
session.send_keys('C-b')
session.send_string('c')
wait_for { session.content }.to eq('echo hello')
end
end
context 'invoked on an empty buffer' do
it 'does not fetch a suggestion' do
with_history('echo hello') do
session.send_keys('C-b')
sleep 1
expect(session.content).to eq('')
end
end
end
context 'invoked on a non-empty buffer' do
it 'fetches a suggestion' do
with_history('echo hello') do
session.send_string('e')
sleep 1
expect(session.content).to eq('e')
session.send_keys('C-b')
wait_for { session.content }.to eq('echo hello')
end
end
end
end

View file

@ -0,0 +1,24 @@
describe 'the `autosuggest-fetch` widget' do
context 'when suggestions are disabled' do
before do
session.
run_command('bindkey ^B autosuggest-disable').
run_command('bindkey ^F autosuggest-fetch').
send_keys('C-b')
end
it 'will fetch and display a suggestion' do
with_history('echo hello') do
session.send_string('echo h')
sleep 1
expect(session.content).to eq('echo h')
session.send_keys('C-f')
wait_for { session.content }.to eq('echo hello')
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
end
end
end
end

View file

@ -0,0 +1,26 @@
describe 'the `autosuggest-toggle` widget' do
before do
session.run_command('bindkey ^B autosuggest-toggle')
end
it 'toggles suggestions' do
with_history('echo world', 'echo hello') do
session.send_string('echo')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content }.to eq('echo')
session.send_string(' h')
sleep 1
expect(session.content).to eq('echo h')
session.send_keys('C-b')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-h')
session.send_string('w')
wait_for { session.content }.to eq('echo world')
end
end
end

View file

@ -0,0 +1,77 @@
#--------------------------------------------------------------------#
# Async #
#--------------------------------------------------------------------#
_zsh_autosuggest_async_request() {
zmodload zsh/system 2>/dev/null # For `$sysparams`
typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
# If we've got a pending request, cancel it
if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
# Close the file descriptor and remove the handler
builtin exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
# We won't know the pid unless the user has zsh/system module installed
if [[ -n "$_ZSH_AUTOSUGGEST_CHILD_PID" ]]; then
# Zsh will make a new process group for the child process only if job
# control is enabled (MONITOR option)
if [[ -o MONITOR ]]; then
# Send the signal to the process group to kill any processes that may
# have been forked by the suggestion strategy
kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
else
# Kill just the child process since it wasn't placed in a new process
# group. If the suggestion strategy forked any child processes they may
# be orphaned and left behind.
kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
fi
fi
fi
# Fork a process to fetch a suggestion and open a pipe to read from it
builtin exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
# Tell parent process our pid
echo $sysparams[pid]
# Fetch and print the suggestion
local suggestion
_zsh_autosuggest_fetch_suggestion "$1"
echo -nE "$suggestion"
)
# There's a weird bug here where ^C stops working unless we force a fork
# See https://github.com/zsh-users/zsh-autosuggestions/issues/364
autoload -Uz is-at-least
is-at-least 5.8 || command true
# Read the pid from the child process
read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD
# When the fd is readable, call the response handler
zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
}
# Called when new data is ready to be read from the pipe
# First arg will be fd ready for reading
# Second arg will be passed in case of error
_zsh_autosuggest_async_response() {
emulate -L zsh
local suggestion
if [[ -z "$2" || "$2" == "hup" ]]; then
# Read everything from the fd and give it as a suggestion
IFS='' read -rd '' -u $1 suggestion
zle autosuggest-suggest -- "$suggestion"
# Close the fd
builtin exec {1}<&-
fi
# Always remove the handler
zle -F "$1"
_ZSH_AUTOSUGGEST_ASYNC_FD=
}

View file

@ -0,0 +1,106 @@
#--------------------------------------------------------------------#
# Widget Helpers #
#--------------------------------------------------------------------#
_zsh_autosuggest_incr_bind_count() {
typeset -gi bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]+1))
_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=$bind_count
}
# Bind a single widget to an autosuggest widget, saving a reference to the original widget
_zsh_autosuggest_bind_widget() {
typeset -gA _ZSH_AUTOSUGGEST_BIND_COUNTS
local widget=$1
local autosuggest_action=$2
local prefix=$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX
local -i bind_count
# Save a reference to the original widget
case $widgets[$widget] in
# Already bound
user:_zsh_autosuggest_(bound|orig)_*)
bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$widget]))
;;
# User-defined widget
user:*)
_zsh_autosuggest_incr_bind_count $widget
zle -N $prefix$bind_count-$widget ${widgets[$widget]#*:}
;;
# Built-in widget
builtin)
_zsh_autosuggest_incr_bind_count $widget
eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }"
zle -N $prefix$bind_count-$widget _zsh_autosuggest_orig_$widget
;;
# Completion widget
completion:*)
_zsh_autosuggest_incr_bind_count $widget
eval "zle -C $prefix$bind_count-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
;;
esac
# Pass the original widget's name explicitly into the autosuggest
# function. Use this passed in widget name to call the original
# widget instead of relying on the $WIDGET variable being set
# correctly. $WIDGET cannot be trusted because other plugins call
# zle without the `-w` flag (e.g. `zle self-insert` instead of
# `zle self-insert -w`).
eval "_zsh_autosuggest_bound_${bind_count}_${(q)widget}() {
_zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@
}"
# Create the bound widget
zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget
}
# Map all configured widgets to the right autosuggest widgets
_zsh_autosuggest_bind_widgets() {
emulate -L zsh
local widget
local ignore_widgets
ignore_widgets=(
.\*
_\*
${_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS/#/autosuggest-}
$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\*
$ZSH_AUTOSUGGEST_IGNORE_WIDGETS
)
# Find every widget we might want to bind and bind it appropriately
for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do
if [[ -n ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget clear
elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget accept
elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget execute
elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget partial_accept
else
# Assume any unspecified widget might modify the buffer
_zsh_autosuggest_bind_widget $widget modify
fi
done
}
# Given the name of an original widget and args, invoke it, if it exists
_zsh_autosuggest_invoke_original_widget() {
# Do nothing unless called with at least one arg
(( $# )) || return 0
local original_widget_name="$1"
shift
if (( ${+widgets[$original_widget_name]} )); then
zle $original_widget_name -- $@
fi
}

View file

@ -0,0 +1,95 @@
#--------------------------------------------------------------------#
# Global Configuration Variables #
#--------------------------------------------------------------------#
# Color to use when highlighting suggestion
# Uses format of `region_highlight`
# More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
(( ! ${+ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE} )) &&
typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
# Prefix to use when saving original versions of bound widgets
(( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) &&
typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
# Strategies to use to fetch a suggestion
# Will try each strategy in order until a suggestion is returned
(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
typeset -ga ZSH_AUTOSUGGEST_STRATEGY
ZSH_AUTOSUGGEST_STRATEGY=(history)
}
# Widgets that clear the suggestion
(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS
ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
history-search-forward
history-search-backward
history-beginning-search-forward
history-beginning-search-backward
history-beginning-search-forward-end
history-beginning-search-backward-end
history-substring-search-up
history-substring-search-down
up-line-or-beginning-search
down-line-or-beginning-search
up-line-or-history
down-line-or-history
accept-line
copy-earlier-word
)
}
# Widgets that accept the entire suggestion
(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
forward-char
end-of-line
vi-forward-char
vi-end-of-line
vi-add-eol
)
}
# Widgets that accept the entire suggestion and execute it
(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS
ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
)
}
# Widgets that accept the suggestion as far as the cursor moves
(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
forward-word
emacs-forward-word
vi-forward-word
vi-forward-word-end
vi-forward-blank-word
vi-forward-blank-word-end
vi-find-next-char
vi-find-next-char-skip
)
}
# Widgets that should be ignored (globbing supported but must be escaped)
(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS
ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
orig-\*
beep
run-help
set-local-history
which-command
yank
yank-pop
zle-\*
)
}
# Pty name for capturing completions for completion suggestion strategy
(( ! ${+ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME} )) &&
typeset -g ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty

View file

@ -0,0 +1,27 @@
#--------------------------------------------------------------------#
# Fetch Suggestion #
#--------------------------------------------------------------------#
# Loops through all specified strategies and returns a suggestion
# from the first strategy to provide one.
#
_zsh_autosuggest_fetch_suggestion() {
typeset -g suggestion
local -a strategies
local strategy
# Ensure we are working with an array
strategies=(${=ZSH_AUTOSUGGEST_STRATEGY})
for strategy in $strategies; do
# Try to get a suggestion from this strategy
_zsh_autosuggest_strategy_$strategy "$1"
# Ensure the suggestion matches the prefix
[[ "$suggestion" != "$1"* ]] && unset suggestion
# Break once we've found a valid suggestion
[[ -n "$suggestion" ]] && break
done
}

View file

@ -0,0 +1,26 @@
#--------------------------------------------------------------------#
# Highlighting #
#--------------------------------------------------------------------#
# If there was a highlight, remove it
_zsh_autosuggest_highlight_reset() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
if [[ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]]; then
region_highlight=("${(@)region_highlight:#$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT}")
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
fi
}
# If there's a suggestion, highlight it
_zsh_autosuggest_highlight_apply() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
if (( $#POSTDISPLAY )); then
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE"
region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT")
else
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
fi
}

View file

@ -0,0 +1,33 @@
#--------------------------------------------------------------------#
# Start #
#--------------------------------------------------------------------#
# Start the autosuggestion widgets
_zsh_autosuggest_start() {
# By default we re-bind widgets on every precmd to ensure we wrap other
# wrappers. Specifically, highlighting breaks if our widgets are wrapped by
# zsh-syntax-highlighting widgets. This also allows modifications to the
# widget list variables to take effect on the next precmd. However this has
# a decent performance hit, so users can set ZSH_AUTOSUGGEST_MANUAL_REBIND
# to disable the automatic re-binding.
if (( ${+ZSH_AUTOSUGGEST_MANUAL_REBIND} )); then
add-zsh-hook -d precmd _zsh_autosuggest_start
fi
_zsh_autosuggest_bind_widgets
}
# Mark for auto-loading the functions that we use
autoload -Uz add-zsh-hook is-at-least
# Automatically enable asynchronous mode in newer versions of zsh. Disable for
# older versions because there is a bug when using async mode where ^C does not
# work immediately after fetching a suggestion.
# See https://github.com/zsh-users/zsh-autosuggestions/issues/364
if is-at-least 5.0.8; then
typeset -g ZSH_AUTOSUGGEST_USE_ASYNC=
fi
# Start the autosuggestion widgets on the next precmd
add-zsh-hook precmd _zsh_autosuggest_start

Some files were not shown because too many files have changed in this diff Show more