Text editors III: Emacs

When I started this series of posts, I didn’t expect to take a five year break between the second and third entry. In the first article, I covered Vim, which had at that time been my primary editor for about two years. In the second article, I looked at Acme, a fascinating editor/file browser/shell hybrid that’s sadly been passed over by time, and which, despite some intriguing features, I could never really use for serious work. The obvious next thing to cover was, of course, the other venerable old programmer’s editor.

Emacs

Emacs is a fine operating system in need of a good text editor.

Forget the Linux desktop, it’s time for the Emacs desktop

Forget the Linux desktop, it’s time for the Emacs desktop

For a guy who uses Vim as his primary editor, switching to the opposition – even temporarily – is not really an appealing prospect. And after surmounting Vim’s initial learning curve and getting Stockholm Syndrome comfortable using it for my every text-editing task, I didn’t really feel like going back to those confused and unproductive early days of learning my editor. Especially because Emacs, at a basic level, is not a good text editor. Like most modern editors, and unlike Vim, it’s not modal, so doing anything other than entering text requires key chords. And because Emacs predates modern conventions like Ctrl-C and Ctrl-V, they’re a different set of key chords to everything else. And because Emacs has a tonne of features, it relies on sequences of chords for many actions. If you thought quitting Vim was bad, quitting Emacs is even worse – you have to press Ctrl-X then Ctrl-C! A mere ten minutes into the Emacs tutorial, I could already feel the RSI setting in.

So Emacs’s default key bindings suck, even if you remap your Caps Lock key to Ctrl1, and especially if you’re accustomed to the relative lack of chording in Vim. And as my objective with this series is to explore the good parts of different editors, I eventually opted not to continue C-bing and C-ping around documents, but rather to set up sensible key bindings so that I could take a proper look at Emacs’s famous extensibility.

And by sensible I mean Vim-like. Emacs’s Evil plugin is widely praised as the single best Vim emulation there is. I installed it and used it for a bit, but found the experience somewhat incomplete: while editing text files in Emacs with Evil is almost indistinguishable from editing them in Vim (down to things like :normal and :global that few other Vim emulators have), you’re thrown harshly back into Emacs key-binding world as soon as you try to switch buffers, navigate the file system, or use the built-in help menu or package manager. There are a whole lot of other plugins for Emacs that provide Evil keybindings for these other parts of the program, but I wasn’t sure which would be complete/maintained, and it seemed like a lot of work installing and setting them all up. So I put this post on the backburner again and continued just using Vim.

Later, a friend pointed me to Spacemacs, a set of configurations and packages specifically intended for Vim users migrating to Emacs, so I decided to give that a shot. I tried it a couple of times, but found it kind of overwhelming – now I had to not only learn Emacs, but Spacemacs as well! Plus it was kind of slow.

Emacs + Vim + the spacebar

Emacs + Vim + the spacebar

Sometime later, I stumbled across Doom Emacs, which bills itself as a lighter framework for Vim users migrating to Emacs, and is a lot faster and nicer looking than Spacemacs. So far, I’ve found it to be a nice middleground between the pain and confusion of setting up my own configuration and the overwhelming nature of Spacemacs. That the config files are peppered with snarky comments is a nice bonus.

Emacs + Vim + the spacebar - a bunch

Emacs + Vim + the spacebar - a bunch

And so, now, at last, equipped with Doom Emacs, I began to explore the true power of Emacs: Elisp. As Paul Graham will tell you, Lisp is the most powerful (type of) programming language there is. Emacs, purportedly, allows you to harness this power to edit text like a higher-order being. At the very least, it has to be better than Vimscript, the primary reason for most Vim to Emacs migrations. Vim’s plugin ecosystem is expansive, but this is despite rather than because of the experience of using Vimscript, which is quirky and kludgey. Emacs was designed around the use of Elisp, a fully functional Lisp language, whereas the imperative Vimscript was added to Vim in its early versions, when it was still growing out of vi, and has only recently acquired modern language features such as lambdas.

