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

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?

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.

:%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

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.

Related

Vim : How to insert a / after certain length in multiple line

Here is following piece of text (a c++ code) which I am trying to edit in vim,
#define MACRO(X) /
{ /
if(x)
{
"some action performed here"
}
}
I want to complete this macro syntax by introducing / at each line. For aesthetic reasons I want the / to be aligned at same line length like how it is done for first two lines. How to achieve this in a single or few Vim commands. Assume that macro is very big in line count and I cant manually introduce space and / at every line
First of all, I am confused that the "macro" you meant in your question is a vim macro or your function named "MACRO(X)"?
To solve the problem you need set ve=all read :h 've' for detail.
If you meant the "macro" is a vim one, that is, you want to extend an existing vim macro, it is hard to tell how to do that. That's because we cannot see the existing macro, what does it do.
I list here two ways to do it, one is using a vim macro, you can put it into your existing one and test if it is required. The other one is using :normal command.
Assume that you've set ve=all
Assume that you want to add a / on column 50
vim macro
First record a macro a:
qa050lr/jq
Then you can replay it x times, e.g. 99#a
normal command
%norm! 50lr\
Two more ways to do the same thing.
If set ve=all or set ve=block using blockwise-visual mode.
$<C-V>6jr/
That is, go to an existing "/" at top line. Then enter blockwise-visual mode. Then extend selection downwards. Then replace everything with "/"
Using just :s
1,7s/$/\=repeat(' ', 49 - strlen(getline('.')))..nr2char(47)
That is, substitute "end of lines" 1 through 7 with an expression (variable number of spaces followed by slash).

Is there a better method for find and replace in Vim?

