Wordy Nerdy Zsh Prompt

| posted in: nerdliness 


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%}%'
{% endhighlight%}

##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](https://github.com/landonf "Landon Fuller") gist.

{% gist 1156969 %}

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](http://andrewray.me/bash-prompt-builder/index.html "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.

<div class="highlight"><pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>{% raw %}
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># Using bits from Steve Losh</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">#	http://stevelosh.com/blog/2010/02/my-extravagant-zsh-prompt/</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># Shows little symbol &#39;±&#39; if you&#39;re currently at a git repo,</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">#                     &#39;☿&#39; if you&#39;re currently at a hg repo,</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">#                     &#39;⚡&#39; if you&#39;re currently at a svn repo,</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">#                 and &#39;○&#39; all other times</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">function</span> prompt_char {
</span></span><span style="display:flex;"><span>    git branch &gt;/dev/null 2&gt;/dev/null &amp;&amp; <span style="color:#24909d">echo</span> <span style="color:#ed9d13">&#39;±&#39;</span> &amp;&amp; <span style="color:#6ab825;font-weight:bold">return</span>
</span></span><span style="display:flex;"><span>    hg root &gt;/dev/null 2&gt;/dev/null &amp;&amp; <span style="color:#24909d">echo</span> <span style="color:#ed9d13">&#39;☿&#39;</span> &amp;&amp; <span style="color:#6ab825;font-weight:bold">return</span>
</span></span><span style="display:flex;"><span>	svn info &gt;/dev/null 2&gt;/dev/null &amp;&amp; <span style="color:#24909d">echo</span> <span style="color:#ed9d13">&#39;⚡&#39;</span> &amp;&amp; <span style="color:#6ab825;font-weight:bold">return</span>
</span></span><span style="display:flex;"><span>    <span style="color:#24909d">echo</span> <span style="color:#ed9d13">&#39;○&#39;</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># hg prompt</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># depends upon ~/Projects/hg/hg-prompt</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">function</span> hg_prompt_info {
</span></span><span style="display:flex;"><span>    hg prompt --angle-brackets <span style="color:#ed9d13">&#34;\
</span></span></span><span style="display:flex;"><span><span style="color:#ed9d13">&lt; on %{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[magenta]%}&lt;branch&gt;%{</span><span style="color:#40ffff">$reset_color</span><span style="color:#ed9d13">%}&gt;\
</span></span></span><span style="display:flex;"><span><span style="color:#ed9d13">&lt; at %{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[yellow]%}&lt;tags|%{</span><span style="color:#40ffff">$reset_color</span><span style="color:#ed9d13">%}, %{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[yellow]%}&gt;%{</span><span style="color:#40ffff">$reset_color</span><span style="color:#ed9d13">%}&gt;\
</span></span></span><span style="display:flex;"><span><span style="color:#ed9d13">%{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[green]%}&lt;status|modified|unknown&gt;&lt;update&gt;%{</span><span style="color:#40ffff">$reset_color</span><span style="color:#ed9d13">%}&lt;
</span></span></span><span style="display:flex;"><span><span style="color:#ed9d13">patches: &lt;patches|join( → )|pre_applied(%{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[yellow]%})|post_applied(%{</span><span style="color:#40ffff">$reset_color</span><span style="color:#ed9d13">%})|pre_unapplied(%{</span><span style="color:#40ffff">$fg_bold</span><span style="color:#ed9d13">[black]%})|post_unapplied(%{</span><span style="color:#40ffff">$reset_color</span><span style="color:#ed9d13">%})&gt;&gt;&#34;</span> 2&gt;/dev/null
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># svn prompt</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># based on: https://gist.github.com/1156969 </span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># with help from: http://andrewray.me/bash-prompt-builder/index.html</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#6ab825;font-weight:bold">function</span> svn_prompt_info {
</span></span><span style="display:flex;"><span>	<span style="color:#999;font-style:italic"># Set up defaults</span>
</span></span><span style="display:flex;"><span>	<span style="color:#24909d">local</span> <span style="color:#40ffff">svn_branch</span>=<span style="color:#ed9d13">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#24909d">local</span> <span style="color:#40ffff">svn_repository</span>=<span style="color:#ed9d13">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#24909d">local</span> <span style="color:#40ffff">svn_version</span>=<span style="color:#ed9d13">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#24909d">local</span> <span style="color:#40ffff">svn_change</span>=<span style="color:#ed9d13">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#999;font-style:italic"># only if we are in a directory that contains a .svn entry</span>
</span></span><span style="display:flex;"><span>	<span style="color:#6ab825;font-weight:bold">if</span> [ -d <span style="color:#ed9d13">&#34;.svn&#34;</span> ]; <span style="color:#6ab825;font-weight:bold">then</span>
</span></span><span style="display:flex;"><span>		<span style="color:#999;font-style:italic"># query svn info and parse the results</span>
</span></span><span style="display:flex;"><span>		<span style="color:#40ffff">svn_branch</span>=<span style="color:#ed9d13">`</span>svn info | grep <span style="color:#ed9d13">&#39;^URL:&#39;</span> | egrep -o <span style="color:#ed9d13">&#39;((tags|branches)/[^/]+|trunk).*&#39;</span> | sed -E -e <span style="color:#ed9d13">&#39;s/^(branches|tags)\///g&#39;</span><span style="color:#ed9d13">`</span>
</span></span><span style="display:flex;"><span>		<span style="color:#40ffff">svn_repository</span>=<span style="color:#ed9d13">`</span>svn info | grep <span style="color:#ed9d13">&#39;^Repository Root:&#39;</span> | egrep -o <span style="color:#ed9d13">&#39;(http|https|file|svn|svn+ssh)/[^/]+&#39;</span> | egrep -o <span style="color:#ed9d13">&#39;[^/]+$&#39;</span><span style="color:#ed9d13">`</span>
</span></span><span style="display:flex;"><span>		<span style="color:#40ffff">svn_version</span>=<span style="color:#ed9d13">`</span>svnversion -n<span style="color:#ed9d13">`</span>
</span></span><span style="display:flex;"><span>		
</span></span><span style="display:flex;"><span>		<span style="color:#999;font-style:italic"># this is the slowest test of the bunch</span>
</span></span><span style="display:flex;"><span>		<span style="color:#40ffff">change_count</span>=<span style="color:#ed9d13">`</span>svn status | grep <span style="color:#ed9d13">&#34;?\|\!\|M\|A&#34;</span> | wc -l<span style="color:#ed9d13">`</span>
</span></span><span style="display:flex;"><span>		<span style="color:#6ab825;font-weight:bold">if</span> [ <span style="color:#ed9d13">&#34;</span><span style="color:#40ffff">$change_count</span><span style="color:#ed9d13">&#34;</span> != <span style="color:#ed9d13">&#34;       0&#34;</span> ]; <span style="color:#6ab825;font-weight:bold">then</span>
</span></span><span style="display:flex;"><span>			<span style="color:#40ffff">svn_change</span>=<span style="color:#ed9d13">&#34; [dirty]&#34;</span>
</span></span><span style="display:flex;"><span>		<span style="color:#6ab825;font-weight:bold">else</span>
</span></span><span style="display:flex;"><span>			<span style="color:#40ffff">svn_change</span>=<span style="color:#ed9d13">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>		<span style="color:#6ab825;font-weight:bold">fi</span>
</span></span><span style="display:flex;"><span>		
</span></span><span style="display:flex;"><span>		<span style="color:#999;font-style:italic"># show the results</span>
</span></span><span style="display:flex;"><span>		<span style="color:#24909d">echo</span> <span style="color:#ed9d13">&#34;%{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[blue]%}</span><span style="color:#40ffff">$svn_repository</span><span style="color:#ed9d13">/</span><span style="color:#40ffff">$svn_branch</span><span style="color:#ed9d13"> @ </span><span style="color:#40ffff">$svn_version</span><span style="color:#ed9d13">%{</span><span style="color:#40ffff">$reset_color</span><span style="color:#ed9d13">%}%{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[yellow]%}</span><span style="color:#40ffff">$svn_change</span><span style="color:#ed9d13">%{</span><span style="color:#40ffff">$reset_color</span><span style="color:#ed9d13">%}&#34;</span>
</span></span><span style="display:flex;"><span>		
</span></span><span style="display:flex;"><span>	<span style="color:#6ab825;font-weight:bold">fi</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># git prompt variables</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># depends on using Steve Losh fork of oh-my-zsh</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#40ffff">ZSH_THEME_GIT_PROMPT_PREFIX</span>=<span style="color:#ed9d13">&#34; on %{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[blue]%}&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#40ffff">ZSH_THEME_GIT_PROMPT_SUFFIX</span>=<span style="color:#ed9d13">&#34;%{</span><span style="color:#40ffff">$reset_color</span><span style="color:#ed9d13">%}&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#40ffff">ZSH_THEME_GIT_PROMPT_DIRTY</span>=<span style="color:#ed9d13">&#34;%{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[yellow]%} [dirty]&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#40ffff">ZSH_THEME_GIT_PROMPT_UNTRACKED</span>=<span style="color:#ed9d13">&#34;%{</span><span style="color:#40ffff">$fg</span><span style="color:#ed9d13">[yellow]%} [untracked]&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#40ffff">ZSH_THEME_GIT_PROMPT_CLEAN</span>=<span style="color:#ed9d13">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># zee prompt (ha ha)</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#40ffff">PROMPT</span>=<span style="color:#ed9d13">&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#ed9d13">%{$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)
</span></span></span><span style="display:flex;"><span><span style="color:#ed9d13">$(prompt_char) &#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># ----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic"># rubies are red, and so my Ruby version is too</span>
</span></span><span style="display:flex;"><span><span style="color:#999;font-style:italic">#----------------------------------------------------------------------------</span>
</span></span><span style="display:flex;"><span><span style="color:#40ffff">RPROMPT</span>=<span style="color:#ed9d13">&#39;%{$fg[red]%}$(rbenv version-name)%{$reset_color%}%&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{% endraw %}</span></span></code></pre></div>
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.