280 lines
11 KiB
Plaintext
280 lines
11 KiB
Plaintext
# =============================================================================
|
|
# ~/.zshrc
|
|
# =============================================================================
|
|
# Interactive shell configuration. Environment and PATH live in ~/.zshenv.
|
|
#
|
|
# Order matters:
|
|
# 1. Profiling switch
|
|
# 2. tmux autostart (exec early, so the outer shell skips all init work)
|
|
# 3. Cheap stuff: options, keybindings, history
|
|
# 4. Expensive stuff: completion system, cached init output, prompt
|
|
# 5. Aliases
|
|
# 6. Functions
|
|
# 7. tmux integration (~/.zsh/tmux.zsh)
|
|
# 8. Finish: self-compile, profiling report
|
|
#
|
|
# Clear caches after a theme/tool update: zshrc-refresh
|
|
# Measure startup time: ZSHRC_PROFILE=1 zsh -i -c exit
|
|
# =============================================================================
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 1. Profiling (opt-in via environment variable)
|
|
# -----------------------------------------------------------------------------
|
|
[[ -n "$ZSHRC_PROFILE" ]] && zmodload zsh/zprof
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 2. tmux autostart
|
|
# -----------------------------------------------------------------------------
|
|
# Must come first: the outer shell gets replaced by tmux, so there's no point
|
|
# initializing it. The shell inside tmux runs this file again with $TMUX set.
|
|
#
|
|
# Terminals are detected via environment variables instead of ps//proc, which
|
|
# saves a handful of forks per shell start.
|
|
#
|
|
# Note: none of these variables survive an SSH login, so SSH sessions don't
|
|
# autostart tmux. Use ZSH_FORCE_TMUX=1 or uncomment the SSH line below.
|
|
#
|
|
# Overrides:
|
|
# ZSH_FORCE_TMUX=1 always start tmux
|
|
# ZSH_NO_TMUX=1 never start tmux
|
|
if [[ -o interactive && -z "$TMUX" && -z "$ZSHRC_PROFILE" && $UID -ne 0 && -t 1 ]]; then
|
|
# Leave IDEs and embedded terminals alone
|
|
if [[ -z "$TERMINAL_EMULATOR" && -z "$INSIDE_EMACS" \
|
|
&& -z "$VSCODE_INJECTION" && -z "$NVIM" ]]; then
|
|
|
|
_zsh_want_tmux=0
|
|
[[ "$TERM_PROGRAM" == ghostty || -n "$GHOSTTY_RESOURCES_DIR" ]] && _zsh_want_tmux=1 # Ghostty
|
|
[[ -n "$KITTY_WINDOW_ID" ]] && _zsh_want_tmux=1 # Kitty
|
|
[[ -n "$VTE_VERSION" ]] && _zsh_want_tmux=1 # GNOME Terminal, xfce4-terminal, Tilix
|
|
# [[ -n "$SSH_CONNECTION" ]] && _zsh_want_tmux=1 # SSH logins
|
|
[[ -n "$ZSH_FORCE_TMUX" ]] && _zsh_want_tmux=1
|
|
[[ -n "$ZSH_NO_TMUX" ]] && _zsh_want_tmux=0
|
|
|
|
if (( _zsh_want_tmux )) && (( $+commands[tmux] )); then
|
|
unset _zsh_want_tmux
|
|
exec tmux new-session -A -s main
|
|
fi
|
|
unset _zsh_want_tmux
|
|
fi
|
|
fi
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 3. Options, keybindings, history
|
|
# -----------------------------------------------------------------------------
|
|
bindkey -e # Emacs keybindings, even if EDITOR=vi
|
|
setopt interactive_comments # allow "# comments" on the command line
|
|
|
|
HISTFILE=~/.zsh_history
|
|
HISTSIZE=50000 # entries kept in memory
|
|
SAVEHIST=50000 # entries written to $HISTFILE
|
|
|
|
setopt hist_ignore_all_dups # drop older duplicates
|
|
setopt hist_ignore_space # don't record lines starting with a space
|
|
setopt hist_reduce_blanks # squeeze superfluous whitespace
|
|
setopt hist_verify # show history expansion before running it
|
|
setopt extended_history # store timestamps
|
|
setopt share_history # share history between running shells
|
|
|
|
# If prompts ever feel sluggish: share_history re-reads the history file on
|
|
# every prompt. Alternative that only appends:
|
|
# unsetopt share_history
|
|
# setopt inc_append_history
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 4a. Cache directory and prompt theme
|
|
# -----------------------------------------------------------------------------
|
|
ZSH_CACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/zsh
|
|
[[ -d $ZSH_CACHE_DIR ]] || mkdir -p $ZSH_CACHE_DIR
|
|
|
|
OMP_CONFIG=$HOME/.zsh/atomic-one-line.omp.json
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 4b. Completion system
|
|
# -----------------------------------------------------------------------------
|
|
# A bare "compinit" runs compaudit and stats every $fpath directory on each
|
|
# start - hundreds of stat() calls, noticeable with a cold page cache.
|
|
# So: full check once a day, otherwise load straight from the dump.
|
|
() {
|
|
emulate -L zsh
|
|
setopt extended_glob
|
|
|
|
local zdump=$ZSH_CACHE_DIR/zcompdump
|
|
|
|
autoload -Uz compinit
|
|
if [[ -n $zdump(#qNmh-24) ]]; then
|
|
compinit -C -d $zdump # dump younger than 24h -> skip the audit
|
|
else
|
|
compinit -d $zdump # full run, once a day
|
|
touch $zdump
|
|
fi
|
|
|
|
# Compiled dump loads noticeably faster
|
|
if [[ ! -s ${zdump}.zwc || $zdump -nt ${zdump}.zwc ]]; then
|
|
zcompile $zdump 2>/dev/null
|
|
fi
|
|
}
|
|
|
|
zstyle ':completion:*' auto-description 'specify: %d'
|
|
zstyle ':completion:*' completer _expand _complete _correct _approximate
|
|
zstyle ':completion:*' format 'Completing %d'
|
|
zstyle ':completion:*' group-name ''
|
|
zstyle ':completion:*' menu select=long
|
|
zstyle ':completion:*' list-prompt %SAt %p: Hit TAB for more, or the character to insert%s
|
|
zstyle ':completion:*' matcher-list '' 'm:{a-z}={A-Z}' 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=* l:|=*'
|
|
zstyle ':completion:*' select-prompt %SScrolling active: current selection at %p%s
|
|
zstyle ':completion:*' use-compctl false
|
|
zstyle ':completion:*' verbose true
|
|
|
|
# Cache expensive completion results (e.g. apt package lists)
|
|
zstyle ':completion:*' use-cache on
|
|
zstyle ':completion:*' cache-path $ZSH_CACHE_DIR/compcache
|
|
|
|
# kill: colored process list
|
|
zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#)*=0=01;31'
|
|
zstyle ':completion:*:kill:*' command 'ps -u $USER -o pid,%cpu,tty,cputime,cmd'
|
|
|
|
# ssh: hosts from ~/.ssh/config (+ config.d). "zstyle -e" evaluates this lazily
|
|
# on <Tab>, not on every shell start. Wildcard patterns are filtered out.
|
|
zstyle -e ':completion:*:*:ssh:*' hosts \
|
|
'reply=(${(f)"$(grep -h "^Host " ~/.ssh/config ~/.ssh/config.d/*(N) 2>/dev/null | sed "s/^Host //" | tr " " "\n" | grep -v "[*?]")"})'
|
|
|
|
# (list-colors for files is set further down, once LS_COLORS exists)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 4c. Cached init output (television, dircolors)
|
|
# -----------------------------------------------------------------------------
|
|
# Both tools print the same static shell code on every run. Instead of forking
|
|
# them each time, their output is written to one file, compiled, and sourced.
|
|
# The cache is rebuilt when ~/.dircolors or the tv binary is newer than it.
|
|
#
|
|
# Each block is followed by an explicit newline: some tools end their output
|
|
# without one, which would glue the next block onto the last line.
|
|
() {
|
|
emulate -L zsh
|
|
local cache=$ZSH_CACHE_DIR/init.zsh
|
|
local stale=0
|
|
|
|
[[ -s $cache ]] || stale=1
|
|
[[ -f ~/.dircolors && ~/.dircolors -nt $cache ]] && stale=1
|
|
[[ -n $commands[tv] && $commands[tv] -nt $cache ]] && stale=1
|
|
|
|
if (( stale )); then
|
|
{
|
|
if (( $+commands[tv] )); then
|
|
tv init zsh
|
|
print
|
|
fi
|
|
if (( $+commands[dircolors] )); then
|
|
if [[ -r ~/.dircolors ]]; then
|
|
dircolors -b ~/.dircolors
|
|
else
|
|
dircolors -b
|
|
fi
|
|
print
|
|
fi
|
|
} > $cache.tmp.$$ 2>/dev/null && mv -f $cache.tmp.$$ $cache
|
|
rm -f $cache.tmp.$$
|
|
zcompile $cache 2>/dev/null
|
|
fi
|
|
|
|
[[ -s $cache ]] && source $cache
|
|
}
|
|
|
|
# LS_COLORS is set now -> colorize file completion
|
|
zstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS}
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 4d. Prompt (oh-my-posh)
|
|
# -----------------------------------------------------------------------------
|
|
# Since v26, oh-my-posh caches its init code itself under ~/.cache/oh-my-posh/
|
|
# (file name contains version + config hash); "init zsh" only prints a small
|
|
# stub that sources it. Caching that stub again would break on every update.
|
|
#
|
|
# If the prompt is suddenly plain ("host%"), oh-my-posh isn't in PATH -
|
|
# check ~/.zshenv.
|
|
if (( $+commands[oh-my-posh] )); then
|
|
eval "$(oh-my-posh init zsh --config "$OMP_CONFIG")"
|
|
fi
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 5. Aliases
|
|
# -----------------------------------------------------------------------------
|
|
alias ls='ls --color=auto'
|
|
alias grep='grep --color=auto'
|
|
alias fgrep='fgrep --color=auto'
|
|
alias egrep='egrep --color=auto'
|
|
|
|
[[ -f ~/.bash_aliases ]] && source ~/.bash_aliases
|
|
[[ -f ~/.config/common_aliases ]] && source ~/.config/common_aliases
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 6. Functions
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# zshrc-refresh: clear all zsh caches and restart the shell
|
|
zshrc-refresh() {
|
|
rm -f $ZSH_CACHE_DIR/init.zsh{,.zwc} \
|
|
$ZSH_CACHE_DIR/zcompdump{,.zwc} \
|
|
~/.zshrc.zwc
|
|
# oh-my-posh normally cleans up its own cache. If the prompt misbehaves
|
|
# after an update, additionally run:
|
|
# rm -rf ~/.cache/oh-my-posh
|
|
echo "Cache removed, restart shell"
|
|
exec zsh
|
|
}
|
|
|
|
# tm [name]: create or switch to a tmux session (default: main)
|
|
tm() {
|
|
local s=${1:-main}
|
|
if [[ -n "$TMUX" ]]; then
|
|
tmux has-session -t "=$s" 2>/dev/null || tmux new-session -d -s "$s"
|
|
tmux switch-client -t "=$s"
|
|
else
|
|
tmux new-session -A -s "$s"
|
|
fi
|
|
}
|
|
|
|
# y: run yazi and cd into the directory it was closed in
|
|
y() {
|
|
local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd
|
|
yazi "$@" --cwd-file="$tmp"
|
|
IFS= read -r -d '' cwd < "$tmp"
|
|
[[ -n "$cwd" && "$cwd" != "$PWD" ]] && builtin cd -- "$cwd"
|
|
rm -f -- "$tmp"
|
|
}
|
|
|
|
# Bandcamp Aliases
|
|
|
|
[[ -f ~/.zsh/bandcamp.zsh ]] && source ~/.zsh/bandcamp.zsh
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 7. tmux integration
|
|
# -----------------------------------------------------------------------------
|
|
# Window names from the current directory plus active SSH hosts per pane.
|
|
# Only does something inside tmux.
|
|
[[ -f ~/.zsh/tmux.zsh ]] && source ~/.zsh/tmux.zsh
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 8. Finish
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Compile this file. zsh picks up ~/.zshrc.zwc automatically as long as it is
|
|
# newer than ~/.zshrc, and it gets rebuilt here after every change.
|
|
if [[ ! -s ~/.zshrc.zwc || ~/.zshrc -nt ~/.zshrc.zwc ]]; then
|
|
zcompile ~/.zshrc 2>/dev/null
|
|
fi
|
|
|
|
[[ -n "$ZSHRC_PROFILE" ]] && zprof
|