Following the “7 bash tricks“, I’m now using ZSH as my main shell. The differences that I noticed from bash so far:
- Way smarter completion system.
- A transient rprompt showing information that isn’t important for scrollback and otherwise would only waste space.
- Zsh is more responsive/fast for me. I think the problem in my bash configuration was the git completion/info stuff, but Zsh provides a non-slow git completion/info alternative, then I’ll cast a vote for zsh. Although there are some users installing git modules more expensive in their zsh shells.
- A prompt char whose background color will turn red if the last command returned non-zero. And the exit status will also be printed on the output if non-zero.
- There are more supported Emacs key bindings. And they are easily customizable, then I can mimic some bash and Emacs behaviours.
Regarding the usage, Zsh and Bash aren’t too different and it’d be difficult to tell which shell is which. I type the same commands and I see (mostly) the same output. The svn to git change was way more significant to me than the bash to zsh change. Another recent change of habit was the Solarized’s colorscheme adoption (not shown in this page’s outdated gifs).
There are lots of users adopting some configuration framework like oh-my-zsh, zshuery or prezto. From the usual “look my oh-my-zsh’s based zsh” posts that I’ve read… I think these users like to configure tons of plugins… but I don’t and I don’t see much benefit of using one of these configuration frameworks. In fact, I like an initially minimalist and clean configuration and this is how I maintain my ArchLinux setup. But the main benefit of the framework-free setup is the self-contained nature. The only dependency is zsh itself.
My 239-lines zshrc config file is available online on my git repo and there isn’t much magic there. In this post I’ll only explain the small amount of magic there.
The bash’s Ctrl+C behaviour
When you press Ctrl + C in bash, the shell “abort” the edition of current line and let you try again from the beginning. The difference of behaviour in bash and zsh is that bash will also print a nice “^C” string, then you can know if the previous lines were actually executed or not. I wanted this behaviour in zsh.
I found out that zsh handle stty in a different way and you’d need to caught SIGINT to print the “^C” string. But zsh is customizable and it allow you to easily trap the INT signal.
This is trivial and far from the “magic” concept I wrote at the beginning of the post, but it’ll get complicated soon. Moving on…
The reddish background of the prompt char
After seeing so many esoteric zsh prompts, I started to ponder how I’d want my own prompt. I decided I wanted something minimalist, showing only important information and only when it is interesting. This is why I use relative paths on the prompt and the hostname is omitted if I’m not acessing the terminal from a ssh connection. But all those prompt demos from other zsh users made me wonder how useful a reddish color alert would be. And it would be possible to put the red color on the background of the prompt char, then I wouldn’t waste any extra space.
A simple way to set the prompt the way I want would be like this:
And it works, but there is one small problem. If I hit Ctrl+C, a non-zero status will be returned and the red background will be triggered, tricking me to think something bad happened. To “fix” the behaviour, I decided to add a “_trapped” variable that would would work like a masking, allowing or banning the red background. Then I need to change the previously created TRAPINT function to ban the red background and add a hook before the execution of new commands to allow the red background. The configuration ended up like this:
This change also has the nice side effect of turning the red background off easily by pressing Ctrl + C. The “updatemyprompt” function needs to be called from “TRAPINT”, because the “precmd” function isn’t always called before the prompt is redraw (see manpages). I’d prefer to control the output of the prompt var through something like the “prompt_subst” option, but I haven’t figured out a pretty way to conditionally show a string using zsh’s command and parameter expansion based on the “_trapped” value (yet!). And when I learn a prettier way to control the “_trapped” mask, I’ll still need the “precmd” function, because this is required for the “vcs_info” module.
The above code/config wasn’t the final one (vcs_info’s config is missing) and let’s stop here, because there is no more magic to show. The code end up being ugly, with responsibilities not well defined and error-prone communication system (a single global variable caused such a big damage here). The upside is that this is my shell, it does what I want and therefore I won’t change it. Also, in fact, my real zsh config uses anonymous function to leak less variables and avoid unnecessary naming clash.
Custom keys detection
It turns out that terminals are hard. There are escape sequences that were created to interact with the terminal. The interaction would allow applications move the cursor around and detect certain key combinations done by the user. Unfortunately, there are several protocols and there is no universal way to access these features. To register some fancy key bindings, zsh wiki and several online pages suggest you to use terminfo or zkbd.
The zkbd method requires an annoying manual intervention on the first start-up and it would be desired as a fallback mode only.
The terminfo mode will only work under application mode and the zsh wiki suggest you to activate the application mode on the beginning of the line edition and deactivate it on the end, before the execution of other applications. The problem appears when the terminal doesn’t support application mode. Fortunately, there is a a way to check if the underlying terminal supports application mode, then you can you can even add a nicely integrated zkbd fallback mode. Below there is an initial snippet to configure zsh accordingly.
Note that I initially avoided the terminfo all together because I thought the lack of info around “go to application mode” too scary, resembling some “error-prone thing that will break”. I needed to understand that zsh would leave application mode before execute other applications and it would only enter in application mode if supported. I learned all these bits thanks to comments in this prezto’s pull request.