Subscribe for free with iTunes, Twitter, or RSS (Ogg or Quicktime).

Vimcasts

Project-wide find and replace

#45

Vim doesn’t have a built-in command for project-wide find and replace operations, but we can perform this task by combining primitive Ex commands such as :substitute, :argdo, and :vimgrep. We’ll look at two possible strategies: first using the arglist, then the quickfix list.

Download

OGG 7.8 MB

Quicktime 12.8 MB

In the video, I find all occurrences of Vimcasts.com and change them to Vimcasts.org. I show two different ways of doing it. This builds on material that was introduced in episodes 41, 42, 43, and 44.

Strategy #1 – run :substitute across all project files

The first strategy is to populate the arglist with all of the files in the project, then run the substitute command against them all:

:args *.txt
:argdo %s/Vimcasts\.\zscom/org/ge
:argdo update

This may mean that the substutite command runs in buffers that don’t contain a match.

Strategy #2 – run :substitute across project files that contain a match

The second strategy breaks the substitute command into two steps: find all matches, then run the substitute command only in those buffers that contain a match. Here are the steps:

:args *.txt
:vimgrep /Vimcasts\.\zscom/g ##
:Qargs
:argdo %s/Vimcasts\.\zscom/org/ge
:argdo update

This uses a custom :Qargs command to prune the arglist of buffers that don’t contain a match.

The :Qargs helper command

The custom :Qargs command sets the arglist to contain each of the files referenced by the quickfix list. You add it to Vim by copying these lines into your vimrc file:

command! -nargs=0 -bar Qargs execute 'args' QuickfixFilenames()
function! QuickfixFilenames()
  " Building a hash ensures we get each buffer only once
  let buffer_numbers = {}
  for quickfix_item in getqflist()
    let buffer_numbers[quickfix_item['bufnr']] = bufname(quickfix_item['bufnr'])
  endfor
  return join(map(values(buffer_numbers), 'fnameescape(v:val)'))
endfunction

Or you could install the vim-qargs plugin from github. Credit where due: I adapted this code from a Stack Overflow solution posted by Dr Al.

Proposal: :cdo

Vim needs a :cdo {cmd} command. It would behave like the :argdo and :bufdo commands, except that it would iterate through the quickfix list. The :cdo {cmd} command would work as follows:

:cfirst
:{cmd}
:cnfile
:{cmd}
etc.

If such a command existed, then we could refine Strategy #2, removing the :Qargs step:

:args *.txt
:vimgrep /Vimcasts\.\zscom/g ##
:cdo %s/Vimcasts\.\zscom/org/ge
:cdo update

UPDATE on June 2nd, 2013, Yegappan submitted a patch to add native :cdo and :ldo commands to Vim’s core.

:cdo implementations in the wild

Henrik Nye created a :Qdo command in his fork of my Qargs plugin. This takes the slightly dirty approach of populating the arglist from the contents of the quickfix list, then using the built-in :argdo command to iterate over the resulting arglist. Of course, this has the side-effect that it changes the contents of the arglist. That’s tolerable, but not ideal. Henrik has written up how to use :Qdo for project wide search and replace.

Peter Jaros created vim-cdo, which includes :Cdo as well as :Ldo for operating on the location list. Peter’s plugin differs slightly from the formula I described above, because it effectively uses :cnext instead of :cnfile.

In the comments, Vincent Velociter pointed out another implementation that creates both :Qfdo and :Qfdofile commands.

Further reading