11

I'm using vim-multiple-cursors plugin.

I would like to put cursors exactly where I want. For example ([x] are the cursor positions):

Lorem ipsum dolor sit amet[1], consectetur adipiscing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna[2]
aliqua.

In Sublime Text I would normally put the first cursor, then go to the next position with the arrows keys and put the second one.

Is there something similar in VIM? With this plugin or another.

EDIT

Following a comment, my interest in doing so appeared when trying to write
\section[My first section in this book]{My first section in this book}
in a .tex file. My first reaction was to write \section[]{} and then put two cursors in order to write the same thing inside the [] and {}.

Other example would be to add _someStuff after several different variables names. For example, turn this:

variable1 = 2
my_variable2 = 12
var3 = 14

into this:

variable1_someStuff = 2
my_variable2_someStuff = 12
var3_someStuff = 14

With multiple cursors I can do by selecting a column cursor and then going to the end of the word, then inserting, etc. But I guess I would be easy to choose exactly where I want the cursors to be.

  • 1
  • 1
    I think that an important question to ask is why do you want to do that? What are you trying to do? Using multiple cursors isn't really "following the Vim way", there is probably a different way to do what you want, at least as efficient, using built-in features. – statox Aug 13 '15 at 18:08
  • @statox. See edit. I gave two examples that came to my mind. The second one I can accomplish with the vim-multiple-cursors plugin, but the first one I can't, at least in an easy manner. – tomasyany Aug 13 '15 at 19:32
  • @Nobe4. Thanks, the second script made it. – tomasyany Aug 13 '15 at 19:33
27

Using multiple cursor isn't a Vimmer thing

As I said in the comments using multi cursors (even with a plugin) isn't really "following the Vim way", I totally understand that it is attractive for someone coming from Sublime-Text but you can often find alternatives which are at least as efficient with Vim built-in features.

Of course, finding these alternative solutions isn't always easy and sometimes it takes time but it will get easier with your Vim experience and you'll see that with time multiple cursors will seem totally useless to you.

That's cool but how can I find an alternative way?

There is no universal answer since it depends a lot on what you're trying to do, I'll just try to give some hints about the first things to try:


The dot command .

The Dot command is probably one of the most powerful tools in Vim, it simply allows us to repeat the last change. I couldn't explain it better than Drew Neil in his Practical Vim. I think that every Vimmer should consider reading this book.

The strength of this command is that the last change can be an action working on a character, a line or a whole file. For example, a change can be delimited by the moment you enter insert mode and the moment you go back to normal mode.

