Helmut Granda
Helmut Granda

Reputation: 4705

Refactoring in Vim

Of course the fact that you can refactor on IDEs is priceless for many, I hardly ever do it when I am coding but I may try to do it when editing some one else's source. How do you accomplish such a trivial task across multiple files in Vim?

I found this plugin for refactoring Ruby, but how about "any" language?

Upvotes: 123

Views: 85141

Answers (15)

dsummersl
dsummersl

Reputation: 6737

Disclaimer: a huge ecosystem of Language Server Protocol servers have been created since I wrote this. They have brought great refactoring capabilities to Vim (and other editors). IMO they are still far from equaling the capabilities of a purpose-built IDE, but they're great. I prefer ALE, nvim-lspconfig, null-ls, and refactoring.nvim to leverage these features (and neovim). See other answers for more info!


I subscribe to the 'Vim is not an IDE' philosophy. But there are times when there isn't an IDE. Here's what I do in those situations:

:grep and :cdo commands

If the refactoring task has more to do with regular replacements I use a :vimgrep-like command to find files, record a macro to do the refactor or use :g and :s if its a no brainer, and use :cdo (:argdo, :bufdo, etc) to execute the macro/command over all the matches.

Usually vim'll let me quickly modify a large number of files with very little effort. Honestly, I use this method more than any other.

The built-in vim commands might be slow/inconvenient for you. If you use git, you'll wanna use the excellent Fugitive plugin and its :Ggrep command to only search files checked into git. I also like vim-grepper because it is search-tool-agnostic (supports ag, sift, ripgrep, etc). I often use quickfix-reflector to winnow down matches, and edit directly in the quickfix window.

command line

When a simple :grep command doesn't cut it, I resort to the command line 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.

Striving to understand my tools always makes the job easier: git staging, CLI tools, and the language that I'm working with.


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:

Vimgolf is also a great way to practice.

Upvotes: 98

Hotschke
Hotschke

Reputation: 10210

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:

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/.

Upvotes: 49

Jordan R&#233;jaud
Jordan R&#233;jaud

Reputation: 482

The CoC addon has (among other features) the ability to rename variables.

https://github.com/neoclide/coc.nvim

" Symbol renaming.
nmap <leader>rn <Plug>(coc-rename)

Upvotes: 0

Hotschke
Hotschke

Reputation: 10210

Plugin YouCompleteMe (YCM) (20k stars on github)

http://ycm-core.github.io/YouCompleteMe/#the-refactorrename-new-name-subcommand

:h RefactorRename-new-name

In supported file types, this command attempts to perform a semantic rename of the identifier under the cursor. This includes renaming declarations, definitions and usages of the identifier, or any other language-appropriate action. The specific behavior is defined by the semantic engine in use.

Similar to FixIt, this command applies automatic modifications to your source files. Rename operations may involve changes to multiple files, which may or may not be open in Vim buffers at the time. YouCompleteMe handles all of this for you. The behavior is described in the following section.

Supported in filetypes: c, cpp, objc, objcpp, cuda, java, javascript, typescript, rust, cs

By default there is no mapping.

Upvotes: 5

Hotschke
Hotschke

Reputation: 10210

Go

  1. The tool godoctor (github) supports several refactoring capabilities
  • Rename
  • Extract Function
  • Extract Local Variable
  • Toggle var ⇔ :=
  • Add Godoc stubs

There is a vim plugin https://github.com/godoctor/godoctor.vim which makes them available

With cursor in thing to rename:

:Rename <newname>

Highlighting block to extract:

:Refactor extract newfunc
  1. vim-go

    • Precise type-safe renaming of identifiers with :GoRename.
  2. Language server gopls

Upvotes: 1

Hotschke
Hotschke

Reputation: 10210

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

Upvotes: 19

Geremia
Geremia

Reputation: 5676

Place cursor at name to refactor and type

gd (or gD if you're refactoring a global variable).

Then

cgn new_name esc

and

. one or more times to refactor next occurrence(s)

or

:%norm . to refactor all occurrences in the buffer at once.

Upvotes: 5

C. Kelly Osborn
C. Kelly Osborn

Reputation: 1

I would consider using the spacemacs version of emacs. It is uses the same modes and most keystrokes as Vim but has many more add-on because of it's lisp nature. If you want to program in C++ you just add the c++ layer and most of the IDE is just set up for you already. For other interpreted languages like python or bash you do not need to leave spacemacs to use them. They even have a way to run blocks of code directly within your text which works fantastic for literate programming or reproducible programming where the code and the data are in the same file. Both done as text.

Spacemacs is much more heavy handed in it's initial load but the additional stuff you can do with it is worth the few seconds of startup cost. One layer org-mode is worth checking it out. It is the best outliner, programmer, day timer / todo list I have ever used.

Upvotes: 0

Jason
Jason

Reputation: 1

A combination of two plugins: vim-ripgrep, to find across files and put the results in the quickfix window, and quickfix-reflector to save the changes right in the quickfix window and have it automatically save each change across the files.

Upvotes: 0

BB Chung
BB Chung

Reputation: 201

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>
    

Upvotes: 13

Hotschke
Hotschke

Reputation: 10210

Plugin Factorus

There is another vim plugin dedicated for refactoring called factorus which is available on github.

Currently (2017-12), it supports the languages

  • c,
  • java, and
  • python.

Upvotes: 3

BenC
BenC

Reputation: 8976

For refactoring, if you're using Unite (and you should), you can then use vim-qfreplace and make it extremely easy. Check this video that demonstrates how it works. Once your workflow is set, you can make some mappings to optimize it (instead of typing most things like in the video).

Upvotes: 0

dkrikun
dkrikun

Reputation: 1328

I write a lot of C/C++ code in vim. The most common refactoring that I do is renaming variables, class names, etc. Usually, I use :bufdo :%s/source/dest/g to do a search/replace in files, which is almost the same as renaming provided by big IDE's.
However, in my case, I found that I usually rename similar entities, spelled in different cases (i.e CamelCase, snake_case, etc.), so I decided to write a small utility to help with this kind of "smart-case" search/replace, it is hosted here. It is a command-line utility, not a plugin for vim, I hope that you can find it useful.

Upvotes: 1

Luc Hermitte
Luc Hermitte

Reputation: 32976

I wrote this plugin for generic refactoring. It still requires many improvements. Sometime in the future I'll try to abandon ctags in favour of clang for C&C++ refactorings.

Upvotes: 5

fjrg76
fjrg76

Reputation: 63

Maybe not the most elegant solution, but I found it very handy: I use ECLIM to connect VIM and Eclipse. Of course all my source code editing is done in VIM, but when it's time to refactor, one can take advantage of Eclipse's superior cababilities in this matter.

Give it a try.

Upvotes: 5

Related Questions