simulacrum party

Switching To Fish

I've been using zsh and Oh My Zsh for a few years now. It was a marked improvement to bash, and I was really excited about it at first. Its tab completions are much better (and extensible when they aren't), it's more easily configurable, and there's a plugin and theme community around Oh My Zsh that make it easy to have a good looking terminal that behaves just how you want it to.

Lately though, I've been noticing that opening a new shell is slow. I often find myself opening a tmux split just to run a quick command before closing it, and with my zsh setup, opening that split would take about five seconds. I tried paring down my .zshrc, figuring that some plugin or other was slowing it down. Ultimately, I realized that it was Oh My Zsh itself that was responsible for the majority of the startup time. I disabled it and found that the custom prompt I'd painstakingly configured in my .zshrc had no effect in vanilla zsh. Rather than trying to start from the ground up without the configuration helpers OMZ provides, I figured I'd give fish a shot.

Fish (Friendly Interactive SHell) is a project that I've been aware of for a long time but I've been happy with zsh so I didn't pay too much attention. I'd heard good things though so I brew install fished (I should really start making sure that I'm installing the right thing but life is too short to brew info) and found that it booted in mere milliseconds. So far so good! As long as it's fast I'm on board.

Probably the first thing you notice about fish is that everything is syntax-highlighted as you type:

Example of fish's syntax highlighting

Considering how long and complex shell pipelines can get, having highlighting to visually break it up into its component parts is fantastic. It's possible to disable it if you don't like it, but I think it's great. Unfortunately, fish can't simply let iTerm handle colors, which is how I had configured zsh, but it's easy enough to modify them however you want. Running fish_config launches a web interface where you can edit these colors:

Fish's color editor

The other thing that jumped out at me is fish's autosuggestion feature. As you type, fish compares what you've entered to your history, relevant options for the program you're invoking, and filepaths, and suggests commands it thinks you might be interested in. This is related to zsh's ability to partially type a command and then up-arrow through commands that match, but more powerful and more semantically aware.

An example of fish showing an autosuggestion

To accept the suggestion, simply press , or alt-→ to accept the first word of the suggestion. I think not using tab to accept the suggestion is a little unintuitive, but since it might suggest a command from your history that you want to ignore and instead tab complete filepaths or program flags, it makes sense that it has to be different. What's most striking to me about this feature is that it's fast, which I was honestly not expecting.

While the first thing I did after switching to zsh was install Oh My Zsh, fish looks nice and works well right out of the box. I installed the Fisher package manager for installing plugins but I don't feel the need to install something as big as Oh My Zsh. Configuring fish is pleasant enough that I'm happy to do it myself without the help of another tool. Happily, even after installing a few plugins to get back to the level of functionality I had with Oh My Zsh, I haven't noticed a decrease in speed.

However, Fish's configuration is a little daunting coming from zsh, where, oftentimes anyway, you just throw everything in .zshrc and call it a day. Fish's configuration is stored in ~/.config/fish in numerous files across several subdirectories and everything is kept well separated. For example, when you create an alias, fish creates a file called functions/<alias>.fish and writes a function that performs the alias to that file. The prompt is configured via a function called fish_prompt in functions/fish_prompt.fish, the return value of which becomes your prompt. There's also a fish_variables file where you can set colors and so on, and you can use config.fish for other miscellaneous configuration. This is a lot more complicated than I expected but I think it's a nice system overall. Keeping everything separate makes it feel a lot less cluttered, and I think it's easier to find what I'm looking for than in a big .zshrc.

In general fish feels a lot cleaner than zsh. Fish's design goals note that fish should follow the POSIX standard unless doing so would negatively impact the user experience, and it's obvious that the developers are serious aboout UX. For instance, fish's $PATH is a list, rather than a colon-delimited string, which makes sense! Fish's if and switch statements thankfully both end with end rather than bash's fi and esac. There's a general air of freshness and conscious design about fish and I appreciate its attempt to make a better shell experience rather than adhering to unintuitive or user-hostile standards.

However, this non-compliance can make things difficult as well. I use RVM and NVM, and neither of them can run natively under fish. Luckily, there are fish-specifc versions of both tools, but I expect that I'll eventually encounter something that doesn't work with fish at all. For now, it's worth it, and if I do have to write my own fish wrapper script, at least the language is nicer than bash.

Another con is that, while slick, the web interface for editing configuration files is not as powerful as I'd like. For example, the color picker up above only allows you to choose from those 256 colors. If you want to enter a hex code, you have to open up the file in an editor. There's a tab where you can choose from a preset group of prompts but you can't edit the prompt manually. The tabs for variables and key bindings appear to be entirely read-only. I'm not sure why exactly, but it's all editable elsewhere anyway.

Overall, I've only been using fish for a couple of days but I'm enjoying it a lot so far. When I open a tmux pane and it loads instantaneously I can't believe how long I put up with a multi-second wait. There are a lot of shell features I don't use, and I don't do a lot of shell scripting - maybe if I was a sysadmin or something I would feel more strongly about the ways in which fish differs from POSIX - but fish is a great option for me and I'm happy I gave it a shot.