Skip to content

Commit

Permalink
"Managing hotkeys in Linux" draft post
Browse files Browse the repository at this point in the history
  • Loading branch information
kencx committed Feb 20, 2024
1 parent 9560528 commit 4dcbc3c
Showing 1 changed file with 103 additions and 117 deletions.
220 changes: 103 additions & 117 deletions content/posts/keybinds/index.md
Original file line number Diff line number Diff line change
@@ -1,158 +1,144 @@
---
title: "keyb - A hotkey cheatsheet in the terminal"
date: 2023-07-12T00:18:00+08:00
lastmod: 2023-07-12
title: "Managing hotkeys in Linux"
date: 2024-02-20T21:37:00+08:00
lastmod:
draft: true
toc: true
tags:
- projects
- hotkeys
---

[`keyb`](https://github.com/kencx/keyb) is a lightweight hotkey cheatsheet in
the terminal. It can create and view custom hotkey cheatsheets in a nice TUI.
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:

{{< figure src="keyb.gif" caption="keyb demo" class="center" >}}

It is *fully* customizable[^1], supports fuzzy filtering, vim keybindings and
its output can be exported to fzf or rofi.

It does not support:

- Auto-detection of key bindings for applications
- Setting of key bindings for applications
- Command selection

## Motivation
- Resizing a window in bspwm
- Resizing a pane in tmux
- Creating a new split in vim
- Creating a new pane/window in tmux

I built `keyb` because I often have trouble remembering hotkeys, and wanted a
quick, no-nonsense way to list and search through my hotkeys. It would also be
good if it was possible to fulfil the following criteria:
If you used any combination of window manager, text editor and terminal
multiplexer before, I think you can see where I'm going. These are all very
similar operations in different programs and I mixed up the hotkeys for them ALL
THE TIME.

- Contain ALL my hotkeys, regardless of the application or software
- Support fuzzy-finding
- Be quickly accessible via a custom hotkey
## Remembering Hotkeys

There wasn't any such tool that I could find, but two came close and heavily
influenced the UX of `keyb`:
Naturally, I looked for methods to quickly reference these hotkey combinations
in a cheatsheet. Vim has
plugins like [which-key](https://github.com/liuchengxu/vim-which-key) (or
[which-key.nvim](https://github.com/folke/which-key.nvim)), tmux has the builtin
`Ctrl+b; ?` and bspwm has well... sxhkd which isn't really helpful by itself.

- [awesomewm](https://awesomewm.org/)'s shortcut menu
- A [custom
script](https://github.com/kencx/dotfiles/blob/master/dots/bin/bin/kbinds)
that uses `fzf` to parse `sxhkd` bindings (this was what I went with before
building `keyb`)
### 1. Sxhkd

awesomewm's shortcut menu came really close to what I really wanted. The only
problem was that I was locked into using awesomewm, which was a dealbreaker for
me. I mean, no hate for awesomewm but I just prefer bspwm.

{{< figure src="awesomewm-shortcut-menu.png" caption="awesomewm shortcut menu. [Source](https://stackoverflow.com/questions/73519361/awesome-wm-shortcut-to-toggle-or-make-a-window-sticky-this-shortcut-is-not-show)" class="center" >}}
I set out to create my own sxhkd reference. The `sxhkdrc` configuration file
looks like this:

The alternative was a custom script that utilized `fzf` to parse my `bspwm` and
`sxhkd` bindings. This was good but I wanted to include bindings for other
software like neovim and tmux.
```bash
# floating terminal
super + Return
st

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

If you go back to the demo gif, you can see that `keyb` borrows heavily from
`fzf`'s style, but it presents the hotkeys in a table with headings and
two columns - the description and the actual keybind.
# rofi
super + space
rofi -show drun -width -100
```

The headings help to visually split all hotkeys by their application, and was
influenced by awesomewm's shortcut menu. These headings are fully customizable
and how you split them is dependent on how you organized your `keyb`
configuration file (more on that later).
which can be parsed with `awk`:

The fuzzy filtering is similar to that in `fzf` but it offers two modes: normal
and heading mode. Normal filtering simply filters all rows except the headings,
while heading filtering filters for heading rows only. Heading mode is activated
by prefixing the search input with `h:`.
```bash
awk '/^[a-zA-Z{]/ && last {print $0,"\t",last} {last=""} /^#/{last=$0}' \
~/.config/sxhkd/sxhkdrc | column -t -s $'\t'
```

> For my issues with heading mode, see [here](#heading-mode).
to generate a list of hotkeys with a description:

## Configuration
```bash
super + Return # floating terminal
super + shift + Return # tiled terminal
super + space # rofi
```

`keyb` works by listing all your hotkeys in a `keyb.yml` file. Hotkeys are
classified into sections with a name and an (optional) prefix field.
This can be paired with `fzf` to generate a nice table of hotkeys with a fuzzy
search:

```yml
- name: bspwm
keybinds:
- name: terminal
key: Super + Return
```bash
fzf --tac --cycle \
--layout=reverse \
--border=rounded \
--margin=1 \
--padding=1 \
--prompt='keys > ' \
--ansi
```

This name goes to become the heading's name. The prefix is a key combination
that is prepended to every hotkey in that section. This is useful for specific
applications with a common leading hotkey like `tmux`:

```yml
- name: tmux
prefix: ctrl + b
keybinds:
- name: Create new window
key: c
- name: Prev, next window
key: Shift + {←, →}
ignore_prefix: true
```
Combine the above into a script and assign it a hotkey and you get a quick sxhkd
cheatsheet!

The configurability of the `keyb.yml` file allows you to list any application's
hotkeys with any custom keybinds you want. You can even split an application's
hotkeys into different groups or modes - normal, insert, visual mode in vim.
### 2. Towards A Global Cheatsheet

> See my
> [keyb.yml](https://github.com/kencx/dotfiles/blob/master/dots/keyb/.config/keyb/custom.yml)
> for some examples.
These reference sheets are great but I find it annoying to switch between 3
different cheatsheets.

## Improvements
I wanted a cheatsheet with the following features:

While I am happy with the state of `keyb` now, I do have a few ideas for improving it.
- All hotkeys, regardless of application or software
- Fuzzy search
- Quickly accessible via a hotkey

### Heading Mode
During research, [awesomewm](https://awesomewm.org/)'s shortcut menu comes
really close to what I really wanted. The only problem was that it involved
being locked into awesomewm, which wasn't great.

Personally, I find heading mode cumbersome to activate, especially when I use it
so frequently. Naturally, [others](https://github.com/kencx/keyb/issues/16)
agree with this. However, I'm struggling to find a good method to combine
filtering through headings and the normal rows due to a few issues:
{{< figure src="awesomewm-shortcut-menu.png" caption="awesomewm shortcut menu. [Source](https://stackoverflow.com/questions/73519361/awesome-wm-shortcut-to-toggle-or-make-a-window-sticky-this-shortcut-is-not-show)" class="center" >}}

- How should we prioritize the search results when there exists a heading
and a description with the same text? It doesn't make sense to only show the
heading row without its constituents. But, if the specific heading has a lot of
constituent rows, it would drown out the rest of the filtered descriptions.
- It would be good if we can toggle between combined filtering and
filtering only non-headings rows.
- It would also be good to implement filtering within the filtered sub-table.
### 3. Building my own solution: keyb

The best idea I have now would be to implement a truncated sub-table that can be
expanded into its own view, which can be filtered.
I built [keyb](https://github.com/kencx/keyb) to fulfil my need for a global
cheatsheet.

### Command Selection and Remapping
It can be used to create and view custom hotkey cheatsheets in a TUI. `keyb` is
built in Go, with the
[bubbletea](https://github.com/charmbracelet/bubbletea/tree/master) framework.

I recently discovered [xdotool](https://github.com/jordansissel/xdotool) and
[xremap](https://github.com/k0kubun/xremap) which can be used to perform command
selection and remapping respectively. I think it would be pretty cool if `keyb`
could be extended to work with these tools.
{{< figure src="keyb.gif" caption="keyb demo" class="center" >}}

`xremap` reads a specific `config.yml` to remap hotkeys. Something like this
could probably be implemented:
Features:
- fully customizable
- supports fuzzy filtering
- vim keybindings
- output can be exported to fzf or rofi

```bash
$ keyb --export-xremap | xremap
```
Non-features:
- Auto-detection of key bindings for applications
- Setting of key bindings for applications
- Command selection

`xdotool` can simulate keystrokes from stdin. This input could be easily piped
from `keyb` when a specific keybind is selected:
Its design is heavily inspired by `fzf`, but it presents the hotkeys in a table with headings and two columns - the description and the actual hotkey combination

```bash
$ keyb | xdotool key
```
<!-- Comparison image between fzf and keyb -->

### Reading Config from a Directory
The headings help to visually split all hotkeys by their application, and was
influenced by awesomewm's shortcut menu. These headings are fully customizable
and how you split them is dependent on how you wrote your `keyb` configuration
file.

The fuzzy filtering is similar to that in `fzf` but it offers two modes: normal
and heading mode. Normal filtering simply filters all rows except the headings,
while heading filtering filters for heading rows only. Heading mode is activated
by prefixing the search input with `h:`. However, a modal UX proves to be quite
cumbersome. My rough thoughts on how it can be improved are
[here](https://github.com/kencx/keyb/issues/16), although I have not implemented
any changes yet.

I found my `keyb.yml` to be getting a little long and it would be great if it
could be split into multiple files. This feature seems easy enough and I just
need to find some time to implement this.
## Running Hotkeys

## Remapping Hotkeys

[^1]: Feel free to [open an issue](https://github.com/kencx/keyb/issues) if you
feel there's something that should be customized that isn't
## References

0 comments on commit 4dcbc3c

Please sign in to comment.