More Powerful Zsh Incremental History Search Using Zaw

| Comments

Zaw is a Zsh widget that works much like Emacs’s Helm or anything.el modules. I don’t use Helm or anything.el, and I don’t actually make full use of Zaw in that fashion, either. The only piece of Zaw that I do use is its excellent history search. I bound Zaw’s zaw-history function to control-r instead of the default built-in history-incremental-search-backward.

What makes Zaw’s history search better than Zsh or Bash’s built-in history search?

The default incremental history is very handy, but it is also very rigid. You can keep flipping back through all the matching commands in your history, but you only get to search for a single substring. Zaw lets you search for multiple substrings, each separated by a space.

Back in the decade when I still used Bash, there were innumerable situations where I wished that I had exactly this kind of functionality. The most common cases were probably trying to find an rsync or scp command with a particular server as the target. With the default incremental search, the best you could do was search for the server name or the rsync command, and then flip through results until you found the one you were looking for.

Sometimes you luck out, and it is only a few entries away. Other times, you end up tapping that control-r over and over for what seems like eternity.

Zaw is a bit too visually intrusive

With its default settings, Zaw takes up quite a bit of vertical terminal real estate. If you primarily operate in standard size 80x24 terminals-like I do, Zaw will end up pushing almost everything up and off of your screen. I ended up limiting mine to just three lines. This way, it isn’t nearly as intrusive, and I still get a bit more information than I would from the default incremental history search.

“Better” keys for moving through search results

I needed to bind a few extra keys to make Zaw behave more like the function it was replacing. I bound control-r to Zaw’s down-line-or-history. This is intuitively backwards, but it perfectly fits my muscle memory.

I also needed a key to complement control-r for moving in the opposite direction. Since control-s is usually bound to history-incremental-search-forward, I thought it would be reasonable to use control-s for moving in the opposite direction.

Editing the command before executing

This is another situation where I had to appease my muscle memory. When using the default incremental search, I always hit control-e to accept the result and move the cursor to the end of the line. By default, the same action with Zaw is accomplished by hitting alt-enter.

I’m just too old to retrain myself. I ended up binding control-e to Zaw’s accept-search command.

source $HOME/.zprezto/modules/zaw/external/zaw.zsh
bindkey '^R' zaw-history
bindkey -M filterselect '^R' down-line-or-history
bindkey -M filterselect '^S' up-line-or-history
bindkey -M filterselect '^E' accept-search

zstyle ':filter-select:highlight' matched fg=green
zstyle ':filter-select' max-lines 3
zstyle ':filter-select' case-insensitive yes # enable case-insensitive 
zstyle ':filter-select' extended-search yes # see below