Vim repeat append something at end of word - vim

In vim I frequently find myself wanting to append a suffix to an identifier in my source code, and to then repeat it on other identifiers using '.'.
i.e. to transform:
foo bar baz faz
to:
foo_old bar_old baz_old faz_old
I would like to be able to do:
ea_old<ESC>w.w.w.
instead of:
ea_old<ESC>wea_old<ESC>wea_old<ESC>wea_old<ESC>
In other words, I want appending text to the end of a word to appear as a repeatable command in the history. Anyone know how to do this?
I can do:
nmap <C-a> ea
to create a slightly more convenient way to append at the end of a word, but it only repeats the "a". Ideally I want to be able to repeat the whole "eaarbitrarytext" sequence.
I have the repeat.vim plugin installed and tinkered a bit, but I don't really know what I'm doing in vimscript.
Clarifying the requirement: I want to be able to jump around using arbitrary movement commands until my cursor is somewhere on the identifier, and then hit "." to repeat the appending of a suffix. The above example is intended to be a special case.

ea_oldESCe.e.e. should work for you.
Another possible solution would be to use the c flag on a search and replace command:
:.s/\<[[:alnum:]]\+\>/&_old/gc
Then all you have to do is press y to confirm each replacement. This would be faster if you have a lot of replacements to do and want to confirm each one manually. If, on the other hand, you want to add _old to every word on a line, you can remove the c:
:.s/\<[[:alnum:]]\+\>/&_old/g

My guess is that the OP took a very simple example to illustrate a more generic problem that can be re-formulated as "I would like to repeat an arbitrarily big sequence of commands very easily".
And for that, there is the q command. Pick your favourite register for recording, say "q", then:
qq -- starts recording
(do any complicated set of actions here...)
q -- stops the ercording
#q -- plays back the recording
And as I am myself using this very often when programming, I ended up mapping the actions above to F2, F3 and F4 respectively on my keyboard. This allows to repeat your set of actions in really 1 key stroke. In .vimrc:
nmap <F2> qq
nmap <F3> q
nmap <F4> #q

For this specific case, you can use search and replace: s/ /_old /g.

Related

VIM: Finding and replacing first N occurrences of a word

