Switching to Starship

A long time ago, I started experimenting with customising my Bash shell prompt. Rather than just showing the current working directory, I wanted to add snippets of useful information to my prompt. I started with displaying the currently active branch of a Git repository. Next I added code to show which version of a particular programming language was selected.

I continued making improvements to only show the relevant information at the right time, for example hiding the Git branch if I wasn’t currently in a Git repository. and the same with the language version. This stopped the prompt becoming too large and cumbersome.

Problems Arise

Over the years I added more code to this custom prompt, as new technologies were added to my toolbox, however problems started to show.

This custom prompt code isn’t just executed when you open a new tab, it’s run every time the prompt has to display. So the amount of code you put in there has an impact on how quickly your prompt will display. Over the years, it had become so bloated that it was now taking several seconds to execute, and remember this was happening every time the prompt needed to be displayed.

Let’s just say this Bash script had evolved very … organically … and making changes to it was becoming more difficult. Some of the more “artisanal” parts from the early days had become almost undecipherable.

Refactor

My initial fixes were to go through the oldest parts of the prompt code involved janky functions for setting custom colours, and were needlessly defining variables all over the place. Some other areas were invoking commands in subshells and capturing the output, in order to use it later, when in reality they always returned the same value, and were just wasting cycles.

I ripped all of this out to reduce the amount of code being executed, removing code defining colours I never used, and replacing subshells with constants defined at the top of the script. I also looked into the performance of things like rbenv and pyenv to see if they could be sped up.

Performance got a lot better, but it was still taking around a second or so. Satisfied for the time being, I made a note to look into alternatives in the near future.

Board the Starship

A friend of mine had previously recommended a cross-shell prompt called Starship which is written in Rust, but my initial experiments hadn’t been very successful. Given Rust programs have a reputation for being blazing fast, I thought it was time to see how the project had progressed, and maybe give it another try.

I’m happy to say this time, I was able to get Starship to do everything I wanted.

Install

I followed the official documentation to install Starship via Homebrew:

brew install starship

I did not opt to install their “Nerd Font” as this is only needed if you want to make use of the various icons and logos in your prompt, and I wanted to keep things simple.

Next step was to disable my old custom prompt code, and replace it with this:

export STARSHIP_CONFIG=~/.starship.toml
eval "$(starship init bash)"

Notice I have set a custom configuration file location, this is purely because I already have a system for synchronising my config files between systems, and the default location wasn’t suitable. You can probably skip that step.

Restarting the shell I was greeted with my new super fast prompt!

Configuration

The default prompt configuration tries to cover all the bases, but I found it too verbose, so I took a look at the configuration options.

Firstly, I wanted to limit what was displayed to a few key pieces of information, rather than have every supported option jostling for position. I wanted to have the prompt on a single line, and show only the current working directory, Ruby and Python information, the current Git branch, and finally the prompt character:

# ~/.starship.toml
add_newline = false
format = """
$directory\
$ruby\
$python\
$git_branch\
$character
"""

Next I wanted to customise the sections I had enabled so they didn’t show a symbol, and were all bracketed, so each section would be distinct, yet compact. I started by using the Starship presets and then made my modifications on top, resulting in this extra config:

# ~/.starship.toml
[git_branch]
format = '\[[$symbol$branch]($style)\]'
style = 'bold white'
symbol = ''

[python]
format = '\[[${symbol}${pyenv_prefix}(${version})(\($virtualenv\))]($style)\]'
symbol = ''

[ruby]
format = '\[[$symbol($version)]($style)\]'
symbol = ''

Finally I wanted to make the directory section better match the others, by bracketing it, and making it show the full file path for clarity:

# ~/.starship.toml
[directory]
truncation_length = 0
truncate_to_repo = false
format = '\[[$path]($style)[$read_only]($read_only_style)\]'

You can see all the options for the prompt sections I have used below:

Result

The end result looks exactly the same as my old custom prompt did, which is a testament to the customisability of Starship. The performance difference is striking. Bash now starts almost instantly, and my prompt returns so quickly it’s almost imperceptible.

I’m a big proponent of making time to address seemingly small issues like these. They have a habit of building up over time, until every interaction you’re having with your computer is like drying yourself with sandpaper.

I’m very happy I took another look at Starship, and can finally tick this off my todo list.