1

I was wondering what would be the most Vimish way to the following: I want to invert the text on the right side of the equal sign to the left (and the left to the right). For example, I want this:

v1 = a1
v2 = a2

to become:

a1 = v1
a2 = v2

Thanks

  • Welcome to Vi and Vim! Are your variables always the same length? (In other words, are the =s aligned?) Asking about it because you could use blockwise visual mode in that case... – filbranden May 13 at 3:13
  • 1
    Yes I was thinking varying lengths, so thanks for your general solution! – user191919 May 13 at 19:27
2

Record and replay a macro

You can record a macro to handle each line, and then replay it to handle the following lines using the same sequence of commands.

Let's make a somewhat more interesting example, with varying lengths of variable names, with unaligned =s, but fixed single space around the =s. Let's also add some indentation to the lines:

    v1 = a1
    v2 = a2
    v345 = a345
    v67890 = a67890

In order to swap left side with right side, use the following operations. Assume we're starting with the cursor on the first line of the block. Then:

  1. Use q, q to start recording a macro into register "q.
  2. Use ^ to go to the first character in v1, the cursor will be on the v.
  3. Use ", a, d, W to delete a word (sequence of non-whitespace characters) into register "a.
  4. Use ", b, d, 3, l to delete the next 3 characters (which are =, the = with its surrounding spaces) into register "b.
  5. Use $ to move the cursor to the end of the line.
  6. Use ", b, p to paste the contents of register "b (the = with its surrounding spaces.)
  7. Use ", a, p to paste the contents of register "a (the variable name that was on the left side.) At this point the first line will have been fixed.
  8. Use + to move the cursor to the beginning of the next line (using j to move down would also work here.) Including the move in the recording means we'll be put on the next line after each replay of the macro, so we'll be ready to execute it again on the following line!
  9. Use q to stop recording the macro, which will have been stored in register "q.
  10. Use 3, @, q to replay the macro 3 times, handling the second through the fourth line. This will repeat operations from steps 2 through 9, three more times in total.

You'll end up with the desired result:

    a1 = v1
    a2 = v2
    a345 = v345
    a67890 = v67890

This method can be quite flexible, as long as you use meaningful motions while recording the macro, in order to have it successfully replayed on different lines.

You could have handled distinct formatting easier, for example df= to delete all the way to the =, or vf=wh"bd to use Visual selection, go to the =, then skip whitespace to the right of it by going to the start of the word that follows it and moving left one character, then deleting that Visual selection into register "b. Using insert mode or appending to the end of the line with A can also make it into the recording.

You use the @ command to replay a macro from a register. You can also use @@ to replay the last macro you used, which is very useful when you're interleaving a macro replay with other movement commands or other edits, you can find the next location then simply tap @@, hitting the same key twice, to execute a complex operation using a sequence of steps.

| improve this answer | |
1

Use a :substitute command with a regex

Using a :substitute command is pretty Vimish! This method will be good for you if you feel comfortable with Vim regexes, or if your input is uniform enough that you can use a simple regex to match it.

A simple way would be to use Visual mode to select a range of lines where to apply the transformation, then use the following command:

:s/\(.*\) = \(.*\)/\2 = \1/

This is somewhat naive in that it assumes the lines have a single =, have no indentation, always have a single space around the = and so on.

(When you type : with a Visual selection, you'll see :'<,'>, that's normal and expected, it means the next command will apply to the range of lines from the Visual selection.)

Perhaps a somewhat more robust version of the regex would be:

:s/\(\s*\)\(.\{-}\S\)\s*=\s*\(.*\S\)\s*/\1\3 = \2/

This takes care of many corner cases, but it's of course a lot more complex... Or, as the quote says:

Some people, when confronted with a problem, think “I know, I'll use regular expressions.” Now they have two problems.

😁

| improve this answer | |
0

Use blockwise Visual mode on aligned columns of text

If the = signs are aligned, you can use blockwise Visual mode to execute this replacement.

Let's make a somewhat more interesting example, with varying lengths of variable names, but still aligned =s. Let's also add some indentation to the lines:

    v1     = a1
    v2     = a2
    v345   = a345
    v67890 = a67890