With that in mind it is easy to do what you wanted to do with multicursor:

  • First let's set up our environment: Let's write as you suggested

    \section[]{}

  • Then make a repeatable change The cursor is now on },hit F[ to go back on the [ character. Then enter insert mode with i and type My first section in this book and go back to normal mode with ESC:

    \section[My first section in this book]{}

  • And here comes the magic part: Let's type f{ to put the cursor on the { character and hit . to repeat the last change:

    \section[My first section in this book]{My first section in this book}

All the challenge of the dot command is to learn how to make repeatable changes: it will come with grokking Vim but the basic is to understand how to make your change in a repeatable way.

For example to insert a semi colon at the end of a line you'll prefer using A; instead of $a;. Why?

Because A; creates an atomic action so when you'll use . on another line, no matter where you are in the line you'll insert the semi colon at the end. Whereas when using $a; you split your change in two parts $a and the insertion of ; so if you use . it will insert the semi colon on the current position of the cursor.

NOTE The magic formula in Vim is n.. A really cool workflow is:

  • search for the place you want to make an edit with /pattern
  • make your repeatable edit
  • use n to go to the next place to edit
  • use . to repeat the edit
  • repeat the last two steps: You're the king of the world (or at least of edits)

macros

Macros are another extremely important tool in Vim since it allows you to record a sequence of keystrokes and repeat it as if you typed it again.

I'll use, as an example, your second use case:

variable1 = 2
my_variable2 = 12
var3 = 14

Once again the important is to learn how to make your macros efficient (I'll give a counter example just after):

  • Put your cursor on the word variable1 and begin to record your macro with qq. This means "start recording all my future keystrokes in the register named q".

  • Start making your edit typing:

    • 0 to go at the beginning of the line
    • e to go at the end of the first word
    • a to append after your cursor
    • .someStuff to append the wanted text
    • <Esc> to stop the insertion
    • j to go on the next line
    • q to stop recording the macro
  • You'll get:

variable1.someStuff = 2
my_variable2 = 12
var3 = 14
  • Now you can use the macro to repeat your edit. As you are on the right line to edit you can simply execute the macro with @q. As we want to execute it twice you can use 2@q and you'll get the following result:
variable1.someStuff = 2
my_variable2.someStuff = 12
var3.someStuff = 14

NOTE 1 As you may have noticed, using 0ea at the beginning of the macro was really important. Indeed, if you had put your cursor at the end of the first word before recording the macro and executing it again your result would have been:

variable1.someStuff = 2
my_variable2 = 12.someStuff
var3 = 14.someStuff

As your cursor the text would have been inserted at the position of the cursor after changing of line (i.e. the end of the line in this case)

NOTE 2 Macros are extremely powerful and you can even create recursive macros when you are comfortable with them. Here your macro could have been:

`0ea.someStuff<Esc>j@q`

The final @q would have called the macro by itself instead of using 2@q; you'd just have used @q and all the work would have been done.


visual block

Here comes another trick that doesn't directly apply to your use case but can be really useful to edit a large number of line at the same time. Let's get this extract of CSS code:

li.one   a{ background-image: url('/images/sprite.png'); }
li.two   a{ background-image: url('/images/sprite.png'); }
li.three a{ background-image: url('/images/sprite.png'); }

What if you moved the sprites from images to components?

Well you can put your cursor on the i of images and press <C-v>. This will start the visual block mode which allows to select blocks. Now you can type t/ to select the word you want to change and 2j to select all the occurrences of the word.

After that you simply have to type c to change the word and then components. When you'll go out of insert mode you'll see:

li.one   a{ background-image: url('/components/sprite.png'); }
li.two   a{ background-image: url('/components/sprite.png'); }
li.three a{ background-image: url('/components/sprite.png'); }

The global command

The global command is a tool which allows to apply an ex mode command on lines matching a pattern, once again that's a good way to apply the same change on different place without needing multiple cursors.

The syntax is the following:

:[range] g / pattern / command

For more details on the [range] parameter please see :h :range. I won't detail it here, I'll simply remind that % represents the whole file, '<,'> represents the last selection, and 1,5 represents the lines 1 to 5 of the file.

This parameter defines the lines which will be treated by the global command. If no range is specified, then the global command will use % by default.

The [pattern] argument is a search pattern as you are used to use with the search engine. As it integrates the search history you can leave this field blank and the global command will then use the last search pattern in the search history.

Finally the [command] parameter is an ex command as you are probably used to.

Now the behavior of the global command is pretty simple:

  • Iterate through all the lines defined in the [range] parameter
  • If the current line matches the defined pattern, apply the command

As the [command] parameter is an ex command, you can do a lot of things. Let's take the following pseudo code which isn't pretty interesting and have a lot of debugging messages:

var myList  = null
var i       = 0

myList = new List()
echo "List instantiated"

for (i=0; i<10; i++)
    myList.Add(i)
    echo i . " added to the list"

echo "end of for loop"

Now let's say that you're sure this code works and you want to delete these useless echo statements:

  • You can apply your global command on the whole file so you'll have to prepend the command with % (or with nothing since % is the default range).

  • You know that the lines you want delete all matches the pattern echo

  • You want to delete these lines so you'll have to use the command :delete which can also be abbreviated as d

So you'll simply have to use the following function:

:%global/echo/delete

Which can also be abbreviated as

:g/echo/d

Note that % disappeared, global is abbreviated as g and delete as d. As you might imagine the result is:

var myList  = null
var i       = 0

myList = new List()

for (i=0; i<10; i++)
    myList.Add(i)

NOTE 1 An important point that took me some time to realize is that the normal command is an ex command which means that you can use it with the global command. That can be really powerful: let's say that I want to duplicate all the lines which contains echo, I don't need a macro or even the magic formula n.. I can simply use

:g/echo/normal YP

And voila:

var myList  = null
var i       = 0

myList = new List()
echo "List instantiated"
echo "List instantiated"

for (i=0; i<10; i++)
    myList.Add(i)
    echo i . " added to the list"
    echo i . " added to the list"

echo "end of for loop"
echo "end of for loop"

NOTE 2 "Hey what if I want to use my command on lines which doesn't match a specific pattern?"

global has an opposite command vglobal abbreviated v which works exactly like global except that the command will be applied on lines which don't match the [pattern] parameter. This way if we apply

:v/echo/d

On our previous example we get:

echo "List instantiated"
    echo i . " added to the list"
echo "end of for loop"

The delete command has been applied on lines which didn't contained echo.


Here I hope that those few hints will give you ideas on how to get rid of your multi cursor plugin and use Vim in the Vim way ;-)

As you can imagine these examples are pretty simple and are just made to demonstrate that when you follow the Vim way you really rarely need several cursors. My advice would be when you encounter a situation where you think it would be useful, write it down and take some time later to find a better solution. 99% of the time you'll eventually find a faster/more efficient way to do it.

Also I will repeat myself one more time but I really encourage you to read Practical Vim by Drew Neil because this book is not about "How to do that or this in Vim" it is about "How to learn to think in the Vim way" which will allow you to built your own solution to your future problems in a good way.


PS Special thanks to @Alex Stragies for his editing work and the corrections he made to this long post.

  • Very complete answer. I actually did know all those commands, but never thought of using them like that... I guess I need more practice. However, I must say that sometimes the 'vim-way' takes longer than just using some plugin (for example, your CSS example can be done much quicker with the plugin I mentioned), and sometimes it takes much shorter (actually, the vast majority of times vim is faster than any text editor, if used properly). – tomasyany Aug 13 '15 at 22:18
  • Btw, I think your answer is already pretty complete. I will accept it, but if you want to add more details, please do and I'll be glad to read them. – tomasyany Aug 13 '15 at 22:20
  • 1
    @tomasyany: Indeed sometimes using the "vim way" might seem longer (I can't count how many times I spend more time on creating a working macro than if I had done the changes manually) but with the practice it eventually get faster. I've been using Vim for only one year and I'm (very) far from being an expert but I can feel that my editing continuously improves as I practice. – statox Aug 14 '15 at 9:22
  • 1
    Should probably mention the gn text object as well. It addresses some use cases of multiple cursors. Search with /, then dgn or cgn, then repeat with .. – Justin M. Keyes Aug 15 '15 at 1:17
  • @Justin I'm not really familiar with gn I'll give it a try and add it after. Thanks for the head up! – statox Aug 15 '15 at 7:52
5

Your given use-cases to me sound like to want to know the . key command (repeat last Editing command)

  • Go to first place, press i, insert your text, <ESC>
  • move to location 2, press . (Dot) ==> Identical Text is inserted
  • To move down one line here and insert again (when inserting into consecutive lines), press <ALT>-j.

Your tex example for me is solved by:

I\section[]{}<ESC>hhiMy first Sec...<ESC>lll.

  • It's a pretty nice solution. I knew perfectly the . key command, but never thought of using it that way. I agree it is a nice way. But your answer doesn't actually refer to the real question, so it wouldn't be appropriate to accept it I guess. – tomasyany Aug 13 '15 at 21:15
  • 1
    Well, your question (after you added the desired use-cases) reminded me of "I really like my scissors, and want to tighten a screw with them. Tell me how". And me pointing you to a nifty use for a screwdriver ;) (I added as an answer, because i don't know, how to do formatting in comments) – Alex Stragies Aug 13 '15 at 21:24
4

More Vim ways.

First task:

\section[foobar baz]{foobar baz}

Workflow:

\section[]{}
hh
ifoobar baz<Esc>
$.

Second task:

variable1 = 2
my_variable2 = 12
var3 = 14

Command:

3:norm ea_somestuff

Result:

variable1_somestuff = 2
my_variable2_somestuff = 12
var3_somestuff = 14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.