How to provide parameter in vim macro - vim

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].

Related

What is the VIM way to copy a list of variables (a list of words vertically aligned with different widths)?

let(:aaaaa) { 123 }
let(:bb) { true }
let(:ccc_ccc) { "foo bar" }
I want to copy all variable names (:aaaaa, :bb, :ccc_ccc).
In VsCode, for example, I would use a multi-line selection.
How can I do it in VIM?
Block selection didn't work, once the variable names have a different length.
You could use the command :%norm f:"Qyt) to make your 'q register' contain the
following:
:aaaaa:bb:ccc_ccc
The way it works is as follows:
:%norm means 'to all lines, apply the following normal commands'
f: moves the cursor to the first colon on the line
"Qy appends yanked text to the 'q' register
t) is a motion 'till the next closing parenthesis
This assumes that the 'q' register is already empty (you can use qqq to clear
it). If you only want to do this for a subset of lines, you'd replace the %
with a range (or visual selection).
What you do with the register's contents after that is up to you.
"qp will put them into the buffer, and maybe you'd then do :s/:/\r:/g to
split the lines at the colons like this:
:aaaaa
:bb
:ccc_ccc
If your immediate goal is to have something like this in the default register:
:aaaaa
:bb
:ccc_ccc
then it won't be easy to achieve without the ability to visually select multiple non-contiguous pieces of text of arbitrary length, which Vim doesn't have out of the box.
This means that, if we don't want to use a multiple cursors plugin, we are left with more pedestrian ways involving substitutions, macros, etc.
Assuming the cursor is on the first line, you could do something like:
:,+2t+2 " copy the block below itself
:'[,s/.*(\(.*\)).*/\1 " remove everything you don't need
:'[,d " cut the three lines to the unnamed register
But Vim works best when it is used with intent. "Copy this" is rarely a goal in and of itself: it generally is one of the several low-level steps necessary to complete a higher-level task (which itself might be one of the steps of another even higher-level task). What one intends to do with the copied text often plays an important role in choosing the best strategy. Here, your actual goal may have been to get the three variable names on three lines right below their definition, something that actually doesn't imply copying them, so the two first steps would have been enough.

Paste the content from dot . command in vim

Given the word foo in a text, if I type ciwbar<ESC> while recording it as a macro in w register and then type in the command line
:<C-r>w it will brings me :ciwbar^[
However if i type :<C-r>. it will only brings me bar
But both commands, the #w and dot, are able to perform the change inner word bar in other parts of my text
Is there any way to get all the content from the dot as I got the w register? I would like to paste it and work to automate the last command in macros in some steps forward
The . register and the . command are not the same thing. The . register stores the last text you have inserted and the . command repeats the last non-movement normal-mode command you executed.
Rather than try to force it to work another way, why not call a different registry and store values in there? The . registry will be overwritten by the last inserted text but many of the other registries are only changed when you actively tell vim to, so you can fill them yourself with the exact keypresses you want and call them again later.
Type
:reg
To see a list of what is currently inside each of your registries
Also have a look at this fantastic resource explaining vim's registers
https://www.brianstorti.com/vim-registers/

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?
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.

Enclosing the function call in another function call (retval as parameter)

Having this LOC:
printf("%s (%d)\t(%d)\t%d-%d\t", meta_scanner_token_name($ret['major']), $ret['major'], (string)$ret['dirty'], $ret['start_line'], $ret['minor']);
What is the fastest way in terms of key strokes to enclose the call to meta_scanner_token_name in another function call to foo, yelding:
printf("%s (%d)\t(%d)\t%d-%d\t", foo(meta_scanner_token_name($ret['major'])), $ret['major'], (string)$ret['dirty'], $ret['start_line'], $ret['minor']);
given that
first scenario: my cursor is on 'm' at the beginning of the function?
second scenario: my cursor is somewhere on meta_scanner_token_name?
va)oB would select the entire line, and ys%) would enclose only the m, resulting in:
... (m)eta_sca...
Please answer to both scenarios.
(I am using spf13-vim with default settings except some visual changes, if that has any relevance)
ifoo(<Esc> then f)i)<Esc>
bifoo(<Esc> then f)i)<Esc>
but I'm still a Vim noob
-- EDIT --
I see "Surrounding.vim" is a modified version of "Surround.vim" if it's compatible with Surround you can do:
Scenario 1
vt,sffoo<CR>
vt, to select everything until the first ,
s to launch Surround.vim
f to instruct Surround to input a "function"
foo the identifier
<CR> Enter key.
That's 6 keystrokes not including typing foo which — I think — can't really be avoided.
Scenario 2
bvt,sffoo<CR>
It's the same as scenario 1 except that you type b first to go back to the first letter of meta_scanner_token_name.
Using normal vim you could do this (prefix with b for scenario 2)
`cf)foo()<esc>P`
If your vim plugins add the closing paren for you, you can drop that from the sequence. Depending on where it leaves your cursor, you might need to use p instead of P.

Append general buffer to the end of every line in VI

I'm trying to add the contents of the general buffer to the end of every line. I'm sure this is fairly simple, however, an hour of google searches have lead me nowhere.
This is what my file looks like
::Things to bring camping
--matches
--tent
--sleeping bags
--inflatable bed
--firewood
--camping stove
--skillet
I want to add "::Things to bring camping" to the end of every line.
This is i have figured out so far.
/:: -> brings me to the line in question
Y -> yanks the entire line to the general buffer
I tried :%s/$/\p -> this added a "p" to the end of every line.
My problem is with step 3. How do I tell the "search and replace command" to used the "p" (the contents of the general buffer) instead of the "p" the character
Thank you so much for your help.
Just a suggestion: If you try doing it with a macro, you will be able to use 'p' to add the contents of the general buffer.
Sorry, I had to go into vim and find out.
The way to copy your entire line while in command mode, is:
^r "
(that's CTRL and r, then " )
That should paste the entire line you yanked into your search and replace command
For step three, instead of \p, you should use ctrl-R-a. Hold down the control key and type an uppercase "R", continue holding control, and type a lowercase "a".
For a line with multiple words, use ctrl-R-" instead.
I agree with using a macro - they're very powerful.
In this case I took your list example and positioned it at the first colon.
I used y$ to grab the remainder of the line in the buffer.
Then I recorded the macro - I chose 1.
q1
j$pq
Then you can call it for any number of rows in your list. E.g. 10#1
Learned something figuring this one out ...
:%s/$/\=getreg()/
The \= says that what follows is an expression to be evaluated, and the getreg() call gets the contents of the register, by default the "general buffer" as it used to be called by vi.

Resources