I converted to Zsh a while back, when I first saw the Oh-My-Zsh framework.
OMZ is really just a long chain of source
calls
with some logic to turn groups of features on or off.
I’ve since changed to Zplugin
(not to be confused with a similar framework, zplug),
a wonderful framework which supports asynchronous plugin loading.
This has better than halved the load time for Zsh on my Pi and phone (Termux).
However, my theme had not changed, it still has to wait until (relatively)
expensive git
calls have completed before redrawing the prompt.
Now is the time to change this.
The theme I will be replacing is one I had modified from OMZ’s dieter theme.
Over the years (!) I have occasionally tweaked it a bit,
removing the time, adding, then removing Vi-mode status, adding, then removing the classic λ
prefix…
What I’m left with is this:
# Two directories, colored by permissions
function color_pwd() {
if (( $(stat -c "%u" . ) == UID )); then
# owner
print "%{\033[38;5;4m%}%2c"
elif [[ -w . ]]; then
# not owner, but have write permissions
print "%{\033[38;5;3m%}%2c"
else
# no write permissions
print "%{\033[38;5;5m%}%2c"
fi
}
Edit 2019-02-01: We can actually remove the fork to
stat
by abusing zsh globbing qualifiers. The test[[ -n .(#qNu$UID) ]]
replaces the stat call and comparison:
.( )
: Match.
(current working directory), with the following options:#q
: enables glob qualifiersN
: sets NULL_GLOB: if the pattern doesn’t match, it returns no stringu$UID
: Matches if user id of each file is owned by$UID
So, if the cwd is owned by $UID, this resolves to
[[ -n '.' ]]
(true). Otherwise, it resolves to[[ -n ]]
(false).
ZSH_THEME_GIT_PROMPT_PREFIX=" %{\033[38;5;3m%} "
ZSH_THEME_GIT_PROMPT_DIRTY=""
ZSH_THEME_GIT_PROMPT_UNTRACKED="?"
ZSH_THEME_GIT_PROMPT_ADDED="+"
ZSH_THEME_GIT_PROMPT_DELETED="-"
ZSH_THEME_GIT_PROMPT_MODIFIED="*"
function git_info() {
local info="$(git_prompt_info)"
(( ${+info} )) && print -n "$info$(git_prompt_status)%{\033[0m%}" \
|| print -n "$info%{\033[0m%}"
}
PROMPT='$PS1_HEADER$(color_pwd)$(git_info) '
# exitcode on the right when >0
return_code_enabled="%(?..%{$fg[red]%}%? ↵%{$reset_color%})"
return_code_disabled=
return_code=$return_code_enabled
RPS1="${return_code}"
function accept-line-or-clear-warning () {
if [[ -z $BUFFER ]]; then
time=$time_disabled
return_code=$return_code_disabled
else
time=$time_enabled
return_code=$return_code_enabled
fi
zle accept-line
}
zle -N accept-line-or-clear-warning
zle -N zle-keymap-select
bindkey '^M' accept-line-or-clear-warning
I am still using OMZ’s libs to get git status.
What is the most unusual is my conditionally coloring the CWD depending on my permissions.
If I own the directory, it is printed in blue,
if I can write to it (like /tmp), it shows yellow,
and if I don’t have write permissions, I will see magenta.
Also, I show the current directory only to a depth of 2, but still replace $HOME
with ~
.
Enter pure, an asynchronous Zsh prompt. This will be our starting point to recreate and extend my current theme.
Pure asynchronously fetches your git remote if it detects it is in a repository. It then will show arrows up and/or down if you are ahead and/or behind your default remote.
I like seeing my status, but I want to emulate git status -sb
,
which concisely shows your status with the commit count.
So let’s nix the arrows, and instead use [ahead_count:behind_count]
,
which takes up only a few characters and can indicate exactly how much I’ve forgotten to push.
Let’s rename and rewrite Pure’s prompt_pure_check_git_arrows
:
prompt_pure_check_git_status() {
setopt localoptions noshwordsplit
local ret left=${1:-0} right=${2:-0}
(( left > 0 )) && ret+="%F{green}$left"
(( left * right > 0 )) && ret+="%F{242}:"
(( right > 0 )) && ret+="%F{red}$right"
[[ -n $ret ]] || return
typeset -g REPLY="%F{242}[$ret%F{242}]"
}
Using (( math ))
is pretty quick, and by multiplying left and right together,
we can detect whether we need a :
to separate the two.
Really, there aren’t many other changes to Pure.
I still use two directories, colored by permissions;
I still use RPS1
for non-zero exit codes;
I still keep the prompt compact on one line;
I still have no prompt character.