Wordy Nerdy Zsh Prompt

March 09, 2012

I've been using zsh has my primary shell since attending Überconf last July in Denver Colorado. For all intents and purposes it's a superset of the venerable bash shell. I've also been using oh-my-zsh, which is a community driven framework for managing your zsh configuration.

Yesterday evening I refactored my prompt using Steve Losh's My Extravagant Zsh Prompt posting as a jumping off point. The end result is a nicely colorful, informationally dense prompt. In addition to showing me what machine I'm on, it displays the current working directory, the Ruby version, and information about source control should the working directory contain either a Git, Mercurial (hg), or Subversion (svn) repository.

A picture, or screen shot, is worth a thousand words.

Prompts

And here's an annotated image that explains all the parts and pieces.

Annotated prompts

What You'll Need

Create an oh-my-zsh theme

I copied the Soliah theme when I started with oh-my-zsh originally. Over time I've completely modified it to suit my tastes. Browse through the theme offerings and find one you like. Make a copy and give it a unique name. I named mine after my domain, or zanshin.zsh-theme.

You can grab a copy of my theme file from my dotfiles repository on Github.

The Prompt

From left to right my prompt shows me:

If there is a source control repository present at the working directory, git, hg, or svn, information about the state of the repository is shown next.

For Git repositories the prompt character changes to a ± and this information is shown:

For hg repositories the prompt character changes to a and the following information is shown:

For svn repositories the prompt character changes to a and the following information is shown:

Right Prompt

Zsh supports the idea of a right prompt and I've made use of this to show the current Ruby version. I'm using rbenv to manage my Ruby versions. The presence of a .rbenv-version file triggers changing the Ruby in effect. My right prompt queries rbenv to determine what to display.

RPROMPT='%{$fg[red]%}$(rbenv version-name)%{$reset_color%}%'

Subversion Prompt

Steve Losh's prompt works beautifully as-is for git and hg repositories. I wanted to extend my prompt to work for subversion repositories since my employer is subversion-based. I got the bulk of my svn prompt from this Landon Fuller gist.

I stripped out some of his code, paring it down to just the bare essentials to display svn information. What his prompt script didn't provide was a way to indicate whether or not the working directory had uncommitted changes. Using output from the bash prompt builder as an example I was able to add a test for uncommitted changes.

Source Code

Here's a copy of the complete zsh-theme I'm using.

# ----------------------------------------------------------------------------
# Using bits from Steve Losh
#   http://stevelosh.com/blog/2010/02/my-extravagant-zsh-prompt/
# ----------------------------------------------------------------------------

# ----------------------------------------------------------------------------
# Shows little symbol '±' if you're currently at a git repo,
#                     '☿' if you're currently at a hg repo,
#                     '⚡' if you're currently at a svn repo,
#                 and '○' all other times
# ----------------------------------------------------------------------------
function prompt_char {
    git branch >/dev/null 2>/dev/null && echo '±' && return
    hg root >/dev/null 2>/dev/null && echo '☿' && return
    svn info >/dev/null 2>/dev/null && echo '⚡' && return
    echo '○'
}

# ----------------------------------------------------------------------------
# hg prompt
# depends upon ~/Projects/hg/hg-prompt
# ----------------------------------------------------------------------------
function hg_prompt_info {
    hg prompt --angle-brackets "\
< on %{$fg[magenta]%}<branch>%{$reset_color%}>\
< at %{$fg[yellow]%}<tags|%{$reset_color%}, %{$fg[yellow]%}>%{$reset_color%}>\
%{$fg[green]%}<status|modified|unknown><update>%{$reset_color%}<
patches: <patches|join( → )|pre_applied(%{$fg[yellow]%})|post_applied(%{$reset_color%})|pre_unapplied(%{$fg_bold[black]%})|post_unapplied(%{$reset_color%})>>" 2>/dev/null
}

# ----------------------------------------------------------------------------
# svn prompt
# based on: https://gist.github.com/1156969 
# with help from: http://andrewray.me/bash-prompt-builder/index.html
# ----------------------------------------------------------------------------
function svn_prompt_info {
    # Set up defaults
    local svn_branch=""
    local svn_repository=""
    local svn_version=""
    local svn_change=""

    # only if we are in a directory that contains a .svn entry
    if [ -d ".svn" ]; then
        # query svn info and parse the results
        svn_branch=`svn info | grep '^URL:' | egrep -o '((tags|branches)/[^/]+|trunk).*' | sed -E -e 's/^(branches|tags)\///g'`
        svn_repository=`svn info | grep '^Repository Root:' | egrep -o '(http|https|file|svn|svn+ssh)/[^/]+' | egrep -o '[^/]+$'`
        svn_version=`svnversion -n`
        
        # this is the slowest test of the bunch
        change_count=`svn status | grep "?\|\!\|M\|A" | wc -l`
        if [ "$change_count" != "       0" ]; then
            svn_change=" [dirty]"
        else
            svn_change=""
        fi
        
        # show the results
        echo "%{$fg[blue]%}$svn_repository/$svn_branch @ $svn_version%{$reset_color%}%{$fg[yellow]%}$svn_change%{$reset_color%}"
        
    fi
}

# ----------------------------------------------------------------------------
# git prompt variables
# depends on using Steve Losh fork of oh-my-zsh
# ----------------------------------------------------------------------------
ZSH_THEME_GIT_PROMPT_PREFIX=" on %{$fg[blue]%}"
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%}"
ZSH_THEME_GIT_PROMPT_DIRTY="%{$fg[yellow]%} [dirty]"
ZSH_THEME_GIT_PROMPT_UNTRACKED="%{$fg[yellow]%} [untracked]"
ZSH_THEME_GIT_PROMPT_CLEAN=""

# ----------------------------------------------------------------------------
# zee prompt (ha ha)
# ----------------------------------------------------------------------------
PROMPT='
%{$fg[blue]%}%n%{$reset_color%} at %{$fg[yellow]%}%m%{$reset_color%} in %{$fg[green]%}${PWD/#$HOME/~}%b%{$reset_color%}$(hg_prompt_info)$(git_prompt_info)$(svn_prompt_info)
$(prompt_char) '

# ----------------------------------------------------------------------------
# rubies are red, and so my Ruby version is too
#----------------------------------------------------------------------------
RPROMPT='%{$fg[red]%}$(rbenv version-name)%{$reset_color%}%'
Author's profile picture

Mark H. Nichols

I am a husband, cellist, code prole, nerd, technologist, and all around good guy living and working in fly-over country. You should follow me on Twitter.