Edit: I moved this over to the Vi and Vim site: https://vi.stackexchange.com/questions/13689/how-to-find-and-replace-in-vim-without-having-to-type-the-original-word
I'd like to optimize my "find and replace" workflow in Vim. It's something I do often, as I'm sure most of you do too. Usually something along the lines of -- copy a block and change the name of a variable in a few places. I know, I know, that probably triggers your "why are you copying and pasting code" reflex, but let's not go down that road... There are plenty of valid use cases :)
I'm well aware of the search and replace commands: :s or :%s but I don't like them. It forces me to type out both the full variable name I'm searching for and what I'm changing it to. Maybe there is a better way fix the the amount of typing with :%s? I often use long descriptive variable names, so that is really a deal breaker for me. I also don't like how typing out a variable name from scratch is typo prone and can consume time and brainpower hunting down typos. I much prefer typing it once, and then copying and pasting to just avoid this entirely if possible.
My current workflow uses some combination of movement/yank/select/search/put to move around the file and replace one by one. It is not great but has the benefit of avoiding typing out full variable names. I might just need to type the first few letters with / or use another movement command (i.e. fx) depending on what's around and then hit ve to select the whole word. I also don't mind that I have to repeat for every instance. I never do a full find replace without confirming each change. But it would be much preferable if I could repeat the replacement action with a single keystroke (which I can't do with this method). each replacement is usually something like n then ve then p (or even worse "0p)
Is there a faster way?
My own workflow is similar to yours:
To start, get the cursor on one instance, possibly with / or by navigation.
Hit * to find the next instance of that word.
Change one instance with cw and then the new variable name.
Then it's fast: n/N to get to the next/previous instance, and . to repeat the last edit.
This workflow gives me the same advantage as yours, in that I can review each case before applying the change, but it's just two keystrokes for each additional change.
Hope this helps.
I like the "visual highlight then edit" approach.
shift + v to highlight the region that you want to modify.
then :s/old/new/r where old is what word you want to replace with new.
r changes the first instance of that word old.
Note* There are options other than r which modify its behavior how you want to replace the word.

Toward Vim moves from conventional moves (<left> <right> <up> <down> <backspace>)

I am not trying to play golf with my editor. I am just trying to improve my editing skills with vim.
Let's consider this piece of assembly that I would like to convert to C. In order to do it methodically, I want to make small changes iteratively line after line.
dm(__abcd_bar_id + axis) = f4;
f1 = dm(_abcd_foo_id + axis);
f5 = f4 - f1;
The job with this example is:
Simplify the first line with abcd_bar_id[axis] = f4
Simplify the second line with f1 = abcd_foo_id[axis]
Replace f1 in the third line with the second line
Remove the second line
These steps are not negotiable. I know I can easily get rid of all my dm(__variable + index) with a regex like the one below but this is off topic.
:%s/dm\s*(\s*_\+\(\w\+\)\s\++\s\+\(\w\+\)\s*)/\1[\2]/g
So, to achieve these changes I traditionally do this:
▶▶▶▶DelDelDelDelDel▶▶▶▶▶▶▶▶▶▶▶▶[DelDelDel▶▶▶▶Right]
▼DeleteDelDel[▶▶▶▶]Del
Home▶▶▶▶RightDelDelDelDel
Shift+End Shift+◀ Ctrl+c
▼End◀◀BackspaceBackspace Ctrl+v
And the result should be this:
abcd_bar_id[axis] = f4;
f5 = f4 - abcd_foo_id[axis];
What saves me is I am quite fast hitting the same key multiple times. However I am sure I can be more productive if I use vi features
vfahd
wh3lxi[wr]
j:%s/dm(_//Enter
f+hv2lxi[Escwr]
$hvF2ay
jf1hhplxxx
Well, this seems to me much more complicated for my brain because a pre-processing bain-time is needed before each keystrokes.
For instance if I want to move to f1 I need to parse with my eyes if there is no other 1 on the way to f1.
I really feel I need years of training to be 'fluent' with vim.
So the questions are:
How a vim guru will treat this example?
Does a vim guru exist?
I definitely don't consider myself vim guru, although I use it on the daily basis. Answering your second question first, probably there's somebody who can be treated as a guru, there are simply so many options and possibilities in vim, that everybody can have their own way of doing things. Moreover, because you can tailor vim to your needs, it's easy to simplify regular tasks, and those configurations may differ a lot. Also people who are considered gurus by me (like, for instance, Derek Wyatt) claim that have still much to learn about vim, so it can definitely take years to become one.
But don't be discouraged, it takes only some practise to start thinking vim-way, and your editing tasks will become much easier :)
Back to your example. First of all, I'd edit the first line with slightly less keystrokes:
dta
f)r]
bdTd
i[
The difference isn't huge in terms of number of keystrokes, but it illustrates different approach. It allows, in my opinion, much less pre-processing, which is the problem you highlighted. I divided those keystrokes into sections to show you my thought process:
delete till a
find ) and replace it with ]
back one word and delete Till (backwards) d
insert [
I don't have to think much, when I apply those changes. You might think that this is counter-intuitive, that I jumped to ) character first, but it was much easier for me to spot closing bracket than count words or
hit h or l multiple times. Of course you might know the keystrokes but when you edit something you don't always remember all of them. This comes with practise and forcing yourself to use some of them (like t/T)
to put them firmly under your fingers. Also, print a cheat-sheet trying to make use of every key, until you'll learn it by heart. It won't take long ;)
As William already suggested in the comment, I'd also think about macro here. It's a powerful and easy-to-use tool, which can really automate your changes.
I already know how to edit first line. In your example, I know that in the second step I'll be doing the same thing, but in slightly different location, so instead of editing first line, I instantly record a macro, but I have to make it universal
for easier application. So I think about putting my cursor in proper location first, before making any changes. My macro would look like this:
qq
0fd dta f)r] bdTd i[
q
Notice, that I added 3 keystrokes at the beginning (not counting qq, which starts recording macro to q register). That might look redundant in the first line, but it ensures proper location of the cursor before making any changes.
That way I can easily apply this macro in the second line with #q
Now, you have to replace this f1 in the third line. You're still in the second line with your cursor, so you just yank with:
0fay$
and then paste it to the third line:
j$bPlD
Using macros mith look like a redundant thing when you edit just 3 lines, but when you get used to making changes in a vim way, you'll really feel you're taking advantage of it's power.
When it comes to remembering recorded macros it's not that hard, you have to have the proper attitude. First of all, you record your macros to registers, so typing :registers will show you also your macros. Secondly, you can edit them,
by pasting specific register, altering it and then saving to the same register. And then you can play it with #[register_letter]. And finally, don't get attached to specific macros. Save one or two, use them to make multiple changes at
once and forget about them. And then record another one under the same letter. For example, if you realize that you have to make some repetitive change across the file, use qq, because it's fast and intuitive. After making changes you rarely
need to play the same macro over again, because whole buffer is already in the right state. But if you know, that you'll need it, record next macro under another letter. If you'll get comfortable making changes intuitively vim way, so that
they can easily be repeted, you'll find that's much easier to record another macro than trying to remember under which letter you recorded previous one.
I hope that this answer will convince you, that you don't need years of training to get fluent, but of course it won't happen overnight ;)

trouble creating vim macro using search history

