I was introduced to the venerable vi text editor back in the early ’90s, apparently not long after the release of Vim. It might have been my first editor (pico and nano being the other possibilities). Neither was I ever an expert, confining myself to writing simple Perl scripts, nor did I use it for long, since I soon started favouring Windows. Still, it’s hard for a software developer not to encounter Linux with Vim (or at least vi) installed on a regular basis, so I kept my rudimentary skills sharp over the years. Meanwhile, after trying a succession of editors that failed to satisfy me—the ones I remember being Notepad++, UltraEdit, and the Komodo IDE—I adopted Emacs as my religion primary editor in 2006.

15 long years later, in November 2021, I replaced jsonconfig-mode with JSON Par mode (in tandem with json-mode). I learnt it slowly, with one Firefox tab permanently dedicated to the documentation, but I was very impressed. It was my first taste of modal editing in Emacs, albeit in a smarter, language-aware form.

With that success under my belt, a few months later, I migrated from smartparens to a combination of the intelligently modal Lispy and the non-modal Puni. While this was much harder to learn and there are still large gaps in my understanding, I can use it effectively enough to see the value in it. Maybe I’ll even give Symex a go someday in the (far) future.

Now, I only took these first tentative steps towards modal editing because I’d been contemplating the concept for a while already, prompted by several conversations about its efficiency and the comparatively lower physical strain. evil-mode (the extensible vi layer for Emacs), specifically, had been on my mind for years. In fact, I’d even put this in my configuration after watching a System Crafters video in October 2021:

Emacs Lisp(use-package evil
  ; (…elided…)
  ;; not yet not yet
  :disabled t)

I wasn’t ready back then, but I finally took the plunge at the end of January last year by enabling evil-mode, evil-collection, and lispyville. As you might expect, I had no idea what anything did when I started. All the guides said to learn Vim, presumably because evil-mode, in the abstract, only emulates it, so I looked up several Vim guides, cheatsheets, and the like to gain some modicum of efficiency. Here are my scattered thoughts, 14 months into my delicious fall from grace.

Embracing evil after years of innocence

What I initially found most confusing and frustrating is being in insert mode and wanting to do something that I knew had an Emacs shortcut but not an insert mode equivalent (such as toggling case or killing a sexp). I had to learn to fluidly switch modes as well as keep an eye on which commands I use most often with a view towards moving them to simpler insert mode shortcuts. Perhaps it’s also acceptable to, for example, use M-c to capitalize things in insert mode.[1] No one’s going to yell at me. I hope.

By the end of the first month, I had concluded that there were two missing pieces: an easy way to yank text (C-y in insert mode first runs evil-copy-from-above, then pops the right text after it with M-y; S-insert is awkward; p requires being in normal mode) and multiple cursors. I couldn’t figure out which library to use among multiple-cursors, evil-mc, and evil-multiedit after seeing a possibly outdated Reddit post. evil-mc seemed fine, except I didn’t know how to mimic the multiple-cursors behaviour of marking only occurrences either before or after the cursor.

I deeply enjoyed deleting swathes of code from my Emacs configuration as they were made redundant. I did have to disable the lispy section of evil-collection, though. Not only did it conflict with lispyville, it also set TAB to self-insert when not activating a hydra, so I unwittingly put a bunch of tab characters in my files.

Following the common wisdom, I set SPC to be my leader key. I would like to give C-h its normal Vim binding, but I constantly rely on Emacs’s help system. I’m still debating whether to bind help to C-H instead, even though it would confuse me a bit.

It took a little time to get used to C-z switching in and out of Emacs mode instead of minimizing the window (for which I now need to use C-x C-z). I also had to work to remember that C-o in insert mode runs the next command in normal mode.

A taste of super-vi-llainy

Two months into the switch, I felt like I had superpowers, notwithstanding that I couldn’t determine the best way to work with structured or balanced expressions via Puni, or that I desperately needed to improve my use of evil-textobj-tree-sitter. One continual mystery has been how to perform regular motions using such text objects, without a verb. For instance, I can delete or select any custom text objects I define without extra work, but to simply move to their beginning or end, I need additional key bindings. (A similar question on the Emacs Stack Exchange has no clear answer.)

Another persistent annoyance is not having an easy way to move back to the previous point after a motion: I would normally use C-x C-x, but the concept itself is more complicated and nebulous with evil-mode. g ; is only a partial replacement. C-o may be what I need, although I don’t find its behaviour entirely intuitive. pwnedary on Reddit suggested ` `; I’ve tried it before but I’m giving it another shot now.

Coming to terms with more complex devilry

At the three-month mark, I began exploring ex commands and setting up more useful leader key shortcuts for file and buffer management. I also forced myself to use the Vim style of managing windows as much as possible. Having to think about the keys certainly slowed me down, but it was worth it.

