I built a terminal hotkey cheatsheet
2024-02-22 • 6 mins

Summary

I discuss how and why I built my own application keyb to remember custom hotkeys in Linux.

When I first began learning Vim, I also started using tmux and a new window manager (bspwm) at the same time. In hindsight, this was a bad idea because I really struggled with remembering the correct hotkey combination for things like:

These are all very similar operations in different programs and I mixed them up ALL THE TIME.

# Remembering Hotkeys

I started looking for methods to quickly reference the hotkeys for these operations. Vim has plugins like which-key (or which-key.nvim), tmux has the builtin Ctrl+b; ? and bspwm has well... sxhkd.

# Sxhkd

sxhkd (Simple X hotkey daemon) is commonly paired with bspwm as a hotkey manager for X. It is configured with a sxhkdrc configuration file that looks like this:

# floating terminal
super + Return
	st

# tiled terminal
super + shift + Return
    st -c tiled

# rofi
super + space
	rofi -show drun -width -100

To create a quick reference sheet for sxhkd, we can parse the config file with awk:

awk '/^[a-zA-Z{]/ && last {print $0,"\t",last} {last=""} /^#/{last=$0}' \
    ~/.config/sxhkd/sxhkdrc | \
    column -t -s $'\t'

to generate a list of hotkeys with a description like this:

super + Return            # floating terminal
super + shift + Return    # tiled terminal
super + space             # rofi

We can combine this with fzf to generate a table of hotkeys with fuzzy search:

A handy sxhkd cheatsheet
A handy sxhkd cheatsheet
Full script

Use a hotkey to activate the script and you got an quick reference cheatsheet!

#!/usr/bin/env bash

awk '/^[a-zA-Z{]/ && last {print $0,"\t",last} {last=""} /^#/{last=$0}' \
    ~/.config/sxhkd/sxhkdrc | \
    column -t -s $'\t' | \
    fzf --tac --cycle \
        --layout=reverse \
        --border=rounded \
        --margin=1 \
        --padding=1 \
        --prompt='keys > ' \
		--ansi

# Towards A Global Cheatsheet

This script was really great. In fact, it was so great that I consistently tried to look up my Vim and tmux hotkeys on it as well, leading to confusion, then frustration.

What I really wanted was a global cheatsheet that could store all my hotkeys (even custom ones). Ideally, it should have fuzzy search and be presented in an easy to read format (I've been spoiled by my sxhkd + fzf script).

Looking around, I saw that awesomewm's shortcut menu comes really close to what I wanted, but there were two problems:

  1. I would be locked into awesomewm. No hate to it, but I was already using bspwm
  2. At a glance, its quite difficult to find the hotkey I want since there's no search function
awesomewm shortcut menu. [Source](https://stackoverflow.com/questions/73519361/awesome-wm-shortcut-to-toggle-or-make-a-window-sticky-this-shortcut-is-not-show)
awesomewm shortcut menu. Source

# Building my own solution

I searched extensively for any customizable hotkey reference tool but couldn't find one, so I built my own: keyb. Its built in Go and the fantastic bubbletea framework.

keyb demo
keyb demo

keyb requires you to create a hotkey file (keyb.yml) which you populate with any keybinds you want to show:

- name: bspwm
  keybinds:
    - name: terminal
      key: Super + Return

From the gif, I think you can tell the design was heavily inspired by the custom sxhkd + fzf script. All hotkeys and their descriptions are presented into a two column table, separated into sections (or groups) and delimited by their headers in bold.

The sections were inspired by awesomewm's shortcut menu and help to visually split all hotkeys by their applications/software. These section and section headers are fully customizable and how you split them is dependent on how you wrote the keyb.yml file.

This allows you to split your hotkeys in any way you like. For example, I split my bspwm and tmux hotkeys based on their purpose, and my neovim hotkeys based on their plugins.

All hotkeys related to copy and pasting in tmux are in the tmux (copy) section
All hotkeys related to copy and pasting in tmux are in the tmux (copy) section

The other key feature of keyb is its fuzzy search. There are two search modes:

This means that there are three different ways of presenting table rows: non-filtered, normal search filtered and header search filtered. These three tables had to be distinct enough that the user should know which mode they are in without getting lost, but also similar enough to maintain a common UX throughout. This was challenging to develop since I'm not a designer or UX expert.

What I went with was this:

To me, this UX seems straightforward and I don't get easily lost or forget which mode I am in. However, this modal UX is far from perfect.

# Areas of Improvement

Firstly, keyb activates header search by checking for the prefix h: string in the search bar. This proves to be quite cumbersome especially when you use this mode often. I have some ideas on how to improve it, but nothing concrete so far. My rough thoughts are here.

Secondly, it would be fantastic if we can set or rebind hotkeys using keyb as well. This is entirely outside the scope of keyb but there are some tools that do this, including xremap and keyd. One proposal I have is to allow keyb to export a configuration file for these tools with the defined keyb.yml file that's already present, allowing the user to set a single source of truth.

Lastly, keyb does not support command selection or keyboard input. Again, this is outside the scope of keyb and more suited for tools like xdotool. I also have a proposal to allow keyb to output keyboard input that can be piped into xdotool when the user selects a given row in the table.

I've been dogfooding keyb almost daily for more than a year now, so much that I rarely need to refer to it now, which was kind of the point I guess? There hasn't been major bugs or annoyances as far as I'm aware, but please feel free to open an issue if you do try it out!

Edited: 2024-02-22