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
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
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
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
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
C-x C-k C-s.
- After the recording is complete, press
C-x C-k C-kto save it.
- It then can be replayed with
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 (
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:
- Searches for the phrase we would start our multiple cursors session at (the word we want to replace in the simplest scenario from before).
- Places the cursor in a predictable way.
- Places the mark (aka selection) in a predictable way.
- 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
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.