2019-09-07 12:07:04 +00:00
|
|
|
#!/usr/bin/env zsh
|
|
|
|
|
|
|
|
# Escapes `%` in all arguments by replacing it with `%%`. Escaping is needed so
|
|
|
|
# that untrusted input (e.g. git branch names) doesn't affect prompt rendering.
|
|
|
|
prompt_escape() {
|
2021-03-30 12:15:33 +00:00
|
|
|
print -rn -- "${@//\%/%%}"
|
2019-09-07 12:07:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
prompt_preexec_hook() {
|
|
|
|
# record command start time
|
|
|
|
# $EPOCHREALTIME returns a float representing system time in seconds (see
|
|
|
|
# docs for zsh/datetime module) and is much faster than `date +%s.%N` because
|
|
|
|
# it doesn't involve starting a process
|
|
|
|
typeset -gF _PROMPT_EXEC_START_TIME="$EPOCHREALTIME"
|
|
|
|
}
|
|
|
|
|
|
|
|
prompt_precmd_hook() {
|
|
|
|
if [[ -v _PROMPT_EXEC_START_TIME ]]; then
|
|
|
|
local -F duration="$((EPOCHREALTIME - _PROMPT_EXEC_START_TIME))"
|
|
|
|
unset _PROMPT_EXEC_START_TIME
|
|
|
|
|
|
|
|
if (( duration > 1 )); then
|
|
|
|
local -i t="$duration" d h m s
|
|
|
|
typeset -g _PROMPT_EXEC_TIME=""
|
|
|
|
d="$((t/60/60/24))"
|
|
|
|
h="$((t/60/60%24))"
|
|
|
|
m="$((t/60%60))"
|
|
|
|
s="$((t%60))"
|
|
|
|
(( d > 0 )) && _PROMPT_EXEC_TIME+="${d}d"
|
|
|
|
(( h > 0 )) && _PROMPT_EXEC_TIME+="${h}h"
|
|
|
|
(( m > 0 )) && _PROMPT_EXEC_TIME+="${m}m"
|
|
|
|
_PROMPT_EXEC_TIME+="${s}s"
|
|
|
|
else
|
|
|
|
unset _PROMPT_EXEC_TIME
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
prompt_vcs_info() {
|
|
|
|
if [[ "$(command git rev-parse --is-inside-work-tree)" != true ]]; then
|
|
|
|
return
|
|
|
|
fi
|
|
|
|
|
|
|
|
local branch="(no branches)" line
|
2019-10-30 16:32:43 +00:00
|
|
|
command git branch | while IFS= read -r line; do
|
2019-09-07 12:07:04 +00:00
|
|
|
# find a line which starts with `* `, it contains the current branch name
|
|
|
|
if [[ "$line" == "* "* ]]; then
|
|
|
|
# remove the `* ` prefix
|
|
|
|
branch="${line#\* }"
|
|
|
|
break
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
2021-03-30 12:15:33 +00:00
|
|
|
print -rn -- ' %F{blue}git:%F{magenta}'"$(prompt_escape "$branch")"'%f'
|
2019-09-07 12:07:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# configure prompt expansion
|
|
|
|
# nopromptbang
|
|
|
|
# `!` should not be treated specially, use `%!` instead.
|
|
|
|
# promptcr and promptsp
|
|
|
|
# print a character (`%` for normal users, `#` for root) and a newline in
|
|
|
|
# order to preserve output that may be covered by the prompt. See
|
|
|
|
# zshoptions(1) for more details.
|
|
|
|
# promptpercent
|
|
|
|
# enable normal prompt expansion sequences which begin with a `%`.
|
|
|
|
# promptsubst
|
|
|
|
# enable parameter/command/arithmetic expansion/substitution in the prompt.
|
2019-09-24 19:34:15 +00:00
|
|
|
setopt no_prompt_bang prompt_cr prompt_sp prompt_percent prompt_subst
|
2019-09-07 12:07:04 +00:00
|
|
|
|
|
|
|
zmodload zsh/datetime
|
|
|
|
autoload -Uz add-zsh-hook
|
|
|
|
add-zsh-hook preexec prompt_preexec_hook
|
|
|
|
add-zsh-hook precmd prompt_precmd_hook
|
|
|
|
|
|
|
|
# Construct the prompt. See EXPANSION OF PROMPT SEQUENCES in zshmisc(1) for
|
|
|
|
# the list and descriptions of the expansion sequences.
|
|
|
|
|
|
|
|
# Start the prompt with gray (ANSI color 8 is "bright black" or "gray")
|
|
|
|
# box drawing characters, also enable bold font for the rest of it. This
|
|
|
|
# makes the prompt easily distinguishable from command output.
|
|
|
|
PROMPT='%F{8}┌─%f%B'
|
|
|
|
|
|
|
|
# username
|
|
|
|
PROMPT+='%F{%(!.red.yellow)}%n%f'
|
|
|
|
|
|
|
|
# hostname
|
|
|
|
PROMPT+=' at %F{'
|
|
|
|
if [[ -v SSH_CONNECTION ]]; then
|
|
|
|
PROMPT+='blue'
|
|
|
|
else
|
|
|
|
PROMPT+='green'
|
|
|
|
fi
|
|
|
|
PROMPT+='}%m%f'
|
|
|
|
|
|
|
|
# working directory
|
|
|
|
PROMPT+=' in %F{cyan}%~%f'
|
|
|
|
|
|
|
|
# VCS info
|
|
|
|
PROMPT+='$(prompt_vcs_info 2>/dev/null)'
|
|
|
|
|
2021-01-05 22:33:47 +00:00
|
|
|
# Python's virtualenv
|
|
|
|
PROMPT+='${VIRTUAL_ENV:+" %F{blue}venv:%F{magenta}${VIRTUAL_ENV:t}%f"}'
|
2021-02-02 20:50:51 +00:00
|
|
|
VIRTUAL_ENV_DISABLE_PROMPT=true
|
|
|
|
|
|
|
|
# pyenv
|
|
|
|
PROMPT+='${PYENV_VERSION:+" %F{blue}pyenv:%F{magenta}${PYENV_VERSION:t}%f"}'
|
|
|
|
PYENV_VIRTUAL_ENV_DISABLE_PROMPT=true
|
2021-01-05 22:33:47 +00:00
|
|
|
|
2019-09-07 12:07:04 +00:00
|
|
|
PROMPT+=' '
|
|
|
|
|
|
|
|
# command execution time
|
|
|
|
PROMPT+='${_PROMPT_EXEC_TIME:+" %F{yellow}$(prompt_escape "$_PROMPT_EXEC_TIME")%f"}'
|
|
|
|
|
|
|
|
# exit code of the previous command
|
|
|
|
PROMPT+='%(?.. %F{red}EXIT:%?%f)'
|
|
|
|
|
|
|
|
# number of currently running background jobs
|
|
|
|
PROMPT+='%1(j. %F{blue}JOBS:%j%f.)'
|
|
|
|
|
|
|
|
# A while ago I decided to start using a multiline prompt because:
|
|
|
|
# a) all commands I type are visually aligned
|
|
|
|
# b) I can type pretty long commands without text wrapping in small terminal
|
|
|
|
# windows (e.g. in Termux on my phone)
|
|
|
|
PROMPT+=$'\n'
|
|
|
|
|
|
|
|
# see prompt beginning
|
|
|
|
PROMPT+='%b%F{8}└─%f'
|
|
|
|
|
|
|
|
# the last character
|
|
|
|
PROMPT+='%F{%(?.green.red)}%(!.#.\$)%f '
|
|
|
|
|
|
|
|
# PROMPT2 is used when you type an unfinished command. Spaces are needed for
|
|
|
|
# alignment with normal PROMPT.
|
|
|
|
PROMPT2=' %_> '
|