Reputation: 9256
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?
Upvotes: 128
Views: 78739
Reputation: 59844
The following is how to rename a variable which is defined in the current scope {}
.
gd
. Which means - move the cursor to the definition.[{
- this will bring you to the scope begin.V
- will turn on Visual Line selection.%
- will jump to the opposite }
and thus will select the whole scope.:s/
- start of the substitute command.<C-R>/
- will insert a pattern that matches the 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 macro 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 should enter new_variable_name
and press Enter.
Upvotes: 270
Reputation: 21066
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 repeatn.
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:
*
or gn
for word selection is very quick -- just one keystroke (well, let's say 1.5)*
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.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.Upvotes: 53
Reputation: 754863
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:
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.
Upvotes: 10
Reputation: 99
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.
Upvotes: 9
Reputation: 2967
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.
Upvotes: 3
Reputation: 3737
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.
Upvotes: 0
Reputation: 1280
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.
Upvotes: 0