EINVAL: Valid solutions for invalid problems

Multiple cursors considered… suboptimal

The famous “considered harmful” would be a vast overstatement, but I see them as a solution preventing better solutions. I’m going to talk about the GNU Emacs flavor of them so not all problems and alternatives will apply to other text editors.

First of all, I’d like to preface it with a statement that I have massive respect for Magnar Sveen and all the multiple-cursors.el contributors. The concept of multiple cursors is not native to Emacs and what this package achieves is nothing short of a miracle. Their readme puts it quite well: “This is some pretty crazy functionality, so yes, there are kinks.” While it’s crazy it works as well as it does, I continued to look for other solutions. Partially because of the mentioned kinks, partially because of my sheer curiosity.

What multiple cursors do

Multiple cursors are primarily used for making bulk changes to multiple similar code fragments, often as a simple form of refactoring. In the simplest scenario it’s essentially a search & replace operation.

Example (fake-migrating from the straight.el Emacs package manager back to the stock package.el):

In a more complex scenario it allows editing the surroundings too, effectively making each change a bit different based on the code around it.

Example (making the installed package name explicit):

While technically one could do the same with a regular expression search & replace using the capture groups, it’s not a pleasant experience due to the lack of immediate feedback.

My conclusion: this immediate feedback is what makes multiple cursors so appealing. With the classic regexp search & replace approach the feedback comes only after we finish the operation. Some flavors of them come with a handy preview, but even then we need to first provide a complete valid regexp making it not nearly as instant.

What multiple cursors don’t do

Why not just use multiple cursors then? After all they are pleasant to work with and they work well most of the time. I actually find it hard to find a specific clear answer.

My general impression is that while they work very well 90% of the time, there are some Emacs features they simply don’t play nicely with. The most obvious one is isearch which needs a replacement specifically made to work with multiple cursors.

I also find it hard to correct mistakes in cursor navigation. If I make one of the cursors move in an unfortunate way (for example a reckless forward-word or forward-sexp when I should have used more fine grained commands), it’s often easier to start from scratch than try to move it back where I wanted.

Meet keyboard macros

From my experience lots of Emacs users overlook the fact Emacs comes with a fully-featured keyboard macro system. For the people not familiar with it:

  • To start recording a macro, press either F3, C-x ( or C-x C-k C-s.
  • After the recording is complete, press F4, C-x ) or C-x C-k C-k to save it.
  • It then can be replayed with F4, C-x e or C-x C-k C-k.

Each one of these keys works in a subtly different way, so read their docs for the details. Sounds complex—especially with so many alternative keys—but each set of them makes sense on its own, so stick to one (F3+F4, C-x (+C-x ) or C-x C-k …) and all should be good.

I’ve been using it as my replacement for multiple cursors for a quite a while now and so far with great success.

While they allow for many other things, let’s focus on the workflow usually typical to multiple cursors. When recreated with the keyboard macros, this workflow consists on recording a macro that:

  1. Searches for the phrase we would start our multiple cursors session at (the word we want to replace in the simplest scenario from before).
  2. Places the cursor in a predictable way.
  3. Places the mark (aka selection) in a predictable way.
  4. Applies the changes to the first occurrence.

Then we finalize the macro which is now ready to be applied to the other occurrences.

While the feedback is immediate only for the first occurrence, so are any mistakes. Emacs comes with a macro editor (C-x C-k C-e) which makes fixing a faulty macro surprisingly easy. While we’re at it: if we forget to start recording, or the recording gets interrupted, we can still salvage the macro by turning the keystroke history (aka “the lossage”) into a macro (C-x C-k l).

Now, steps 1–3 are painfully repetitive, so I came up with a wrapper that automates them. It’s available as a part of my kmacro-x package. It also comes with an option to highlight the other occurrences of the word the macro searches for, which brings it even closer to the original multiple cursors experience.

The same example as before, using kmacro-x-mc:

It feels a little bit slower than using the actual multiple cursors but at the same time gives some additional control and also allows using all the keyboard macro facilities. Do check the keys under the C-x C-k … keymap for all the options. My current favorite one is C-x C-k r which applies a macro to each line in a region.

Use what works the best at the moment

Do I still use multiple cursors? Definitely. They are a very intuitive solution that feels good to use due to the immediate feedback it provides. At the same time, learning to use the keyboard macros really pays of, especially when the use cases get more complex.

As always, it all boils down to choosing the best tool for each job.