# zsh/.prompt.zsh -- a pure-prompt inspired two-line prompt in plain zsh.
#
# Why roll my own instead of using pure/starship/p10k:
# * I want zero runtime dependencies on a fresh box.
# * I want it to stay snappy over a laggy ssh.
# The prompt renders the last exit code, current dir, git branch + dirty
# marker, and a ' $ ' (red on failure). Async git probing keeps cd fast.
autoload -Uz add-zsh-hook vcs_info
setopt PROMPT_SUBST
zstyle ':vcs_info:*' enable git
zstyle ':vcs_info:git:*' formats ' %F{244}%b%f'
zstyle ':vcs_info:git:*' actionformats ' %F{244}%b|%a%f'
typeset -g _dot_prompt_git=""
_dot_prompt_git_async() {
# Runs in a subshell, writes status into a file; the precmd hook reads
# the file and re-draws. Keeps the foreground prompt instant even in
# huge monorepos.
local repo_root status_line
repo_root=$(command git rev-parse --show-toplevel 2>/dev/null) || return
local dirty=""
if ! command git diff --quiet --ignore-submodules=dirty 2>/dev/null; then
dirty="*"
elif ! command git diff --cached --quiet 2>/dev/null; then
dirty="+"
fi
local branch
branch=$(command git symbolic-ref --quiet --short HEAD 2>/dev/null || command git describe --tags --exact-match 2>/dev/null || command git rev-parse --short HEAD 2>/dev/null)
printf '%s%s' "$branch" "$dirty" > "${_dot_prompt_cache:?}"
kill -USR1 $$ 2>/dev/null || true
}
_dot_prompt_precmd() {
local last=$?
vcs_info
_dot_prompt_last_exit=$last
_dot_prompt_cache="${TMPDIR:-/tmp}/zsh-prompt-$$"
# Launch async probe if inside a git repo.
if command git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
( _dot_prompt_git_async & ) &!
else
_dot_prompt_git=""
fi
}
_dot_prompt_on_usr1() {
[[ -r "$_dot_prompt_cache" ]] || return
_dot_prompt_git=" %F{244}$(<"$_dot_prompt_cache")%f"
rm -f "$_dot_prompt_cache"
zle -I
zle reset-prompt 2>/dev/null || true
}
trap '_dot_prompt_on_usr1' USR1
add-zsh-hook precmd _dot_prompt_precmd
_dot_prompt_exit_badge() {
if (( _dot_prompt_last_exit != 0 )); then
print -n -- "%F{red}[$_dot_prompt_last_exit]%f "
fi
}
_dot_prompt_venv() {
[[ -n $VIRTUAL_ENV ]] || return
print -n -- " %F{244}(${VIRTUAL_ENV:t})%f"
}
_dot_prompt_jobs() {
# Show jobs count only when non-zero so the normal line stays tidy.
jobs -l | awk 'END { if (NR) printf " %%F{244}⚙%d%%f", NR }'
}
# Arrow colour: green on success, red on failure. Plain ASCII '$' so the
# prompt still copies cleanly in terminals without a nerd font.
_dot_prompt_arrow() {
if (( _dot_prompt_last_exit == 0 )); then
print -n -- '%F{green}$%f '
else
print -n -- '%F{red}$%f '
fi
}
PROMPT='$(_dot_prompt_exit_badge)%F{cyan}%~%f${vcs_info_msg_0_}${_dot_prompt_git}$(_dot_prompt_venv)$(_dot_prompt_jobs)
$(_dot_prompt_arrow)'
RPROMPT='%F{244}%*%f'
# Continuation prompt: show which construct we are inside (quote, brace...)
PROMPT2='%F{244}%_%f > '
# Select / menu prompt.
PROMPT3='?# '
# Shortcut for opting out on slow sshfs mounts.
if [[ -n ${DOT_PROMPT_PLAIN:-} ]]; then
PROMPT='%n@%m:%~ %# '
RPROMPT=""
fi