4

I have a lot of difficulty doing things like the following in Vim:

From

1 'apple'
2 'orange'
3 'pear'
4 $fruit1 = 
5 $fruit2 =
6 $fruit3 =

To

1
2
3
4 $fruit1 = 'apple';
5 $fruit2 = 'orange';
6 $fruit3 = 'pear';

The best approach I can come up with is to record what I hope is a sound macro to make the first edit, then replay it multiple times to get the other lines. Even if I nail that macro the first time, it's complicated enough to require more thought than would Sublime's multiple cursors, and it's certainly more time consuming. In these cases I'm certainly not editing text at the speed of thought. How do you deal with this problem quickly and efficiently in Vim?

7
  • 1
    This seems less of a real question than a contrived example trying to show that multiple cursors are superior to macros.
    – ilia choly
    Sep 19, 2017 at 20:18
  • This is hardly a contrived example. Instances of this show up all the time in my workflow and I find myself copy-pasting to and from sublime text, which is annoying, because I much prefer to stay in Vim.
    – Kvass
    Sep 19, 2017 at 20:55
  • 1
    I think you'll find this answer of mine useful about multiple cursors. (The question might be a duplicate tho). For your particular use case, I think visual block mode as recommended by bew is probably the solution I would use too.
    – statox
    Sep 20, 2017 at 6:46
  • my example was contrived only in the sense that it was oversimplified -- the cases where this comes up are rarely neatly aligned enough where visual block could apply nicely. but it does work well for the example I presented here.
    – Kvass
    Sep 20, 2017 at 8:12
  • @statox that looks like a great writeup I'll give it a careful read-through
    – Kvass
    Sep 20, 2017 at 8:14

4 Answers 4

3

One way to do it (probably not the best):

^Vjj$d)$^VjjA ^[lpgvA;^[

(With ^V as CtrlV and ^[ as Escape)


Assuming the cursor is on the top left corner:

Select & delete the first 3 lines

^V => enter in block selection mode

jj$ => select the 2 lines below to the end

d => delete those lines (they get stored in the default register)

Add a space after all =

)$ => move to the next "paragraph" (here, the line 4) then to the end of the line (on the = sign)

^Vjj => select in block mode (the 3 = signs)

A ^[ => append a space to them

l => move the cursor on the space after the =

Paste the lines & add the ;

p => paste the 3 lines (copied at the beginning)

^Vjj$ => reselect what we just pasted (maybe a bad way to do it)

A;^[ => append ; at the end of the 3 lines

3

I would record a macro (as you said) that is pretty easy one:

qqdd2jpkJA;<ESC>2-q

What this does is:

  • qq record macro into register q
  • dd delete line
  • 2j move 2 lines down (to our respective variable)
  • p paste deleted text (it will be pasted as a line below)
  • k move back up a line
  • J join lines
  • A;<ESC> add semicolon
  • 2- move 2 lines up (to start of line)
  • q stop recording

The benefit is that this will also work for unaligned lines.

3
  • It's a minor point, but you've deleted lines 1-3 and the question wanted them to be left empty.
    – Rich
    Sep 20, 2017 at 9:34
  • Then it would be a little more hassle, but still easy doable with simple macro.
    – Hauleth
    Sep 20, 2017 at 9:57
  • It's not even any more hassle, really (e.g. see my answer).
    – Rich
    Sep 20, 2017 at 10:25
1

The tricky thing about macros is getting your head around what movements and text objects you can use that will work in the general case, so the macro doesn't break when you run it in another location. Generally, you want to avoid character-based movements and prefer searches and word-based movements.

Once you've internalised this, though, you should find that you can record most macros pretty much as fast as you could make a single edit, and will rarely make mistakes. Remember that undoesbackspaces and delete commands are recorded too, so many errors can be fixed while you are still recording the macro! (Edit: I'm not sure if it was ever true that you could use undo while recording a macro, but it's certainly not true in current versions: when played back, the u will undo all the changes since the start of the recording.)

However, in this example the edits you need to make are so well aligned that pretty much any movements you choose will work e.g.:

qqlD3j$pa;<esc>2-q2@q
  • qq Start recording,
  • lD Move one character to the left and delete to the end of the line,
  • 3j Move down three lines,
  • $p Jump to end of the line and paste the deleted text,
  • a;<esc> Add the semicolon,
  • 2- Move back up to the next line we want to work on,
  • q2@q Save the macro and replay it twice.

(N.B. I was under the impression when writing this answer that the numbers were actually part of the text you wanted editing. Reading the other answers it occurs to me that they may be intended as line-numbers. If so, a slight adjustment is required: qqD3jA<space>;<esc>P2-q2@q)

If, after practice, you find that you still have trouble with macros, you could of course try installing a multiple cursors plugin. I would note, however, that although the visual feedback such a plugin provides might help you catch errors sooner, the actual editing commands you need to use will be the same as required for a macro, so if you find that you can perform edits using the plugin without making mistakes and having to undo, that suggests you could also do so using a macro.

2
  • I would use 0D instead of lD. Just to be sure. Also you forgot about space after = so it would be A<Space><C-r>”;<ESC> instead of $pa;<ESC>.
    – Hauleth
    Sep 20, 2017 at 10:32
  • @ŁukaszNiemier Nope. See the N.B. below the description of the steps.
    – Rich
    Sep 20, 2017 at 10:36
0

Let's consider our cursor at the first line, column zero

f' ....................... jumps to the first single quote

Use visual block selection to cut the three first lines

Ctrl-v ................... starts visual block selection
2j ....................... expands to the next two lines
$ ........................ makes selection expand to the end of the lines
A;<Esc> .................. inserts semicolon at the end of selected lines
gv ....................... repeat selection
x ........................ cut contents

Now lets jump to the second part

3j ........................ goes down to the fourth line
A ......................... starts insert at the end of the third line
Ctrl-r Ctrl-o " ........... paste our cutted lines

For more see: :h i_Ctrl-r Ctrlr Ctrlo inserts the content of a register literally and don't autoindent

If you want to set all these keystrokes as a macro:

:let @a="gg0f'\<c-v>2j$A;\<esc>gvx3jA\<c-r>\<c-o>\"\<esc>"
@a

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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