Skip to content

Instantly share code, notes, and snippets.

@ijp
Last active October 1, 2023 16:41
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ijp/4700357 to your computer and use it in GitHub Desktop.
Save ijp/4700357 to your computer and use it in GitHub Desktop.

Semantic Key-bindings

The following is an issue that comes up from time to time on #emacs, and we don’t really have a good answer to it. I don’t for a minute think that I have figured this whole thing out, but here goes.

The problem

Joe Hacker is an Emacs user, and is quite content, only some he doesn’t like a particular key-binding. To make things concrete, he doesn’t like that in non-text buffers ‘g’ is the key to refresh, and would rather it were ‘r’. So he dutifully sets out and modifies the key-bindings to every Emacs extension he uses replacing uses of ‘g’ for refresh with ‘r’, and he lives happily ever after….Until he finds out about the cool new bloat.el and needs to do it again.

See the problem?

In Emacs, modes have a notion of a keymap, and this keymap dictates what actions key combinations perform within that mode. While keymaps can inherit from one another, many modes replace large sections of keybindings with variants of a command more suited to that mode. This is all very well and good, and since we have good conventions in place, much information about modes is transferable. But if you want to go against the convention, you are in for a world of pain.

To take our concrete example from above, if you wanted to change the keybinding for “refresh” for a whole class of modes at once, you’d try and find a parent keymap common to all these, and perform the changes there. This would work quite well if it weren’t for specific modes being smart and trying to provide more “mode-appropriate” behaviour for a particular keybinding.

Similar issues arise with extensions that aim to emulate other editing environments, especially vim emulation.

The issue then is that we treat keybindings as a representative for a particular behaviour, and change the binding to modify the behaviour, when the behaviour and the keybinding should be in some sense separate from each other, so that each can be updated separately.

The “solution”

All problems in computer science can be solved by another level of indirection – David Wheeler

In Emacs, right now, there are certain keybindings that redirect to another one. For example, <tab> refers to the Tab key on your keyboard, and it is usually bound to TAB, which corresponds to the key combination C-i. This allows graphical Emacs instances to behave similar to terminal Emacs instances by default, while still allowing extra flexibility to GUI users which can make the distinction between C-i and <tab>.

What I am proposing is to extend Emacs in a similar fashion, using additional “semantic” keybindings to refer to specific behaviours: Reverting could be bound to <revert>, Completion to <complete>, indenting to <indent>, and so on. We can then, hopefully, have a few common keymaps provided bindings for these, e.g. “g” in a non-text-keymap being bound to <revert>, and instead of rebinding particular keys in modes, we rebind the approprite “semantic binding”.

In theory, this needn’t be an extension to either the current keymap API, nor require additional keybindings to be literally supported. Similar to how people bind custom bindings in mode hooks, we could encourage a style or rebinding “pointer variables” within mode-hooks. Similar to how we would have hacked overriding keybindings if we didn’t have keymaps.

In fact, Emacs already provides such a notion of a “mode-local” variable which I think we can take advantage of here.

Issues with the solution

backwards compatibility / how to handle mixed behaviour

defining suitable “semantic” keymaps for common emacs extensions

Finding the commonality in existing Emacs extensions is probably the most important, and most difficult task in this whole idea. Much of the work seems to be labour-intensive “standardising” work, and I’m not sure how much of it can be automated. We may be able to with some dumb heuristics, like checking for two modes that bind the same key with similarly named (maybe modulo the module-prefix) functions.

Fortunately, we are in good company. The common Emacs creation myth credits just such an effort to rms and Guy Steele back in the 70s.

I also don’t think it will be necessary to make the change all at once. From a users point of view, Emacs should behave exactly the same after making the change as before we made it, so this effort could take place over a prolonged period of time.

new problem domains will lead to fragmentation

Fragmentation is a pain in every type of software, whether purposefully, or through ignorance of an existing solution.

If both foo.el and bar.el have a notion of “baz” and want to define a semantic keybinding for it, it is likely that we will end up with a foo-baz and a bar-baz binding.

I think this may be a less serious problem, since, after discovering the similarity, it should be possible to get the two authors to hash something together, or to provide a separate extension that fixes them both. (sigh, yet another layer of indirection)

it may make built-in emacs help less useful

Right now, if you C-h k on a key you get the function bound to it. If the user instead got information about the ideal behaviour of this keybinding as opposed to the function itself, this would be undesirable. Emacs would need to take into account any indirection.

@nicferrier
Copy link

I really like this - perhaps it should be a wiki page?

One thing I've been thinking about is one handed emacs (it's not what you think). I'm often in the kitchen or somewhere where I've got a cup or a pen or something and I want to do certain things... check IM, check email, switch buffers... I can't do any of those things easily. I am thinking your semantic keybindings could facilitate that.

But then I was thinking this morning... I could probably already do that. If I had a keychord say, to switch to a context menu that had certain standard bindings as well as mode specific ones read from the modes keymap.

So for example, say I'm in an elisp mode and I want to check mail. I could press keychord 12 and then use a general binding for mail, say 3. Then I see some irc alert so I do keychord 12 and then 1 for irc. Then see it was only someone mentioning a repo so I want to switch back through the buffer chain to the elisp, so I do keychord 12 and then b maybe. And then I realize I want to look at another function so I do keychord 12 and then i which has been mapped automatically from elisp's mode menu to be imenu.

I think this would also be useful for tablet driven emacs since the options could be presented always on screen or something, as buttons.

The problem is how to do good job of inference for keybindings into the modeless map.

The only answer to that I can think of is to avoid that problem by letting it be really easy to configure and remember bindings. For example, have a visible menu (probably in a buffer) that appears after a timeout of pressing the keychord. That would let you configure the keybindings, again, one handed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment