codecompleting
codecompleting

Reputation: 9611

Could vim automate the refactoring of this snippet? is that what macros are for?

I have blocks of code that look like:

ICar car = new Car<int>(10);
var result = car.Start(100);

Assert.IsTrue(result.IsValid);

That I want to convert to this:

Assert.IsTrue((new Car<int>(10).Start(100)).IsValid);

I have about 20 of these types of snippets with the exact same format, could this be automated in vim?

Upvotes: 1

Views: 199

Answers (3)

Idan Arye
Idan Arye

Reputation: 12603

Macros are the easiest, but another way to do it is with global commands - :g/regular expression/Ex command. For example(not your example - we will get to it later), you can use :g/^\s*ICar/delete will delete all lines starting with ICar(^ is for start of line, \s* is for skipping the tabs and spaces used for indention).

The advantage of this method over macros is that you can use it on a range: go into visual mode, mark the part you want to refactor, and use the global command. Only matches in the marked block will be affected. If you use macros, you need to either press @@ over and over again until you clear the block, count the exact number of times you want the macro to run, or set a high number and make the no-match error stop the macro. While the third option is quite easy to execute, it's also quite dangerous - you need to make sure the pattern appears only in the parts you want to refactor - so it won't affects unrelated parts of the code - and that the refactoring removes it - otherwise the macro will run on the same lines over and over again.

The advantage of macros is that they are easier to record. In complex refactoring like yours, the global command you need to run can be very long and complex. A macro to do the same thing is just as long and complex as a global command - but you can see how it works while you record it - much easier than calculating the result in your head while designing the global command.

Luckily, you can enjoy both world!
First you record your macro like cdleonard explained in his answer, with two main differences.
The first one is that the first keystroke in the macro should be ^ or _ - to go to the first non-white-space character in the line(that means you start with qq_ and then record as usual). This will guarantee the macro starts from the right place in the line every time.
The second difference is that you don't need to go to the next snippet in the end of the macro. The global command will take care of that for you.

After you've recorded the macro(I'll assume you recorded it to q) mark all the snippets using visual mode, and type :g/^\s*ICar/norm @q - this will search the marked range for all lines that begin with ICar(possibly with indentation before them) and performs the macro on them. This is assuming everything in the range that begins with ICar - and only those places - are snippets you want to refactor. If you have lines that begin with ICar and you don't want to refactor, or if you have lines that you do want to apply the macro to, but they don't begin with ICar - you will have to modify the regex.

Upvotes: 3

Zsolt Botykai
Zsolt Botykai

Reputation: 51603

:%s:^.* = \([^;]\+\);\_.[^.]\+\([^;]\+\);\n\n\+\([^(]\+\)(.*\.\(.*$\):\3((\1\2).\4

Will do it with the exact same format (placement of .s and =, etc are important in the original pattern.

HTH

Upvotes: 3

cdleonard
cdleonard

Reputation: 7050

Crash course in macros:

  • Go to ICar in normal mode.
  • Press qq to start the macro.
  • Modify the code. Try using word-based movements instead of left/right arrows.
  • Go to the next snippet, like with /ICar.
  • Press q again in normal mode to stop recording.

You can then type @q to execute the q macro and reformat one snippet. If it works as expected then type 20@q to execute 20 times.

Upvotes: 5

Related Questions