How to quickly change variable names in Vim?

Viewed 48939

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:

  1. the same single letter name might have different purposes in different scoping blocks; and

  2. the same combination of letters could be part of another longer variable name, a string literal, or a comment.

Are there any known solutions?

7 Answers

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.

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:

  1. Limit the scope of the change my using marks.
  2. 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.
  3. Don't do a multiline replace to reduce chances of a bad replace
  4. 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.

Related