Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 826
Helmut Granda Avatar asked Jan 08 '12 22:01

Helmut Granda


People also ask

What is script refactoring?

In computer programming and software design, code refactoring is the process of restructuring existing computer code—changing the factoring—without changing its external behavior.

What does a refactoring do?

Refactoring is the process of restructuring code, while not changing its original functionality. The goal of refactoring is to improve internal code by making many small changes without altering the code's external behavior.

What is refactoring in github?

Refactoring is a widespread practice that helps developers to improve the maintainability and readability of their code.

What is refactoring in OOP?

Refactoring is a specific, disciplined approach to improving the design of existing code. With refactoring, the overall design and structure of an existing program is improved, while its observable functionality remains unchanged.


7 Answers

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.

like image 122
dsummersl Avatar answered Oct 16 '22 10:10

dsummersl


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

like image 35
Hotschke Avatar answered Oct 16 '22 09:10

Hotschke


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>
    
like image 36
BB Chung Avatar answered Oct 16 '22 10:10

BB Chung


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.

like image 44
fjrg76 Avatar answered Oct 16 '22 09:10

fjrg76


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.

like image 40
Luc Hermitte Avatar answered Oct 16 '22 09:10

Luc Hermitte


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.

like image 28
Geremia Avatar answered Oct 16 '22 08:10

Geremia


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.

like image 28
dkrikun Avatar answered Oct 16 '22 09:10

dkrikun