update zsh config

This commit is contained in:
2026-07-26 23:36:02 +02:00
parent cdf5c6e541
commit 678cfb76ba
19 changed files with 223 additions and 107 deletions
+1
View File
@@ -1,3 +1,4 @@
# antidote generated/cache files
.zsh_plugins.zsh
.zsh_plugins_pre.zsh
.antidote/
+12 -11
View File
@@ -1,17 +1,18 @@
# Syntax highlighting
zsh-users/zsh-syntax-highlighting
# Loaded by conf.d/95-plugins.zsh, AFTER compinit and after every conf.d file
# that defines a ZLE widget. The order below is the load order and is required.
#
# Completions live in .zsh_plugins_pre.txt — they must reach $fpath before
# compinit, so they cannot be listed here.
# Catppuccin Mocha theme for syntax highlighting
# catppuccin/zsh-syntax-highlighting path:themes/catppuccin_latte-zsh-syntax-highlighting.zsh
catppuccin/zsh-syntax-highlighting path:themes/catppuccin_mocha-zsh-syntax-highlighting.zsh
# Fuzzy tab completion — after compinit, before anything that wraps widgets
Aloxaf/fzf-tab
# Fish-like autosuggestions
zsh-users/zsh-autosuggestions
# Additional completions
zsh-users/zsh-completions
# Syntax highlighting — must stay last, it wraps every widget defined so far
zsh-users/zsh-syntax-highlighting
# Long command done notification — handled by conf.d/10-done.zsh
# Fuzzy tab completion
Aloxaf/fzf-tab
# Catppuccin Mocha theme, layered on top of zsh-syntax-highlighting
# catppuccin/zsh-syntax-highlighting path:themes/catppuccin_latte-zsh-syntax-highlighting.zsh
catppuccin/zsh-syntax-highlighting path:themes/catppuccin_mocha-zsh-syntax-highlighting.zsh
@@ -0,0 +1,8 @@
# Loaded by conf.d/02-fpath.zsh, BEFORE compinit.
# kind:fpath only appends to $fpath — nothing here gets sourced.
#
# path:src is required: kind:fpath appends the repo root, but the _* files
# live in src/, so without it compinit finds nothing.
# Additional completions
zsh-users/zsh-completions path:src kind:fpath
+3 -2
View File
@@ -1,8 +1,8 @@
*
!.gitignore
!01-options.zsh
!02-completions.zsh
!03-plugins.zsh
!02-fpath.zsh
!03-compinit.zsh
!10-done.zsh
!10-env.zsh
!10-oops.zsh
@@ -11,3 +11,4 @@
!60-alias.zsh
!60-direnv.zsh
!60-fetch.zsh
!95-plugins.zsh
+12 -8
View File
@@ -1,14 +1,17 @@
# History
HISTFILE="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/history"
histdir="${XDG_DATA_HOME:-$HOME/.local/share}/zsh"
HISTFILE="${histdir}/history"
[[ -d $histdir ]] || mkdir -p "$histdir"
unset histdir
HISTSIZE=100000
SAVEHIST=100000
setopt APPEND_HISTORY SHARE_HISTORY EXTENDED_HISTORY
setopt HIST_IGNORE_DUPS HIST_SAVE_NO_DUPS HIST_FIND_NO_DUPS
setopt HIST_IGNORE_SPACE HIST_REDUCE_BLANKS INC_APPEND_HISTORY
setopt HIST_VERIFY
mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}/zsh"
setopt SHARE_HISTORY EXTENDED_HISTORY
setopt HIST_IGNORE_DUPS HIST_SAVE_NO_DUPS HIST_FIND_NO_DUPS
setopt HIST_IGNORE_SPACE HIST_REDUCE_BLANKS
setopt HIST_VERIFY
# Shell options
@@ -27,6 +30,7 @@ bindkey '^Z' undo # C-z
uy_prepend_sudo() {
if [[ $BUFFER == sudo\ * ]]; then
BUFFER="${BUFFER#sudo }"
(( CURSOR -= 5 ))
else
BUFFER="sudo $BUFFER"
CURSOR+=5
@@ -38,11 +42,11 @@ bindkey '^[s' uy_prepend_sudo
# Edit current command in editor
autoload -Uz edit-command-line
zle -N edit-command-line
bindkey '^X' edit-command-line
bindkey '^X^E' edit-command-line
# Magic space
# bindkey ' ' magic-space
# This is already handled by tab
# zmv
autoload zmv
autoload -Uz zmv
@@ -1,28 +0,0 @@
if [ -d "$HOME/.zfunc" ]; then
fpath=($HOME/.zfunc $fpath)
fi
if [ -d "$HOME/.zsh/completions" ]; then
fpath=($HOME/.zsh/completions $fpath)
fi
# Cache compinit: only regenerate dump once daily
autoload -Uz compinit
if [[ -n ${ZDOTDIR:-$HOME}/.zcompdump(#qN.mh+24) ]]; then
compinit
else
compinit -C
fi
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
zstyle ':completion:*' menu select
zstyle ':completion:*' squeeze-slashes true
zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
zstyle ':completion:*:descriptions' format '%F{cyan}-- %d --%f'
zstyle ':completion:*:warnings' format '%F{red}-- no matches --%f'
# Jujutsu
if (( $+commands[jj] )); then
source <(jj util completion zsh)
fi
@@ -0,0 +1,53 @@
# $fpath must be complete BEFORE compinit runs (03-compinit.zsh)
if [[ -d "$HOME/.zfunc" ]]; then
fpath=("$HOME/.zfunc" $fpath)
fi
if [[ -d "$HOME/.zsh/completions" ]]; then
fpath=("$HOME/.zsh/completions" $fpath)
fi
# Antidote: system package → user-local clone → auto-bootstrap.
# Sourced here because the first `antidote load` happens below.
_uy_antidote_dir="${XDG_DATA_HOME:-$HOME/.local/share}/antidote"
if [[ -f /usr/share/zsh-antidote/antidote.zsh ]]; then
source /usr/share/zsh-antidote/antidote.zsh
elif [[ -f "$_uy_antidote_dir/antidote.zsh" ]]; then
source "$_uy_antidote_dir/antidote.zsh"
elif (( $+commands[git] )); then
_uy_antidote_stamp="${XDG_CACHE_HOME:-$HOME/.cache}/zsh/antidote-bootstrap-failed"
if [[ -z $_uy_antidote_stamp(#qN.mh-1) ]]; then
if (( $+commands[timeout] )); then
# -k: SIGKILL follows if git ignores the SIGTERM
timeout -k 5 20 git clone -q --depth=1 https://github.com/mattmc3/antidote.git "$_uy_antidote_dir"
else
git clone -q --depth=1 https://github.com/mattmc3/antidote.git "$_uy_antidote_dir"
fi
if [[ -f "$_uy_antidote_dir/antidote.zsh" ]]; then
command rm -f -- "$_uy_antidote_stamp"
source "$_uy_antidote_dir/antidote.zsh"
else
# Drop the partial clone: `git clone` refuses to write into a
# non-empty directory, so leaving it behind never recovers.
[[ -d "$_uy_antidote_dir" ]] && command rm -rf -- "$_uy_antidote_dir"
[[ -d "${_uy_antidote_stamp:h}" ]] || mkdir -p "${_uy_antidote_stamp:h}"
: >| "$_uy_antidote_stamp"
print -u2 "zsh: antidote bootstrap failed; plugins disabled, retrying in an hour"
fi
fi
fi
# Pre-compinit bundles — kind:fpath entries only, nothing is sourced.
if (( $+functions[antidote] )); then
_uy_zsh_plugins_pre="${XDG_CONFIG_HOME:-$HOME/.config}/zsh/.zsh_plugins_pre.txt"
if [[ -f "$_uy_zsh_plugins_pre" ]]; then
antidote load "$_uy_zsh_plugins_pre"
fi
fi
unset _uy_antidote_dir _uy_antidote_stamp _uy_zsh_plugins_pre
@@ -0,0 +1,26 @@
# compinit
#
# Sits between 02-fpath.zsh (which finishes $fpath) and
# 95-plugins.zsh (which loads fzf-tab, and fzf-tab requires compinit first).
# Keep the dump next to the other zsh state (cf. $HISTFILE).
_uy_zcompdump="${XDG_CACHE_HOME:-$HOME/.cache}/zsh/zcompdump"
[[ -d "${_uy_zcompdump:h}" ]] || mkdir -p "${_uy_zcompdump:h}"
# Cache compinit: only regenerate dump once daily
autoload -Uz compinit
if [[ -n $_uy_zcompdump(#qN.mh+24) ]]; then
compinit -d "$_uy_zcompdump"
else
compinit -C -d "$_uy_zcompdump"
fi
unset _uy_zcompdump
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
# fzf-tab needs zsh's own menu off so it can capture the unambiguous prefix
zstyle ':completion:*' menu no
zstyle ':completion:*' squeeze-slashes true
zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
zstyle ':completion:*:descriptions' format '%F{cyan}-- %d --%f'
zstyle ':completion:*:warnings' format '%F{red}-- no matches --%f'
@@ -1,20 +0,0 @@
_uy_antidote_dir="${XDG_DATA_HOME:-$HOME/.local/share}/antidote"
# Find antidote: system package → user-local clone → auto-bootstrap
if [[ -f /usr/share/zsh-antidote/antidote.zsh ]]; then
source /usr/share/zsh-antidote/antidote.zsh
elif [[ -f "$_uy_antidote_dir/antidote.zsh" ]]; then
source "$_uy_antidote_dir/antidote.zsh"
elif (( $+commands[git] )); then
git clone --depth=1 https://github.com/mattmc3/antidote.git "$_uy_antidote_dir"
source "$_uy_antidote_dir/antidote.zsh"
fi
if (( $+functions[antidote] )); then
_uy_zsh_plugins="${XDG_CONFIG_HOME:-$HOME/.config}/zsh/.zsh_plugins.txt"
if [[ -f "$_uy_zsh_plugins" ]]; then
antidote load "$_uy_zsh_plugins"
fi
fi
unset _uy_antidote_dir _uy_zsh_plugins
+22 -3
View File
@@ -7,13 +7,31 @@
zmodload zsh/datetime
: ${uy_done_min_cmd_duration:=10}
: ${uy_done_exclude:='^(nvim|helix|hx|vim|vi|nano|less|more|man|ssh|top|htop|btop)$'}
: ${uy_done_exclude:='^(nvim|helix|hx|vim|vi|nano|less|more|man|ssh|top|htop|btop|sudoedit)$'}
uy_done_get_focused_window_id() {
(( $+commands[jq] )) || return
niri msg --json focused-window 2>/dev/null | jq -r '.id // empty'
}
uy_done_cmd_name() {
local -a words
words=(${(z)1})
while (( $#words )); do
case $words[1] in
*=*) ;;
-*) ;;
sudo|doas|env|command|builtin|exec|nohup|nice|ionice|time|stdbuf|setsid) ;;
*) REPLY=${words[1]:t}; return ;;
esac
shift words
done
# Nothing but wrappers — fall back to the first word.
REPLY=${1%% *}
}
uy_done_preexec() {
uy_done_cmd="$1"
uy_done_start=$EPOCHSECONDS
@@ -30,8 +48,9 @@ uy_done_precmd() {
(( elapsed >= uy_done_min_cmd_duration )) || return
# skip excluded commands
local cmd_name="${uy_done_cmd%% *}"
[[ "$cmd_name" =~ $uy_done_exclude ]] && return
local REPLY
uy_done_cmd_name "$uy_done_cmd"
[[ "$REPLY" =~ $uy_done_exclude ]] && return
# skip if window is still focused
local current_id
+1 -1
View File
@@ -23,7 +23,7 @@ fi
# GPG
if (( $+commands[gpg] )); then
export GPG_TTY=$(tty)
export GPG_TTY=$TTY
fi
# Catppuccin Mocha — autosuggestions color
+31 -19
View File
@@ -1,5 +1,9 @@
# Remove the last command from history (both memory and file).
# The alias has a leading space so "oops" itself is not recorded (HIST_IGNORE_SPACE).
#
# NOTE: other shells running concurrently still hold the entry in their own
# in-memory history and can write it back; SHARE_HISTORY does not propagate
# deletions.
uy_oops_confirm() {
# Flush current session to file so we operate on the latest state
fc -W
@@ -9,25 +13,28 @@ uy_oops_confirm() {
return 1
fi
# Read the last entry (may span multiple lines if it ends with \)
local -a entry
local line
while IFS= read -r line; do
entry+=("$line")
# EXTENDED_HISTORY continuation lines end with a literal backslash
[[ "$line" == *'\' ]] || break
done < <(tail -n 50 "$HISTFILE" | tac)
# entry is reversed (last line first), flip it back
# Find where the last entry starts. An entry continues onto the next line
# when the current one ends in a backslash; zsh writes a command that itself
# ends in a backslash as "\ " (trailing space), so the marker is unambiguous.
# Walking forward and remembering the start of each entry is exact for
# entries of any length — scanning backwards over a fixed window is not.
local start
start=$(awk '{ if (!cont) start = NR; cont = /\\$/ } END { print start + 0 }' "$HISTFILE")
if [[ -z "$start" ]] || (( start < 1 )); then
print -P "%F{yellow}Could not parse last history entry.%f"
return 1
fi
local -a ordered
for (( i=${#entry[@]}; i>=1; i-- )); do
ordered+=("${entry[$i]}")
done
ordered=("${(@f)$(tail -n +"$start" "$HISTFILE")}")
if (( ${#ordered[@]} == 0 )); then
print -P "%F{yellow}Could not parse last history entry.%f"
return 1
fi
local line
print -P "About to permanently delete the last command from history:"
for line in "${ordered[@]}"; do
print -P " %F{red}${line}%f"
@@ -38,17 +45,22 @@ uy_oops_confirm() {
read -r reply
if [[ -z "$reply" || "$reply" == [yY]* ]]; then
# Delete the last N lines (the full entry) from the file
# Keep everything before the entry. start-1 == 0 (the entry is the only
# one) correctly leaves an empty file.
local n=${#ordered[@]}
head -n -"$n" "$HISTFILE" > "$HISTFILE.tmp" && mv "$HISTFILE.tmp" "$HISTFILE"
# Reload history from the updated file
fc -R
head -n $(( start - 1 )) "$HISTFILE" > "$HISTFILE.tmp" && mv "$HISTFILE.tmp" "$HISTFILE"
# Replace the in-memory history with the updated file.
# `fc -R` would *append* the file on top of what is already in memory,
# which both duplicates every entry and leaves the deleted command in
# memory — the next `fc -W` (e.g. the one at the top of this function
# on a second `oops`) would then write it straight back to the file.
fc -p "$HISTFILE" $HISTSIZE $SAVEHIST
print -P "%F{green}Deleted ($n line(s) removed).%f"
else
# Reload anyway to stay in sync
fc -R
# Nothing was modified, so there is nothing to reload.
print -P "%F{yellow}Cancelled.%f"
fi
}
# The alias has a leading space so "oops" itself is not recorded (HIST_IGNORE_SPACE).
alias oops=' uy_oops_confirm'
+1 -1
View File
@@ -2,7 +2,7 @@
# $uy_ssh_keys should be set in a device-specific file or left empty for defaults
if [[ "${UY_ENABLE_GPG_AGENT_SSH:-0}" = "1" ]] &&
(( $+commands[gpg-init] )) && (( $+commands[gpgconf] )); then
[[ -x "$HOME/.local/scripts/gpg-init" ]] && (( $+commands[gpgconf] )); then
: # GPG agent handles SSH — nothing to do
elif [[ "${UY_USING_SSH_AGENT:-0}" = "1" ]]; then
+3 -2
View File
@@ -121,7 +121,7 @@ copy() {
# wget
if (( $+commands[wget] )); then
alias wget="wget -c "
alias wget="wget -c"
fi
# pacman
@@ -233,6 +233,7 @@ if (( $+commands[jj] )); then
pos=${2:-@-}
jj bookmark move "$branch" --to "$pos"
jj git push
unfunction default_branch
}
alias jja="jj log -r 'all()' -T builtin_log_detailed"
@@ -242,7 +243,7 @@ fi
# wl-paste
if (( $+commands[wl-paste] )); then
alias -g C="| wl-copy"
alias -g COPY="| wl-copy"
fi
# Redirects
+25 -2
View File
@@ -1,6 +1,6 @@
# Detect logo capability
# Detect logo capability.
if [[ -z "$uy_fetch_logo_type" ]]; then
_uy_fetch_probe() {
if (( $+commands[kgp-query] )) && kgp-query 2>/dev/null; then
uy_fetch_logo_type=kitty
elif (( $+commands[sixel-query] )) && sixel-query 2>/dev/null; then
@@ -10,8 +10,31 @@ if [[ -z "$uy_fetch_logo_type" ]]; then
else
uy_fetch_logo_type=auto
fi
}
if [[ -z "$uy_fetch_logo_type" ]]; then
if [[ -t 0 ]] && (( $+commands[kgp-query] || $+commands[sixel-query] )); then
_uy_fetch_cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/zsh/logo-type"
# Terminal identity, sanitised down to a single path component
_uy_fetch_cache_file="$_uy_fetch_cache_dir/${${TERM:-none}//[^A-Za-z0-9._-]/_}@${${TERM_PROGRAM:-none}//[^A-Za-z0-9._-]/_}"
[[ -s "$_uy_fetch_cache_file" ]] && uy_fetch_logo_type="$(<"$_uy_fetch_cache_file")"
# Covers both a cache miss and a cache file holding anything unexpected
if [[ "$uy_fetch_logo_type" != (kitty|sixel|logo|symbols|auto) ]]; then
_uy_fetch_probe
[[ -d "$_uy_fetch_cache_dir" ]] || mkdir -p "$_uy_fetch_cache_dir"
print -r -- "$uy_fetch_logo_type" >| "$_uy_fetch_cache_file"
fi
unset _uy_fetch_cache_dir _uy_fetch_cache_file
else
_uy_fetch_probe
fi
fi
unfunction _uy_fetch_probe
: ${uy_fetch_color:="38;2;137;180;250"}
# Build fastfetch args (arrays to handle paths with spaces safely)
@@ -0,0 +1,15 @@
# Plugins that wrap ZLE widgets. Deliberately the last conf.d file:
# - fzf-tab must load after compinit (03-compinit.zsh)
# - fzf-tab must load before the plugins that wrap widgets
# - zsh-syntax-highlighting must load after everything that defines a widget,
# i.e. after 50-prompt.zsh (zle hooks) and 60-alias.zsh (`fzf --zsh`)
# Load order inside .zsh_plugins.txt matters and encodes the same rules.
if (( $+functions[antidote] )); then
_uy_zsh_plugins="${XDG_CONFIG_HOME:-$HOME/.config}/zsh/.zsh_plugins.txt"
if [[ -f "$_uy_zsh_plugins" ]]; then
antidote load "$_uy_zsh_plugins"
fi
fi
unset _uy_zsh_plugins
+10 -7
View File
@@ -12,11 +12,14 @@ fi
# SSH agent
if (( $+commands[ssh-add] )) && (( $+commands[ssh-agent] )) &&
{ [[ -z "$SSH_AUTH_SOCK" ]] ||
{ ssh-add -l &>/dev/null; [[ $? -eq 2 ]]; } } &&
[[ -x "$HOME/.local/scripts/ssh-init" ]]; then
unset SSH_AUTH_SOCK
eval "$($HOME/.local/scripts/ssh-init 2>/dev/null)" &>/dev/null
export UY_USING_SSH_AGENT=1
if (( $+commands[ssh-add] )) && (( $+commands[ssh-agent] )); then
# ssh-add -l: 0 = agent with keys, 1 = agent without keys, 2 = no agent
if [[ -n "$SSH_AUTH_SOCK" ]] &&
{ ssh-add -l &>/dev/null; [[ $? -ne 2 ]]; }; then
export UY_USING_SSH_AGENT=1
elif [[ -x "$HOME/.local/scripts/ssh-init" ]]; then
unset SSH_AUTH_SOCK
eval "$($HOME/.local/scripts/ssh-init 2>/dev/null)" &>/dev/null
[[ -n "$SSH_AUTH_SOCK" ]] && export UY_USING_SSH_AGENT=1
fi
fi
-1
View File
@@ -45,7 +45,6 @@ fi
# Paths
[[ -f "$HOME/.cargo/env" ]] && source "$HOME/.cargo/env"
(( $+commands[opam] )) && eval "$(opam env)"
prepend_path "$HOME/.local/share/fnm/aliases/default/bin" # 'fnm env' is eval'ed in .zshrc
prepend_path "$HOME/.cargo/bin"
prepend_path "$HOME/go/bin"
-2
View File
@@ -1,7 +1,5 @@
#!/hint/zsh
[[ $- != *i* ]] && return
for _f in "${XDG_CONFIG_HOME:-$HOME/.config}"/zsh/conf.d/*.zsh(N); do
source "$_f"
done