tags:

views:

354

answers:

4

Is there an easy way to flip code around an equal sign in vi/vim?

Eg: I want to turn this:

value._1 = return_val.delta_clear_flags;
value._2._1 = return_val.delta_inactive_time_ts.tv_sec;
value._2._2 = return_val.delta_inactive_time_ts.tv_nsec;
value._3    = return_val.delta_inactive_distance_km;
(...)

into this:

return_val.delta_clear_flags = value._1;
return_val.delta_inactive_time_ts.tv_sec = value._2._1;
return_val.delta_inactive_time_ts.tv_nsec = value._2._2;
return_val.delta_inactive_distance_km = value._3;
(...)

on A LOT of lines in a file.

I know this seems a little trivial, but I've been running into lots of cases when coding where I've needed to do this in the past, and I've never had a good idea/way to do it that didn't require a lot of typing in vim, or writing a awk script. I would think this would be possible via a one liner in vi.

Explanations of the one-liners is very welcome and will be looked upon highly when I select my accepted answer. :)

+10  A: 

Something like this:

:%s/\([^=]*\)\s\+=\s\+\([^;]*\)/\2 = \1

You might have to fiddle with it a bit if you have more complex code than what you have shown in the example.

EDIT: Explanation

We use the s/find/replace comand. The find part gets us this:

  1. longest possible string consisting of non-equal-signs, expressed by [^=]* ...
  2. ... followed by a space or more, \s\+ (the extra \ in front of + is a vim oddity)
  3. ... followed by = and another space or more, =\s\+
  4. ... followed by the longest possible string of non-semicolon charaters, [^;]*

Then we throw in a couple of capturing parentheses to save the stuff we'll need to construct the replacement string, that's the \(stuff\) syntax

And finally, we use the captured strings in the replace part of the s/find/replace comand: that's \1 and \2.

scrible
Those are actual code snippets.
sheepsimulator
I understand. I meant to say that if other parts of your code are more complex, e.g. have more than a single '=' or a ';' on a line (even in a quoted expression) my solution will not work without further tweaking.
scrible
That's fine. Usually when I have to flip stuff like that, they lines are of the format "lvalue = rvalue;" above, so your solution hits the nail on the head.
sheepsimulator
Is it a problem that this one _requires_ spaces around the equals? It won't work for "lvalue=rvalue;" I believe. depesz's solution doesn't have that problem.
wxs
Spaces are (technically) optional, but I usually write assignment statements with spaces between the lvalue,rvalue and the operator.
sheepsimulator
+4  A: 
:%s/^\s*\(.\{-}\)\s*=\s*\(.\{-}\)\s*;\s*$/\2 = \1;/

should work nicely.

depesz
This works, I tried it, but I'm not sure about the \(.\{-}\) parts work. I know those match the code pieces on both sides of the equals sign. Can you explain how those code segments work?
sheepsimulator
Ooooh... I get it... match any character, with any number of instances...
sheepsimulator
... and don't be greedy, unlike '*'
Luc Hermitte
+5  A: 

For interest's sake, here's how I did it as a recorded macro:

qq0"+df=xA<BACKSPACE> = <ESC>"+pxi;<ESC>jq

Peforming that on the first line sets the "q" macro to do what's required. Then on each subsequent line you can execute the macro by typing:

@q

or, say you want to apply the macro to the next 10 lines:

10@q

I always find macros easier for a quick switch like this than figuring out the regex, because they're essentially an extension of how I would do it by hand.

Edit: Dan Olson points out in his comment that if you want to then apply the macro to a range of lines, for instance lines 6-100, you can enter the following. I don't know if there's a more concise syntax that doesn't require the ".*" pattern match.

:6,100g/.*/normal @q

Explanation of the macro

  • qq
    • start recording in register q
  • 0
    • go to beginning of line
  • "+df=
    • delete up to the '=' and put the text into the '+' register
  • x
    • delete extra space
  • A
    • go to end of line and enter insert mode
  • <BACKSPACE> = <ESC>
    • Delete the semicolon, insert an equals sign and a space
  • "+p
    • insert the test copied earlier into register '+'
  • xi;<ESC>
    • reinsert the trailing semicolon
  • j
    • move down to the next line, ready to reapply the macro
  • q
    • stop recording
wxs
I didn't know you could record macros like that in vim.
sheepsimulator
I always find that to be one of the killer vim features; before macros I would often end up in one of those repetitive loops performing the same brainless action on 20 sequential lines.
wxs
I tend to use macros like this a lot in combination with :g... for exmaple :g/something/normal @q
Dan Olson
+1 to Dan Olson. I guess that'll be my handy new Vim feature for the day.
wxs
you can use ranges directly with the normal command ":.,+20 normal @q". I sometimes select a range of lines (shift+V) and then do a :normal <command-mode commands>
Leonardo Constantino
A: 

This "question" was already "answered" on vimtips.

Luc Hermitte