Reputation: 1634
Lets say I have the following text file
name: John Doe
description: My name is John Doe and I'm really good at vim!
name: John Doe
description: My name is John Doe and I'm really good at vim!
name: John Doe
description: My name is John Doe and I'm really good at vim!
name: John Doe
description: My name is John Doe and I'm really good at vim!
Is there a way to record a macro that does the following:
Starting at the first John
:
cw
but allow the user who runs the macro to give input here
js<is>w
sneak the word after is
.
replace John with the given inputIdeally I would like to enter the following keystrokes on the first line:
@qJane
and have:
name: Jane Doe
description: My name is Jane Doe and I'm really good at vim!
@qJim
name: Jim Doe
description: My name is Jim Doe and I'm really good at vim!
Upvotes: 3
Views: 928
Reputation: 59287
Although q
is often used with the term macro, its
purpose is to simply record and repeat a sequence of
characters. Repeating being key here — if your intention is
not to repeat the same exact sequence, then q
is probably
not the ideal solution.
You should be able to achieve this easily with a map instead
(note that :h macro
will point you to :map
). You can use
:execute
to build a command which substitutes the first
occurrences of John in the current line and the line after
with whatever input()
returns. The latter is responsible
for prompting the user.
Here's a small example that implements this mapped to ,n
:
:nnoremap ,n :exe ',+s/John/' . input('Name: ')<CR>
If you don't know the name to be replaced, you can ask for that too:
:nnoremap ,n :exe ',+s/' . input('Old name: ') . '/' . input('New name: ')<CR>
Or use a regex which captures the first word after "name" ignoring ":" or "is":
:nnoremap ,n :exe ',+s/\vname(: \| is )+\zs\w+/' . input('New name: ')<CR>
Upvotes: 3
Reputation: 8898
Simplest way to take user input is to use the input()
function, which lets you prompt for a name and use it in expressions.
Unfortunately, you can't really use it from a macro, since a macro would record your answer to the prompt as well (and replaying one you build would have a similar issue.)
You can use it from a function and bind that function to a key map though:
function! ChangeName()
let newname = input('Name? ')
return '0/\<John\>'."\<cr>cw".newname."\<esc>n."
endfunction
nnoremap <expr> <leader>cn ChangeName()
You can then type \cn
, which will prompt you for a new name. Once you type the new name and press ENTER, it will replace the first two occurrences of John
with the new name.
The function returns the expansion of the mapping, using <expr>
in the map command to have it use the return value of the function. (Having the function issue a :normal!
command with an :execute
would also be a possible approach.)
You mentioned pressing @qJim
or @qJane
, where there's not a prompt and the new name "feels" like part of the command. It's possible to get closer to something like that by using getchar()
in a loop from the function behind your mapping. You still need to decide on how to terminate the name, will you take an ENTER at the end? Will you use timing to decide when the command is over (need to type it quickly?) You might also need to handle backspace and cancelling the command if you use getchar()
. Using input()
is certainly easier, and might be enough depending on your particular use case.
Upvotes: 3