3.7 KiB
title | tags | description | date | draft |
---|---|---|---|---|
Building a ZSH prompt | [Linux ZSH] | There are many frameworks out there to help you build a prompt, I thought I'd see how far I could get without one. | 2020-03-24 | true |
Until recently, I was using oh-my-zsh with the Power Level 9k prompt.
In part of my effort to understand how different parts of my system works, and to reduce my reliance on some libraries, I decided to see if I could get what I needed without them. In this blog post, I am going to focus on my prompt configuration.
If you want to follow along, my configuration can be found in my dotfiles.
My prompt currently looks something like this:
Basics
Zsh simply prints the variables PROMPT
and RPROMPT
when drawing the left and right prompts respectively. PROMPT
is roughly equivalent to the PS1
variable in bash.
By setting the variable, you can change how the prompt works. E.g.
oldPrompt $ PROMPT="new prompt > "
new prompt > ls
Documents Downloads GitRepos
new prompt > █
ZSH Hooks
Zsh has a concept called hooks. These allow you to run a function (or functions) automatically at certain times.
For the purposes of setting my prompt, I want to run a function to set PROMPT
and RPROMPT
before the prompt is drawn. This can be done with the precmd
hook.
set_prompts(){
PROMPT="$ "
RPROMPT=""
}
zle -N set_prompts
autoload -U add-zsh-hook
add-zsh-hook precmd set_prompts
This defines a function set_prompts
and it will be run just before the prompt is drawn, every time. We can therefore use it to build the prompt.
Drawing segments
I like the arrow style that was made popular by Powerline. To achieve this, you will need a font that contains the powewline glyphs. If you don't have one, I highly recommend the Nerd Fonts project that aggregates a lot of icons as well as the powerline symbols. It even includes a script that allows you to add them to your favourite font.
Exit Code
In ZSH (and most other shells), the exit code of the previous command is available in the special $?
variable. If we want the exit code of the last command run, we need to assign this to a variable first.
# Get the return status of the previous command
local RETVAL=$?
Git
I use Git a lot. It is useful to tell, at a glance, which branch I'm on, which tag I'm on and what the state of repository is. The function is quite simple:
prompt_git(){
local repoTopLevel="$(command git rev-parse --show-toplevel 2> /dev/null)"
if [ -n "$repoTopLevel" ]; then
local branch="$(git branch --show-current 2> /dev/null)"
local tag="$(git describe --tags --exact-match HEAD 2> /dev/null)"
local color="green"
local ret=""
[ -n "$branch" ] && ret="$branch "
[ -n "$tag" ] && ret+="$tag "
[ -n "$ret" ] || ret="$(git rev-parse --short HEAD 2> /dev/null)"
local repoTopLevel="$(command git rev-parse --show-toplevel 2> /dev/null)"
local untrackedFiles=$(command git ls-files --others --exclude-standard "${repoTopLevel}" 2> /dev/null)
local modified=$(command git diff --name-only 2> /dev/null)
local staged=$(command git diff --staged --name-only 2> /dev/null)
if [ -n "$untrackedFiles" ]; then
ret+=" "
color="orange1"
fi
if [ -n "$modified" ]; then
ret+=" "
color="orange1"
fi
if [ -n "$staged" ]; then
ret+=" "
color="orange1"
fi
fi
ret="$(echo "$ret" | sed -e 's/ *$//')"
echo "$ret"
echo "$color"
}