--- title: Building a ZSH prompt tags: - Linux - ZSH description: There are many frameworks out there to help you build a prompt, I thought I'd see how far I could get without one. date: 2020-03-24 draft: true --- Until recently, I was using [oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh) with the [Power Level 9k](https://github.com/Powerlevel9k/powerlevel9k) 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](https://github.com/Jab2870/dotfiles/blob/master/shells/zsh/includes/promptconfig.zsh). My prompt currently looks something like this: ![My Prompt](/assets/prompt/prompt.png) ## 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. ```zsh 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](https://github.com/powerline/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](https://www.nerdfonts.com/) 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. ```bash # 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: ```bash 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" } ```