My first experience with Emacs extensibility was setting up my custom keybindings. Because I was doing this in Doom Emacs, I was building on top of a foundation of a couple of plugins.2 Plain Emacs lets you define keybindings with define-key (among others), Evil provides evil-define-key (among others), and Doom wraps the latter with map!. Emacs makes it quite easy to go into the Elisp source of these different functions, so you can see exactly how they wrap each other.

In any case, setting keybindings in Emacs is a very different experience from doing so in Vim. In Vim, you remap keys by referencing other keys, i.e. by speaking in the language of Vim. For example, here are my Vim keybindings for dealing with split panes:

" New panes
nnoremap <leader>w <C-w>v<C-w>l
nnoremap <leader>z <C-w>s<C-w>j

" Pane navigation
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l

In Vim, the default key sequence for opening a new pane to the side is C-w v, which I’ve remapped to ,w (, is my leader). When you open a pane, Vim leaves your cursor in the current pane, which I don’t want, so I’ve added C-w l, which is the Vim key sequence for “move to the pane on the right”. Thus, when I press ,w, Vim interprets it as C-w v C-w l. I’ve done a similar thing for opening new panes below the current one. And I’ve also remapped pane navigation from C-w <direction> to the simpler C-<direction>.

In Emacs, you map keys by referencing the Elisp function you want them to invoke, rather than the key sequence you’d press in default Emacs. Thus a partial implementation of the above in Doom Emacs looks like this:

