Refactoring in Vim

I agree with the 'Vim is not an IDE' paradigm. But there are times when there isn't an IDE. Here's what I use in those situations:

Disclaimer: The ubiquity of Language Server Protocol servers, linters and fixers since I wrote this have also brought some great refactoring capabilities to Vim (and other editors). IMO they are a long way from equaling the capabilities of a purpose-built IDE (I prefer ALE and nvim-lspconfig for these kinds of features). See other answers on this question for more info!

:grep, :vimgrep, :GrepperAg, :Ggrep

Refactoring that has more to do with regular replacements I usually use :grep on my project tree and then record a macro to do the refactor - :g and :s are no brainers. Usually it'll let me quickly modify a large number of files with very little effort. Honestly, I use this method more than any other.

Depending on your workflow the built-in commands might be slow/inconvenient. If you use git, then you'll wanna use the excellent Fugitive plugin and its :Ggrep command to only search files checked into git. I also like the vim-grepper because it is search-tool-agnostic (supports ag, sift, ripgrep, etc) and speedy.

:argdo, :cdo, and :bufdo

:cdo and :argdo are handy to execute vim commands over a set of files.

command line

When it's harder to determine the list of files that need changes via :vimgrep I resort to the command line grep/find commands to more closely curate the list of files that I need to refactor. Save the list to a text file and use :e and a mashup of macro recordings to make the changes I need to make.

I find that the less rusty I keep my macro recording skills the more useful I find Vim for refactoring: feeling comfortable saving/restoring from registers, incrementing/decrementing register counter variables, cleaning/saving macro recordings to file for later use, etc.


Update

Since writing this more videocasts for the methods I describe have been published on vimcasts.org (I encourage you to watch ALL the Vimcasts!). For refactoring watch these ones:

  • Substitution with :Subvert
  • Project wide search/replace
  • Search multiple files with :vimgrep
  • Use :argdo to change multiple files

Vimgolf is also a great way to practice.


Language Server Protocol (LSP)

The Language server protocol contains the feature for smart renaming of symbols across a project:

https://microsoft.github.io//language-server-protocol/specifications/specification-3-14/#textDocument_rename

For example following language server support this:

  • Clangd for C++
  • ccls for C/C++/Objective-C
  • Eclipse.jdt.ls for Java
  • pyls (with rope) for Python
  • tsserver for TypeScript
  • Solargraph for Ruby
  • gopls official lsp for Go (alpha stage in Nov 2019)
  • texlab for LaTeX

You can find more language servers under https://langserver.org/.

Vim

A vim editor client is necessary to use them within vim. Following options exist:

  1. LanguageClient-neovim (requires rust) suggests the mapping:

     nnoremap <silent> <F2> :call LanguageClient_textDocument_rename()<CR>
    
  2. coc.nvim (requires node.js) suggests the mapping:

     " Remap for rename current word
     nmap <leader>rn <Plug>(coc-rename)
    
  3. Ale has

     nnoremap <silent> <Plug>(ale_rename) :ALERename<Return>
    

    Ale does not define any keybindings. This has to be done by the user.

  4. vim-lsp provides following command

     :LspRename
    

    Similar to Ale no mapping is suggested. However, of course you can define one as following

     nmap <leader>r <plug>(lsp-rename)
    

    (<leader>r is to be replaced by your choice; I do not know one which most plugins agree on)

  5. vim-lsc has a default mapping:

     'Rename': 'gR'
    

See also YouCompleteMe which facilitates LSPs as well.

Neovim

Neovim has initial builtin support for lsp since 13.11.2019

See for common configurations of LSPs the project nvim-lspconfig which suggests <space>rn as a mapping for vim.lsp.buf.rename().

Other Refactorings

I do not know if there are plans for the LSP protocol to support more complex refactorings, such as changing class structure, adding parameters to methods/functions or moving a method to a different class. For a list of refactorings see https://refactoring.com/catalog/.


Python

For the python language following plugins provide 'smart' renaming capabilities for vim:

  • jedi-vim (github) <leader>r
  • ropevim (github) CTRL-c r r
  • python-mode (github) :h pymode-rope-refactoring

C-Family

  1. Try the plugin Clighter for rename-refactoring for the c-family. It is based on clang, but there are limitations and the plugin is marked as deprecated.

    Suggested mapping by Clighter is

     nmap <silent> <Leader>r :call clighter#Rename()<CR>
    

    Note, the successor plugin clighter8 has removed the renaming functionality in the commit 24927db42.

  2. If you use neovim, you can take a look at the plugin clamp. It suggests

     nmap <silent> <Leader>r :call ClampRename()<CR>