In order to swap left side with right side, use the following operations. Assume we're starting with the cursor on the first line of the block. Then:

  1. Use ^ to go to the first character in v1, the cursor will be on the v.
  2. Use Ctrl+V to start blockwise Visual mode.
  3. Use 3, j, e to move the cursor to the 0 in v67890. The Visual selection will be a rectangle of 4 lines and 6 columns, comprising of all variable names on the left side.
  4. Use y to yank that block into the default register. The selection will go away and your cursor will be back to the first line.
  5. Use 2, w to move the cursor to the a in a1.
  6. Use Ctrl+V to start blockwise Visual mode, and then 3, j to select the as in all 4 lines.
  7. Use $ to select all the way to the end of the line. The selection will no longer be rectangular (since lines are uneven) but it will comprise all the variable names.
  8. Use p to paste the contents of the default register, overwriting the contents of the selection. At this point, you'll have your lines with v... = v.... Importantly, the contents of the selection that were just overwritten were now sent to the default register.
  9. Use ^, followed by Ctrl+V, then 3, j, e to select the left side again.
  10. Use p to paste the contents of the default register, overwriting the contents of the selection. This time you'll replace the vs on the left side with the as that came from the right side.

You'll end up with the desired result:

    a1     = v1
    a2     = v2
    a345   = v345
    a67890 = v67890

This method will still work if the left and right blocks have distinct widths, or if there are variables with longer names in the middle of the block. (Using the $ command on the right side ensures they'll be all selected. For the left side you might need a command other than e to select all variables, perhaps t=h will do.) The only pre-requisite is that the blocks are aligned.

The key command that makes this work is p in Visual mode, which pastes the contents of the register, replacing the current selection, while at the same time sending the overwritten contents to the default register.

| improve this answer | |
0

Use the :normal command with a sequence of steps

This is similar to the answer using a macro, but instead of recording the steps, we'll pass them to the :normal command and use a range to execute the sequence on every line.

We can use the following command:

:.,.+3normal ^"adW"bd3l$"bp"ap

The .,.+3 part is a range and it means from this line until 3 lines below this one, in other words, apply this to the 4 lines starting with this one. You can also type 4, : and Vim will automatically turn that into :.,.+3 for you.

Yet another option is to use Visual mode to select the lines and then hit :, which Vim will turn into :'<,'> and that will do the same, assuming the same line range was selected.

The sequence of commands passed to :normal matches steps 2 through 9 of the answer using a macro exactly.

Using a macro has some advantages, such as giving you visual feedback as you go on the initial line and also making the movement to the next line part of the recording, so making that part more flexible than just a range.

But using :normal can be pretty useful, for example if you want to turn that into a function or a script you can source, or when seeing the sequence of Vim normal mode commands is more important to you than seeing their immediate effects on the first line you apply the transformation to.

You could quickly adapt that to a more complex sequence of commands. For example, to handle spacing more flexibly around the =, you could use:

:.,.+3normal ^dWvf=wh"_dA = <Esc>p

Here we're using vf= to start Visual mode, then select all the way to the =. Then wh to select all whitespace on the right side of it, by moving to the first character of the next word, then one motion left from it. We're then deleting that into the black hole register, so we're not clobbering the variable name we have in the default register. Finally, we're using A to enter insert mode appending to the line, adding a fixed = (an = with a single space surrounding it on each side), then leaving insert mode with an <Esc> before finally putting the variable name that was initially on the left side.

One important point here is that we shouldn't enter <Esc> as five separate characters <, E, s, c, > here, that wouldn't work. Instead, we want to enter a literal <Esc> keypress here, which we can enter with the sequence of Ctrl+V, Esc, and it will be displayed as ^[, which is the normal representation for an <Esc> character in Vim.

Alternatively, we could have used the :execute command to enter the :normal sequence from a string, in which case we could have used a \<Esc> sequence inside a double quoted string to enter that particular keystroke. In this case, we'll also need to use \" for the access to the register:

:execute ".,.+3normal ^dWvf=wh\"_dA = \<Esc>p"

This one is slightly more complex, but its main advantage is that you can easily copy and paste it, since every character is typed literally. It's also convenient for storing into a Vim script file, since then you don't need to insert literal control characters into the file, you can keep the file with printable characters only.

| improve this answer | |

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.