Why Vim Doesn't Need Multiple Cursors

We're back with a Vim Special! A lot of people that get to the point where they want to learn Vim often start looking for a plugin to do something like the Multiple Cursors feature that they're used to from other text editors. This post will try to show you why you don't need this feature in Vim.

Let's jump right in with the first basic feature that Vim provides us to make life simple.

;. Repeat movement, Repeat command

As you know, almost all movements in Vim can be done efficiently and fast, using e.g. h j k l w b e f t F T ( ) { }. The cool thing is, Vim remembers the last movement you made and makes it available to repeat immediately using the ; key. If you just jumped to the last word beginning with I by typing FI, you can jump back even more by using ;.

Next up is the command. For instance, we want to lowercase the letter we jumped to: gul. Vim also remembers this command and makes it available using the dot operator: ..

You can now combine those to repeat movement and action over multiple occurences.

/ Search, gn Text object, Dot operator

If you want to combine the power of Vim's search with the dot operator, that's perfectly possible. Let's say you searched for /ruby\|php\|python\|javascript\|java and want to replace some of the matches with Haskell. Just jump to the first one using n and replace it using the gn text object, like this: cgn type Haskell and then hit <Esc>. Since cgn is a command, we can now use . the dot operator to repeat that. If you want to skip an occurrence or undo what you did you can use n and u respectively.

Search & Replace with confirm

  • You most probably know you can search & replace in your whole file by using

    :%s/search goes here/replace goes here/g
    
  • or in a visual selection by making a visual selection using v or V and then typing

    :s/search goes here/replace goes here/g
    

    you should see something like :'<, '>s/search/replace/ which is again some Vim magic. '< points to the beginning of the last visual select, and '> to the end of it. The , that separates them is making a range of it. It's like saying :1,5 if you want to do something with the first 5 lines of your file.

Now, this is really nice, but you might want to skip some matches, or you want to be sure that you don't replace matches that you didn't want to replace. You can use the c modifier to your search, and Vim will ask you to confirm every match using y or n. E.g.:

:'<, '>s/search/replace/gc

Visual Block mode

If the things you want to change are neatly aligned with each other, you can make a Visual Block change:

point 1
point 2
point 3
point 4

I want to uppercase those first p's of the words, and I want to make this an enumeration using -. Let's make a Visual Block selection in the beginning of those lines using 0<C-v>3j and then use U to uppercase the first letters. Then gv to go back to the visual select and I- <Esc> to make the enumeration happen.

- Point 1
- Point 2
- Point 3
- Point 4

Macro's

If you need to do more than just a few inserts or updates on multiple lines or more complicated edits in json, for instance, Vim's macro's can really come in handy. They allow you to "record" a sequence of edits and movements, and replay that sequence as many times as you want. Let's see how we can use this to convert some copied JSON to a PHP array:

{
    "foo": "bar",
    "baz": "qux",
    "vim": "sexy",
    "rest": "lazy"
}
  • We'll start with the cursor on the first : of the file. We'll start recording to register i by pressing qi.
  • As a standard practice, and to increase our chances to reproduce this on the following lines, the first thing I do is move the cursor to the first character of the row: ^.
  • Then, we'll replace the double quotes we're on with single quotes using r' and look for the next one using f". This can now be repeated using the dot operator . and the repeat movement operator ;.
  • We return to the : by using F: and replace it with => by issuing this command: cw =>
  • We did all changes to the first line, so we can end our recording by going to the next line using j (again, to increase our chances of repeating this easily), and ending the recording with q.
  • repeating this is as easy as executing the register i to which we recorded this using @i. You can also use a multiplyer if you need to do this 100 times: 100@i.

We now have

{
    'foo' => 'bar',
    'baz' => 'qux',
    'vim' => 'sexy',
    'rest' => 'lazy'
}

Changing the brackets to PHP's array() should be easy.

Conclusion

I hear you: you now need to learn 5 different ways of doing replaces which could all be solved by using multiple cursors. But all of these have some great benefits. They are deliberately executed on a piece of text, you can undo them easily (and one by one), and they are faster and less error-prone than multiple cursors (when you know them by heart). Also, Vim's autocompletion keeps working when using them, as opposed to all multiple cursor plugins that I've tried.

That said, I still have a multiple cursors plugin installed, for when I'm doing pair programming.

Have fun Vimming! 🎹⌨️

Categories: IDE