-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
"Managing hotkeys in Linux" draft post
- Loading branch information
Showing
1 changed file
with
103 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |