You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
3.7 KiB

5 years ago
---
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"
}
```