My problem is when I create a vim macro, I used some search and replace in the search and replace history. So I use the arrow key to go up in the history to find it. But the trouble is the macro only record my arrow key activity not the command I find. So when I execute the macro again, the search history is changed and the result are messed up. Is there any way to solve this?
Yes, you can solve this problem by understanding that macros record keystrokes, not the result of commands, and act accordingly.
For example, when you use the previous search with <C-r>/ or //, the actual content of the last search register is not recorded. The next time you play that macro, the last search pattern will probably be different and your whole macro will be busted if you expect otherwise. You must actually type the search pattern or perform the search as part of the recording if you want your macro to be reliable.
To get a better grasp of how recording works in Vim, you can paste the macro you just recorded (macros are saved in registers, just like the stuff you yank) and study what's there.
qq
(do stuff)
q
"qp
Remember this fact and don't use such history recall commands when you record a macro. It may be not as convenient, but you'll probably amortize the effort over the repeated macro applications, anyway.
Actually, the shrewd practitioner can use this behavior of macros as a feature. By e.g. referring to the last search pattern (e.g. :s//...) or recalling a partial command (e.g. :w foo<Up>), one can record macros that are applicable to a wide variety of situations.
Finally, you can "salvage" a macro after you realize it's broken; as its contents are stored in a register, you can just re-edit, e.g. via:
:let #a = <C-r><C-r>=string(#a)<CR>

How to provide parameter in vim macro

Is it possible to provide some parameter when recording a macro in vim via prompt or some other ways?
Edit:
I have such code:
foo
bar
And I would like to surround each with ruby block:
expect do
foo
end.to raise_error(CustomClass)
expect do
foo
end.to raise_error(OtherCustomClass)
So, it is easy to create a macro that will result with:
expect do
foo
end.to raise_error()
expect do
foo
end.to raise_error()
But it will be nice to have prompt that will be used to set raise_error method parameter. In each use of macro this parameter will be different.
While I agree with everyone else that if you need this feature, you're probably going about things inefficiently, it is possible to insert a variable text string into a document as part of a macro. The trick is to store the text you want to use in your macro in a register.
yank some text into a named register, for example "xyw to yank a word into the x register
record your macro, qq, when you want to place the variable text, put it, for example "xp to put the text in the x register into the document where the cursor is
now, when you play your q macro, #q, it will use whatever is currently in the x register, so you can yank different text into the x register, play your q macro, and the newly yanked text will be used.
If you are talking about recording a macro with qx...q, this is not possible.
However you could still do : :let #y = substitute(#x, 'old_pattern', 'replacement', 'g') and then use #y.
You could also define a function:
function Play(myArg)
execute 'normal sequence_of_characters' . a:myArg . 'other_sequence_of_characters'
endfunction
call Play('foo')
Very particularly in the OP's situation, where you really only have precisely two variable pieces of content, I find the easiest method to be a bastardisation of #mkomitee's approach above.
Instead of manually saving the two ‘parameters’ into registers before each usage of the macro, I prefer to type the “first parameter,” visual-select it, evaluate the macro, then type the “second parameter.” To achieve this, I start the macro with a deletion command (a simple d, assuming you're always going to invoke the macro in visual-mode, for instance); then finish it with a command that switches to insert mode (like c or i), and finally, while still in insert mode, a Ctrl-O q to cause the macro to also leave Vim in insert mode when it's done.
As a slightly simple example, if the two “parameters” are single words, here's the keystrokes to create (and then invoke) a macro to manipulate widget.snog() to a parameterised widgetFoo.fooSnog(bar):
foob qq "zdw — we're now recording to the q register, with the first ‘argument’ in z
‸
"aP — prefix-paste from a fixed register used elsewhere in the document
widget.snog()‸
^ea␣Ctrl-rEscb~hx — paste the first arg, and capitalize
widget‸Foo.snog()
2w~b"zP — capitalize existing word, then paste the first arg again
widgetFoo.fo‸oSnog()
$Ctrl-Oq — move to the last position, enter insert-mode, and end the macro
widgetFoo.fooSnog(‸)
After finishing the first instance with bar, we can now use it several times:
obazEscb — set up our first ‘argument’,
widgetFoo.fooSnog(bar)
‸baz
#qquuxEsc — invoke the macro, and finish with the second one
widgetFoo.fooSnog(bar)
widgetBaz.bazSnog(quux‸)
ocorgeEscb##graultEsc — repeat a third time
widgetFoo.fooSnog(bar)
widgetBaz.bazSnog(quux)
widgetCorge.corgeSnog(grault‸)
ogarplyEscb##waldoEsc — … and so on
widgetFoo.fooSnog(bar)
widgetBaz.bazSnog(quux)
widgetCorge.corgeSnog(grault)
widgetGarply.garplySnog(waldo‸)
Of course, it looks laborious, typed out in such a long fashion — but it's surprisingly few key-strokes in practice, and very easy to train into your fingers.
tl;dr: type the first argument; enter macro-recording before deleting it into a register; manipulate your text as desired; then leave vim in insert-mode at the position of the second argument with Ctrl-Oq.
If you need to generate a code, which is the case, the best way for this is to use vim snippets. You can configure snippet to put cursor where you need when you [tab].

Resources