Text Manipulation and Modifier Keys

About 3 years ago I switched my primary OS from MacOS back to Linux. I hadn't run it as a desktop OS since college and have memories of updates breaking my system, battling with Xorg configs, and slow, bloated DEs. However, this time around it has been a very positive and painless experience. It's been more stable than MacOS (despite using a rolling release distribution). And now that I'm running a Wayland compositor (Sway), I don't have any screen tearing or need to deal with Xorg configuration anymore. But the feature I've missed most from MacOS is having readline style bindings in all text inputs uniformly across the system.

Readline bindings

GNU Readline is a library that enables a program to read a line of text from the user, and allow basic in-line editing. It is commonly used by shells and REPLs for input handling. A big advantage for using a single library for this is it enables consistent keyboard shortcuts for text manipulation across all programs that use it. Some of the more useful shortcuts are below:

A good way to help remember the last 8 is that the Ctrl modifier operates on letters while the Alt modifier operates on words. (Ctrl-W probably should have been Alt-H for consistency since Ctrl-H deletes backward one character).

MacOS Modifiers

On MacOS, many of these bindings simply work out of the box on Cocoa text inputs. The rest are trivially enabled by creating the file ~/Library/KeyBindings/DefaultKeyBinding.dict and specifying the missing shortcuts.

I created a repo with the missing readline bindings defined that enables them after a simple git clone and a logout. Back when I ran MacOS, cloning this was always one of the first things I would do on a clean install.

This made text manipulation a breeze. For example, say I want to navigate to github.com/swaywm/sway. So I open my browser, press Cmd+L to select the URL, and start typing the URL. But suppose as I start typing the first few characters, the browser suggests github.com/alexdavid/sigma. I jump the cursor to the end of the URL with Ctrl-E, press Ctrl-W twice to delete the project & username, and start typing sw to let the browser auto-complete the rest and press enter.

The entire process takes about 9 keystrokes and is such a habit I am able to do it in under a second. There's no need to move my hand over to the mouse and take the time to precisely click and drag over the text I want to change. There's no need for me to move my hand off the home row to the arrow keys to try to manipulate the text that way. But most importantly, there's no need to remember a completely different set of bindings for manipulating text whether I'm entering a line of text in Firefox, or entering a command into a shell, python REPL, sqlite, etc.

Why Readline Over Vim Bindings?

Vim is my primary editor. I'm a big proponent of modal editing when writing software, or long blocks of text such as email. But when inputting a command into a shell, or a URL into a browser window I found that I want a much smaller set of commands, and for that a quick delete‑back‑word or beginning‑of‑line binding is more useful. In fact I even use readline bindings in vim's command line with vim-husk.

I've found it's much easier to train myself to differentiate shortcuts based on if I'm editing a body of text vs a one off line rather than if the text happens to be in a GUI or a terminal.

Incomplete Solution: GTK Key Themes

GTK 2 and 3 support a feature called "key themes" that tries to solve this problem. By setting the GTK option gtk-key-theme to Emacs, all GTK inputs get the readline bindings. This is a big improvement, but there are some problems.

First, it's GTK only, which for me isn't a huge issue since most of the graphical programs I run are GTK, but that just makes it that much more frustrating when I am running a QT application.

Another big issue stems from the fact that GUI toolkits on Linux use control as the standard modifier. This means pressing Ctrl-W to delete back word will instead close the window. Luckily in Firefox this is solvable by setting ui.key.accelKey to 18 in about:config. This sets the default GUI modifier to alt in firefox, which solves the issue there, but now I have modifier inconsistency between various programs.

Worst of all, though, is GTK 4 has dropped support for key themes altogether. It sounds like they plan on replacing it with a better solution - and I agree the way it is implemented is an abuse of CSS. But I hope there is something to replace it before too many programs start switching to GTK 4.

Sort-of-Complete Solution: QMK Firmware

I use QMK Firmware to set a custom keyboard mapping and enable macros. After my frustration with the drawbacks of GTK key themes I decided to try to add a custom text-modification layer to my keyboard.

This will require re-training my readline muscle memory, but I prefer to do that and have a consistent set of editing shortcuts that allow me to keep my fingers on the home row.

An image of my Preonic keyboard that runs QMK firmware My Preonic keyboard that runs QMK firmware

The basic idea is to add a new modifier key that can add all text manipulation shortcuts needed:

This works well since it's consistent across most GUIs, but it has problems as well.

First, using Ctrl-Backspace is a bit of a hack for delete-back-word. It seems to be standard for most GUIs, but not in the terminal. But what's worse are my bindings for Delete-to-beginning/end-of-line are massive hacks. They work by selecting the text first with Shift+Home/End, and then deleting it, but this is obviously completely broken in a terminal.

However, with some pretty janky bindings in my Alacritty config, I was able to just barely get it working, albeit just barely:

key_bindings:
  - { key: Back, mods: Control, chars: "\x17"  }
  - { key: Home, mods: Shift,   chars: "\x15_" }
  - { key: End,  mods: Shift,   chars: "\x0b" }

This binds Ctrl-Backspace to send a Ctrl-W, Shift-Home to send a Ctrl-U followed by a dummy underscore to be deleted by the next backspace, and Shift-End to send a Ctrl-K.

So far this has been working well for me with the exception of feeling crippled when I'm typing directly on my laptop instead of using my Preonic keyboard, but I have yet to come up with a better solution.