3

I have this:

Alice --> Bob
Dave --> Eve

and want to change that to this:

Bob <-- Alice
Eve <-- Dave

Changing from --> to <-- can easily be done with :s/-->/<--/, but swapping Alice and Bob requires me to delete - jump - put multiple times.

What's a good way to swap Alice & Bob around the --> word? Is there a command to swap words?

3

You could use numbered sub-expressions outlined in the reference manual in :h /\1 and :h /\(, and introduced in the user manual in :h 12.2. To simplify, I'm assuming that you have a line containing only the following:

Alice --> Bob

Then, you could use the following substitution to do the entire operation in one go:

:s/\(\w\+\) --> \(\w\+\)/\2 <-- \1

This would result in:

Bob <-- Alice

Broken down:

:s/.../... - substitution
\(\w\+\)   - a sub-expression consisting of one or more word characters ([0-9A-Za-z_])
\1         - replaced with the first sub-expression
\2         - replaced with the second sub-expression

If that isn't the only text on the line, you will have to do some strategic positioning of :h /\zs and :h /\ze to narrow your match to the desired text.

2

You can do it with a substitute command as others suggested.

There is also a way to do it with normal! command:

%normal! dwwPxdw0Pexbi <

It just runs your keypresses: delete word, next word, Paste, delete a char (space), delete a word, goto beginning of a line, Paste, goto end of word, delete char (>), goto begining of a word, insert <.

Then

Alice --> Bob
Dave --> Eve

would become:

Bob <-- Alice
Eve <-- Dave

PS, you can also do it with a macro.

1

One way to automate repetitive tasks is to [record a macro] with a sequence of commands to repeat on every block.

For this particular case of swapping words, one useful technique is to use a visual selection for the second word and use a "put" command that will both replace it with the contents of the default register, but also send the overwritten contents to the default register. That way you can use the next put to include them in the first location.

In your case, assuming you're at the start of a line, for example on the "A" in "Alice". You could then use dE to delete that word (leaving the whitespace intact.) Then 2W to skip the arrow and move to the beginning of "Bob". Use vE to create a visual selection around "Bob". And then you can use p to replace it with "Alice", while sending "Bob" to the default register. At this point, 0P will move back to the beginning of the line and put "Bob" where "Alice" used to be.

If you're recording a macro, end it with a + to move to the beginning of the next line. For example, recording it to the macro "a":

qadE2WvEp0P+q

And then you can replay it with @a, or replay it a large number of times (it will stop when there are no more.lines) with 9999@a.

Instead of a macro, you can also use this expression with a :normal command with a range, which will repeat it for every line:

:%norm dE2WvEp0P

In general, the advantage of a macro is that you have visual feedback while you're recording it, so it's often easier to adapt to commands behaving in an unexpected way.

You can either handle the arrows through a separate global :s command, or you can incorporate that into your macro recording too, after you switch both terms (to avoid messing with the default register in between.)

One other advantage of macros over :s commands is that it's often easier to use them to implement changes that affect multiple lines. For instance, if your name pairs were separated in blocks of multiple lines and your "Alice" and "Bob" were in distinct lines, using a macro is often more straightforward than a :s matching a multi-line block and trying to group multiple parts of it. As long as you're using general commands and anchors that will be valid on every block, you should be good to go.

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.