What Emacs got right, or how I wish modern apps were more like a 50 year old text editor

June 5th, 2024 · 6 minute read

When I first started using Emacs I thought it was pretty weird. I had questions like “Why are windows called frames, and tabs called windows?” and “What the hell does C-x C-s mean?“.

But I stuck with it because I thought it made me look cool, and over time I came to understand the brilliance and practicality of Emacs’ architecture. It’s simple, but it’s exceedingly useful.

So nowadays I think it’s weird that other apps aren’t more like Emacs. I think there’s a lot that this 50 year old text editor has to teach.

Literally everything is a command

Lots of apps and websites these days feature a “command palette”, often summoned with ⌘K. Command palettes usually list a bunch of commands available to you with their associated hotkeys, and let you do a fuzzy search over them. Here are some examples from Superhuman, Linear, Sublime, and Godspeed:

Examples of some command palettes

Command palettes are awesome! There’s no need to remember every hotkey or where every button is located. Instead, just bring up the command palette and type what you’re looking for. If you do something frequently enough you can learn the hotkey.

Emacs has something like a command palette as well that you trigger with M-x, which is Emacs hotkey syntax for Meta+X (Option+X on modern keyboards):

M-x in Emacs

But Emacs kicks it up a notch, because in Emacs literally everything you can do is a command. Note the 8073 in the upper left corner of that screenshot.

The commands aren’t just for fancy stuff like sort-lines or list-processes - they’re for everything. This includes even the most basic interactions like forward-char which, yes, is the command to move your cursor one position to the right.

When you do anything in Emacs - including hitting a hotkey or clicking - you’re actually just executing a command.

And this turns out to be an exceptionally useful architecture for several reasons, the first of which is:

All hotkeys in Emacs are editable

Emacs’ architecture makes this easy to do. Since at the end of the day you’re always just executing a command, all Emacs needs to do is keep a lookup table mapping a hotkey to a command (it’s a little more complicated than that - you need to know what “mode” you’re in as well - but this is essentially right).

Changing a hotkey is as simple as swapping out a value in that lookup table. There’s nothing special or sacred about any of them. And none of them are absolutely necessary anyway, because you can always run a command directly from M-x (which, of course, you can also remap).

The immutable nature of hotkeys in some apps is a common source of frustration. Sometimes a hotkey isn’t what you want, is overriden by a global hotkey, or simply doesn’t exist at all. And there’s nothing you can do about it.

But Emacs hotkeys get this editability for free because of its underlying architecture. But that’s not all this architecture offers.

Scripting Emacs in small ways is really easy and really useful

The simplest way to script Emacs is to write a new command that just runs a bunch of other existing commands. Often this takes the form of what are essentially macros - packaging up a series of keystrokes into a reusable command. You can then bind this to a hotkey like any other command, or access it via M-x.

Though you do have to learn some Elisp to do this, in practice just the very basic syntax is enough to be dangerous with.

But there’s one more problem: you may know which hotkeys to hit to achieve something, but how do you know which commands those correspond to?

Emacs has another trick up it’s sleeve to address this: the describe-key command. When you run the describe-key command Emacs starts listening for the next hotkey you press. But when you press that hotkey, instead of actually running the associated command, it shows you what command would have been run. And with that you’ve got everything you need to start customizing Emacs.

For example, I often found myself wanting to add a TODO: comment to the next line, and thought it might be useful to have a custom command for that. I knew how to do this with hotkeys, of course, so one by one I’d use the describe-key command to figure out what each of those hotkeys did. With that and a little bit of help from Google I came up with this:

(defun todo-comment-on-next-line ()
  (interactive)          ;; Makes the function show up in `M-x`
  (move-end-of-line nil) ;; Jump to end of current line
  (newline)              ;; Insert a newline
  (back-to-indentation)  ;; Move cursor to current indent level
  (insert "// TODO: "))  ;; Insert the comment text

Then I bound this command to a hotkey and I’ve used it every day since.

I didn’t have to set up some complex development environment or learn an extension API. I just had to run a series of commands, like I would when doing anything in Emacs. Once again this command-centric architecture paid off.

Emacs is 100% keyboard oriented

Every single thing in Emacs can be done from the keyboard.

In most apps the GUI comes first and hotkeys are laid on top of it. But Emacs is keyboard-first. There is a GUI, but it’s secondary. You can click on things, but that triggers commands just like hotkeys do.

There is nothing you can achieve in Emacs with a mouse that you couldn’t also achieve with just your keyboard.

I won’t lay this on too thick, but keeping your hands entirely on the keyboard allows for a fluency that is unmatched when you’ve sometimes got to reach for a mouse instead. And there’s additional trust I can place in Emacs knowing that it’s always a matter of finding the way to do something with the keyboard, rather than wondering if it’s possible at all.

What modern apps can learn from Emacs

This command-centric architecture is remarkably powerful, and I’d love to see more modern apps adopt it. Any app with a command palette is so close to achieving this already.

Electron and web apps could do this by taking advantage of the developer console. You’ve got a full blown JavaScript programming environment right there. Stick a few API functions on window and bam, anyone can customize your app.

Here are some examples of things I’d love to be able to do in my fantasy developer console:

  • I often react to the last message in Slack with a 👍. Here’s the command I want:
    function reactWithThumbsUp() {
      window.executeCommand('react-to-last-message', ':+1:');
    }
    
  • Slack’s hotkey for jumping to Unreads is ⌘+Shift+A. But I want it to be ⌘U instead:
    window.setCommandHotkey('jump-to-unreads', 'command+u');
    
  • I get a lot of emails from folks requesting a student discount for one of my apps. I generate the coupon code, respond with a template, label it, and archive it. Here’s the command I want:
    function respondToStudentDiscountRequest() {
      window.executeCommand('insert-template', 'Student discount request');
      window.executeCommand('replace-text', '{COUPON_CODE}', window.getClipboardText());
      window.executeCommand('send');
      window.executeCommand('add-label', 'Discount request');
      window.executeCommand('archive');
    }
    

Closing

If you’ve got any thoughts or comments on this I’d love to hear them. Especially if there are apps you know of that demonstrate some of these principles. Twitter or email (me@ this domain) are the best ways to reach me.

And a quick plug: if you liked this post, you might like the app I built, Godspeed. Godspeed is a fast, 100% keyboard oriented task manager - like Superhuman for your to-do list.

A picture of me
Daniel de Haas is the creator of The Godspeed logo Godspeed, a fast, 100% keyboard oriented task manager - like Superhuman for your to-do list. He lives in Walnut Creek, CA with his fiancée and his dog.