(map!   :n ",w"  #'split-window-horizontally
        :n ",z"  #'split-window-vertically
        :n "C-h" #'evil-window-left
        :n "C-j" #'evil-window-down
        :n "C-k" #'evil-window-up
        :n "C-l" #'evil-window-right)

To set these keybindings, I had to first search for the names of the functions that I invoked using the default keys and specify those. While Emacs (and StackOverflow) makes it relatively simple to do that, you’ll notice I’ve left out complexities such as specifying my leader key and performing more than one action, because when I was first setting these bindings up, I had no idea about how to do either of those things.

While this way of mapping keys is arguably more sane than Vim’s and obviates the need to learn the difference between map and noremap, it loses some of the magic composability of Vim’s way. Once you learn the language of Vim, you can map any sequence of keys to any sequence of commands. For example, my Vim config has the following mapping for fixing nearby comma splices:

nnoremap <leader>cs f,r.wvU

Doing the same thing in Emacs would require learning each key’s underlying function, defining a fix-comma-splice function which composes all of them in the way you want, and then mapping that function to a key, which would take a lot more reading and typing than the 27 characters it takes in Vim.

This, then, is the price of having extensibility rather than a specific keyboard editing language as a core feature. But because of that extensibility, it’s possible to write Elisp functions to facilitate mapping in the Vim way (and someone has). Functions, not terse editing keystrokes, are the core construct of Emacs. It is not so much a text editor, then, as a framework for building your own text editor.

To some degree, that’s true of all the editors programmers like to fight about. Every code editor has settings or preferences that can be configured – the size of tabs, the colour of the background, and so on. And every code editor people actually use will inevitably grow a plugin API and then a collection of plugins that reflects its community of users in scope and ambition. New users of the editor will then build their own version of the editor through tweaking settings and collecting plugins relevant to their needs and habits.

And this is how initial use of Emacs also feels. Tweaking appearance and behaviour, installing and removing packages, exploring what’s already been done and copy-pasting a lot of code. But before long, you’ll run into something that requires more than just copy-pasting, and you’ll need to write some Elisp. Take my frame splitting example above. After a bit of trial and error, I came up with the following code to replicate my Vim config:

;; split windows
(map!   :n ",w" (cmd! (split-window-horizontally) (evil-window-right 1))
        :n ",z" (cmd! (split-window-vertically) (evil-window-down 1)))

cmd! is a macro that expands to (lambda () (interactive) ,@body), i.e. the definition of an interactive (i.e. keymappable) anonymous function. lambda is itself the short form of defun, the Lisp function for defining functions. So my first cmd! block could be written more expansively as

(defun dy/split-window-go-right () ;lisp users think nothing of using special characters in function names 
  "Split window horizontally and go to the one on the right"
  (interactive)
  (split-window-horizontally)
  (evil-window-right 1))

And this was my first Elisp function. I’ve had to delve into Elisp and write functions3 in order to do something I could have done in Vim with the mere knowledge of keystrokes and nnoremap. Hmm. Sounds familiar.

When I started using Vim, I had to learn a whole editing language to do something I could have done in Notepad by clicking or using a universal keyboard shortcut. Instead of C-c and C-v, I had to learn about registers, and figure out "+y and "+p. But workflows that start off feeling slow and convoluted teach you the bones of the editor, and that knowledge becomes the foundation for text wizardry.

In most other editors, there is a natural boundary between users and plugin developers. Ordinary use of Visual Studio Code will never expose you to plugin development – you need to purposefully seek that out. Likewise, I’ve used Vim as my primary editor for seven years and never felt the need to write Vimscript beyond single-line key remappings – I’ve contemplated writing a plugin or two, but demurred as doing so would require actually learning Vimscript.

Emacs erases this distinction. To use the editor fully, you have to learn Elisp, and there’s a clear continuum from writing your first function to writing what would amount to a plugin in any other editor. The ability to change Emacs’s behaviour is itself Emacs’s most fundamental behaviour. To be an Emacs user is to be an Emacs plugin developer.

Writing extensions for your editor is a fast, iterative process. Emacs provides a function for evaluating arbitrary code in real time, so you write a bit of code, highlight it, press g r in Doom Emacs (M-x evaluate-region in regular Emacs) and it will be evaluated. This allows you to test and rewrite your code until it does what you want it to with near-instant feedback, just like in a REPL. And once you’re done, that code becomes a permanent part of your editing experience. That’s pretty powerful.

Emacs is also a gateway to Org Mode, a super-powered version of Vimwiki, which is itself a gateway to Org Roam, an open-source, self-hosted version of Roam Research. These are both things that I’m extremely interested in digging into.

So far, I’ve used Emacs for a lot of writing and a bit of programming, and it’s been strong on both counts. Doom’s Zen module, which wraps writeroom-mode, provides a pleasant distraction-free interface for writing prose, superior in some ways to Goyo, the Vim plugin I use for the same thing. First, it only affects one buffer at a time, whereas Goyo has a global effect – this is quite nice if I want to quickly open a window with some code or config next to my prose. Second, as a GUI rather than a terminal-based application,4 Emacs can mix different fonts together, and writeroom-mode does exactly this in markdown files. Astonishingly, this graphical feat is managed without the service of a Chromium instance.

I&rsquo;ve finally caught up with the early 90s. Can&rsquo;t wait to see what the next 30 years of text editing will bring!

I’ve finally caught up with the early 90s. Can’t wait to see what the next 30 years of text editing will bring!

On the programming side, writing Elisp is obviously a joy, and people have written plugins such as inf-ruby to bring some of that joy to other languages. When I’m in the middle of a programming project in Ruby or Python, the languages I generally gravitate to, I often find myself opening REPL sessions to test out small sections of code. Being able to just write that code in the file I’m already in, then select and eval it, makes that process must tighter and faster. It also brings to mind the best feature of Acme.

Overall, I’m really impressed by what I’ve seen of Emacs, and I’m excited that this is only the tip of the iceberg. I may even switch to it once I figure out a few niggling irritations, like the lack of tab completion for :! commands. I am somewhat concerned that I’ll run into areas where my Evil keybindings will fail me, but this hasn’t happened yet, and hopefully it won’t prove insurmountable. It took me a while to get started with it, but I’m glad I did.


  1. setxkbmap -option ctrl:nocaps in most mainstream Linux distros. It’s truly a crime that a key as worthless as Caps Lock takes up such prime keyboard real estate. ↩︎

  2. Doom Emacs, like Spacemacs, comes with a comprehensive and well organised set of keybindings centred around the spacebar, which I could learn instead of setting my own, but I’m not quite ready to sacrifice the years of muscle memory represented in my own motley crew of keybindings. ↩︎

  3. A large part of development is writing something and then finding out it’s already been done better. As it happens, Evil already provides a mechanism for the behaviour I want with the variables evil-vsplit-window-right and evil-split-window-below↩︎

  4. Yes, there’s GVim, but there’s a reason it’s called GVim and not Vim. ↩︎


similar posts
webmentions(?)