The feeling of having superpowers remained. By this time, I had settled on primarily using evil-multiedit for multiple cursors, and even done a bit of navigating between matches for the first time. I added key bindings for Magit and more complicated file and buffer management under the very convenient leader key too.

Somewhere in there, I had the brilliant idea of adding a key in normal mode to yank text. After a few days of not using it, I remembered with embarrassment that p already exists and I was reaching for it automatically anyway. (Now that I think about it, I can probably use M-y to call consult-yank-pop instead.) In time, it became second nature to press C-o p in insert mode too.

Pragmatic evil

I came across the useful-looking evil-textobj-anyblock thanks to a chance Reddit comment. However, the description said it had been replaced by targets.el. The description for that library says it’s Dead (will eventually be replaced by things.el)… but things.el hasn’t been updated since 2020 and isn’t being actively developed, whereas targets.el has been updated more recently than that. I’m completely at sea. According to the author, noctuid, the eventual successor might be youkai.

evil-goggles is exceedingly useful but I had to disable it because of performance issues. Editing Org Mode tables was painfully slow (pressing TAB would make Emacs lock up for many minutes as it realigned the table). Even then, unfortunately, the delay didn’t go away completely—something in evil-mode continues to cause the errant behaviour. I thought it was an issue with undo-fu, because everything briefly worked fine without it, but the problem soon returned regardless of whether that library was enabled.

Something I find a little annoying if understandable is that r and R don’t take the input method into account. For that, I have to enter insert mode.

persp-mode and input methods

Along the way, I also started using persp-mode, which I’ve considered a few times before. I like it, but it made my custom-built deferred LSP initialization stop working… and then start working again. In addition, at some point, my input method stopped being activated by default (something I had explicitly configured before descending into evil). I got it working again by adding advice around evil-insert-state:

Emacs Lisp(defun aankh/activate-rfc1345 ()
  (setq-local default-input-method 'rfc1345)
  (activate-input-method 'rfc1345))

(defun aankh/activate-input-method-in-insert-state (&rest args)
  (activate-input-method default-input-method))

(advice-add 'evil-insert-state :after 'aankh/activate-input-method-in-insert-state)

Baby’s first demonic manifestations

Fairly early on, I wanted a way to combine avy-goto-char-2 with text objects. My first attempt almost worked, but I had to make a few mostly-unrelated changes:

  1. Bind evil-avy-goto-char-timer instead of avy-goto-char-timer.
  2. Remove the existing binding for SPC in evil-motion-state-map, which was clobbering its use as a leader key.
  3. Run any evil-specific general-configuration only after loading evil.

I prefer to have the motion behave like t (up to the selected character) instead of f (including the selected character), so I redefined it to always do that:

Emacs Lisp(evil-define-avy-motion avy-goto-char-timer exclusive)

After all this, though, I have to admit I hardly use it.

On the other hand, I also built my first custom operator about six months in, to convert between JSON and YAML using yq, which saved me a lot of time while working on my Kubernetes cluster. I’ll write about that in another article.

Being bad ain’t all that simple

Here are just some of the subjects I have yet to fully comprehend as of this writing:

I tried to get Emacs to treat Unicode quotation marks (e.g. ) the same way as their ASCII equivalents by modifying the syntax tables. For example, the w motion before it’s ought to move past the entire thing, like with it's, not stop before the apostrophe. I was able to change the character classes, but the behaviour is still different with or without evil. I can’t tell what I’m missing even after looking at the source for evil-forward-word, evil-forward-word-begin, bounds-of-thing-at-point, thing-at-point, forward-word (in C), and word_boundary_p (ditto).

evil feels good

It was a bit difficult to get started, but selling my soul has been extremely rewarding. Although the novelty has worn off, I still feel like I’ve developed superpowers. Even the imperfect composability of motions and operators allows me to do so much with so little. I look forward to all the many discoveries I’m sure lie ahead of me. I wish I’d done this much earlier, but then again, I imagine I’ve benefited greatly from the years of effort the community has put into the ecosystem, so perhaps the change came at just the right time.

I’ll admit I occasionally glance with jealousy at the Kakoune modal editor. The core concept is simplicity itself: switch the order of actions from verb-object to object-verb, allowing the user to see the area that will be affected by the subsequent command. The advantages of its approach are immediately obvious but, setting aside the trivial matter of having to relearn everything, how likely is it that the world of editors will embrace a competing, er, model of modal editing?

noctuid kindly linked their comprehensive rebuttal of Kakoune’s claimed advantages, curing me of my lamentable envy in one fell swoop. Their argument is clear and persuasive even to my limited understanding.

Philosophical questions aside, my father—who introduced me to Linux, vi, and Emacs all those years ago—would have found the whole affair hilarious. First I come crawling back to Emacs, now Vim… in Emacs?

  1. I eventually wrote an operator to ‘twiddle’ case based on a Reddit comment, and promptly forgot it existed.