I am editing a file and i want to change only a specific word with another word, but only for first N occurrences. I tried multiple commands
N :s/word/newword/
:%s/word/newword/count
And other commands that i could find on google. But none of them works.
EDIT:: Vim commands is preferred. But Vim script can also be used. I don't have any prior experience in vim scripting.
Using a disposable recording allows you to control exactly how many changes you do:
qq " start recording in register q
/foo<CR> " search for next foo
cgnbar<Esc> " change it to bar
q " end recording
11#q " play recording 11 times
See :help recording and :help gn.
Another way, using :normal:
:norm! /foo<C-v><CR>cgnbar<C-v><Esc> <-- should look like this: :norm! /foo^Mcgnbar^[
11#:
See :help :normal and :help #:.
Or simply:
:/foo/|s//bar<CR>
11#:
Although a bit longer, you can do:
:call feedkeys("yyyq") | %s/word/newword/gc
to replace the first 3 occurrences and then stop.
You can change the amount of y's for more or less replacements. (Can also use n to skip some)
Explanation: this is feeding y keystrokes into the /c confirm option of the substitution command.
I'm not sure about specifying the first N occurrences, but I often use this command:
:%s/word/newword/gc
Vim then asks for confirmation of each occurrence of word so you can selectively change some but not others.
My PatternsOnText plugin provides (among many others) a command that takes answers in the form of either yyynyn or 1-5:
:%SubstituteSelected/word/newword/g 1-5

Easy motion vim plugin mapping issue

I have the vim EasyMotion plugin installed.
Pressing,
<Leader><Leader>f searches forward from the current line.
<Leader><Leader>F searches backward from the current line.
Is there a way to search the entire visible part of the buffer only using 'f'? I would ideally not want to use two different bindings for searching forwards and backward. One single binding to search the entire visible portion of the buffer would be most ideal.
You can try a mapping like this:
nnoremap <leader><leader>f :execute "/\\%>" . line('w0') . "1\\%<" . line('w$') . "l"<left>
That's a confusing syntax, so I'll unpack it.
line('w0') and line('w$') return the line numbers of the first and last visible lines in the buffer, respectively, so you use them to find the range for the visible part.
The / search command allows a range to be specified, but with an odd syntax. The format is /\%>Xl\%<Yl where X is the line to start from and Y is the line to end at.
It's not possible to just drop the results from line() into a normal / invocation, but we can build a string, using . to join segments together, and once the command is built, pass it in to :exec to make it happen.
Finally, there's the <left>. That's for cursor positioning. When you execute <leader><leader>f, the whole mapping fires as though you were typing it, so you end up with the full :exec command on the line, and it ends with a ", but you want to type inside those quotes. Alternatively, you could remove "<left> from the end of the mapping, but then you'll have to remember to close the quote after typing your search term.
I'm not familiar with EasyMotion, so this may not give you exactly what you were asking for (I realized this after I typed up the answer), but it will let you run a search in the currently visible part of a buffer only, and you can probably adapt it to EasyMotion's purposes without too much difficulty.

How do I repeat any command in Vim, like "C-x z" in emacs?

In Vim, is there any way to repeat the last command regardless of whether it was an edit or not, and without having the foresight to first record a macro?
E.g. say I type :bn, and want to do it again (it was the wrong file). Pressing . obviously doesn't do it. Or maybe I'm doing gE and want to repeat that (with one keystroke since clearly gE is kinda painful to type).
Perhaps there are some plugins? Similar to this question.
(Even cooler would be to retroactively bind a number of commands to a macro, so one could type 5qa#a or something to repeat the last 5 commands...)
To repeat a command-line command, try #:, To repeat a normal/insert-mode command, try .,
Add below mapping to your .vimrc if you want to shortcut the same:-
:noremap <C-P> #:<CR> - This will map Ctrl+P to previous command-line command. You can map any other combo.
:help repeating will provide the typical repeat commands (like ., #:, etc.). You could try repeat.vim. That may get you closer to what you are looking for.
For motion commands there is no mechanism built into Vim. The Find and To commands (f/F/t/T) have ; and , to repeat and reverse. There are a couple of plugins which extend those bindings to repeat other motion commands:
http://www.vim.org/scripts/script.php?script_id=2174
http://www.vim.org/scripts/script.php?script_id=3665
The later should support repeating gE using ;
You can just use "."
Example:
You have "abc" at 10 places in your file and you want to replace it with "def" at 5 places of it.
Step 1: Find first occurance of abc by typing command "/abc"
Step 2: Once cursor is on "abc", Replace abc by command "cw" to take out word "abc"
Step 3: Type in "def" as replacement and press enter to go to command mode
Step 4: To repeat this action just type command "n" to go to next occurrence of abc and type command "." . The command remembers that you replaced "abc" with "def" last time and will perform the same here.
You can map #: to some key for more convenience:
:map <F2> #:
and then it's easier to use it with repeats.

Vim copy and paste

My previous question seems to be a bit ambiguous, I will rephrase it:
I have a file like this:
copythis abc
replacethis1 xyz
qwerty replacethis2
hasfshd replacethis3 fslfs
And so on...
NOTE: replacethis1, replacethis2, replacethis3, ... could be any words
How do I replace "replacethis1","replacethis2","replacethis3",.. word by "copythis" word by using minimum vim commands.
One way I can do is by these steps:
delete "replacethis1","replacethis2","replacethis3",.. by using 'dw'
copy "copythis" using 'yw'
move cursor to where "replacethis1" was and do 'p'; move cursor to where "replacethis2" was and do 'p' and so on...
Is there a better way to do this in VIM (using less number of vim commands)?
Since you changed your question, I'd do it this way:
Move to the first "replacethis1" and type cw (change word), then type "copythis" manually.
Move to the next "replacethis", hit . (repeat last operation)
Move to the next "replacethis", hit .,
and so on, and so on.
If "copythis" is a small word, I think this is the best solution.
The digit needs to be included, and there could be more than one instance per line:
:%s/replacethis\d/copythis/g
Given that "replacethis[1-3]" can be arbitrary unrelated words, the quickest/simplest way to do this globally would be:
:%s/replacethis1\|replacethis2\|replacethis3/copythis/g
(Note that you need to use \| to get the pipes to function as "or". Otherwise, vim will look for the literal | character.)
I've been struggling with this for a long time too, I think I just worked out the cleanest way:
Use whichever command is cleanest to put copythis into register r:
/copythis
"rye
Then go to the replacement and replace it with the contents of r:
/replacethis
cw<CTRL-R>r<ESC>
Then you can just n.n.n.n.n.n.n. for the rest of them, or if they're wildly different just go to the beginning of each and hit .
The key is replacing and pasting in one step so you can use . later.
:%s/copythis/replacethis/g
To replace all occurrences of copythis with replacethis. Or you can specify a range of line numbers like:
:8,10 s/copythis/replacethis/g
Note, the /g on the end will tell it to replace all occurrences. If you leave that off it will just do the first one.
create this mapping:
:map z cwcopythis^[
( ^[ is the escape character, you can type it in vim using Ctrl+V Ctrl+[ )
go to each word you want to replace and press z
if u need to do essentially the same action multiple times - swap 1st word of one line with second word of the next line, I say you could record a macro and call it whenever you need to
Have you tried string replacement?
%s/replacethis/copythis
A host of other parameters are possible to fine-tune the replacement. Dive into the Vim help for more details. Some more examples here.
You can remap e.g. the m key in normal mode to delete the word under the cursor and paste the buffer: :nnoremap m "_diwP.
Then you can just copy the desired word, move the cursor anywhere onto the to-be-replaced word and type m.
EDIT: Mapping to m is a bad idea since it is used to mark locations. But you can use e.g. ; anyway.

How to quickly change variable names in Vim?

I am using Vim to read through a lot of C and Perl code containing many single letter variable names.
It would be nice to have some command to change the name of a variable to something more meaningful while I’m in the process of reading the code, so that I could read the rest of it faster.
Is there some command in Vim which could let me do this quickly?
I don’t think regexes would work because:
the same single letter name might have different purposes in different scoping blocks; and
the same combination of letters could be part of another longer variable name, a string literal, or a comment.
Are there any known solutions?
The following is how to rename a variable which is defined in the current scope {}.
Move your cursor to the variable usage. Press gd. Which means - move cursor to the definition.
Now Press [{ - this will bring you to the scope begin.
Press V - will turn on Visual Line selection.
Press % - will jump to the opposite } thus will select the whole scope.
Press :s/ - start of the substitute command.
<C-R>/ - will insert pattern that match variable name (that name you were on before pressing gd).
/newname/gc<CR> - will initiate search and replace with confirmation on every match.
Now you have to record a macros or even better - map a key.
Here are the final mappings:
" For local replace
nnoremap gr gd[{V%::s/<C-R>///gc<left><left><left>
" For global replace
nnoremap gR gD:%s/<C-R>///gc<left><left><left>
Put this to your .vimrc or just execute.
After this pressing gr on the local variable will bring you to :s command where you simply should enter new_variable_name and press Enter.
I know it's an old question, and #mykola-golubyev's way obviously IS the best answer for the particular case in the OP question (which, I assume is going through obfuscated code where you're likely to have multiple blocks with same var names); but with the question name like that many people coming here from google searches probably look for less situation-specific ways to rename variables in VIM -- and those can be more concise
I'm surprised no one suggested this way:
* :s// NEWNAME /gc
The * is the same as gn - search the next occurrence of the word under the cursor AND make it the last searched pattern; you can then omit the search pattern in the substitute command and VIM will assume that last one is the pattern to search for.
For small amounts of var copies, here's an even quicker one:
* cw NEWNAME <esc> then repeat n. for other occurrences
* is search for occurrences, cw is change word, n goes to the next occurrence of the last searched term and . repeats the last command (which is now change word to NEWNAME)
(Credits for me knowing all this go to #doomedbunnies on Reddit)
Another cool trick: (credits to #nobe4)
* cgn NEWNAME <esc> then repeat . for other occurrences
cgn is "change whatever is the result of (find next occurrence)". Now that this is the last command, you don't need the n to go to the next occurrence, so fewer strokes again, and, more importantly, no need to alternate n and .. But, obviously, this one has the drawback of not having a way to skip an occurrence.
Here are some benefits of these over other similar approaches, or language-specific plugins with refactoring support:
no command mapping, no fiddling with .vimrc(or init.vim), so you can use it in any VIM copy you come across (e.g. a quick task on some VPS or your friend's machine where configuring VIM your way would defeat the purpose of 'quick')
using * or gn for word selection is very quick -- just one keystroke (well, let's say 1.5)
using * or gn makes sure you don't get any matches inside other words, just as :%s/<C-R>//gc does. Beats typing the :%s/\<OLDNAME\>/NEWNAME/gc by hand: I personally tend to forget to use the \< things to limit matches to whole words only.
Not using a scope will only result in a few extra strokes of n to skip unwanted matches -- probably even fewer than the extra strokes needed to limit the scope to a certain code block. Under normal circumstances, your variables are most likely somewhat localised to a certain code block anyway.
AFAIK, there is no actual refactoring support in VIM. When doing a rename with the intent of a refactor I usually take the following precautions:
Limit the scope of the change my using marks.
When entering the regex, bracket the name with \< and >. This will make it match an entire word which reduces the types of incorrect renames that will occur.
Don't do a multiline replace to reduce chances of a bad replace
Look through the code diff carefully if it's anything other than a small change.
My end change looks something like this
:'a,'bs/\<foo\>/bar
I would love to be wrong about there not being a refactoring tool for VIM but I haven't seen it.
Put this in your .vimrc
" Function to rename the variable under the cursor
function! Rnvar()
let word_to_replace = expand("<cword>")
let replacement = input("new name: ")
execute '%s/\(\W\)' . word_to_replace . '\(\W\)/\1' . replacement . '\2/gc'
endfunction
Call it with :call Rnvar()
expand("<cword>") gets the word under the cursor. The search string uses % for file-scope, and the \(\W\) patterns look for non-word characters at the boundary of the word to replace, and save them in variables \1 and \2 so as to be re-inserted in the replacement pattern.
You could use the 'c' modifier in the global search and replace that would ask you for confirmation for each replace. It would take longer but it might work for a non-humongous code file:
%s/\$var/\$foo/gc
The c stands for confirm.
In c, you may be able to make some progress using cscope. It makes an attempt at understanding syntax, so would have a chance of knowing when the letter was a variable.
If this is across multiple files, you may consider taking a look at sed. Use find to grab your files and xargs plus sed for a replace. Say you want to replace a with a_better_name in all files matching *.c, you could do
find . -name "*.c" | xargs sed -i -e 's/a/a_better_name/g'
Bear in mind that this will replace ALL occurrences of a, so you may want a more robust regex.

Resources