Every once in a while you’ll have some word or phrase that is present in
multiple files across your project. Finding all of instances can be done with
grep outside of Vim. You could probably also use
some other tool to perform a substitution inside these files.
While there’s nothing wrong with this approach, what if you wanted to do this kind of multi-file search and replace inside Vim?
The following methods are by no means the only ways of doing things, it’s just how I tend to solve this problem.
When in a terminal, if you wanted to find all instances of
a project, you’d probably run one of these commands:
grep "FoobarQuux" $(git ls-files) git grep "FoobarQuux" ack "FoobarQuux" ag "FoobarQuux"
Let’s look at alternatives.
The built-in equivalent that Vim has is
vimgrep. A search similar to the ones
above would be this:
:vimgrep /FoobarQuux/ `git ls-files`
You’ll notice a couple of things here.
First of all, the command uses slashes to encapsulate the regex and grave accents for eval. I’m pretty sure the
$() syntax doesn’t work here.
Secondly, the results aren’t highlighted in the way that a regular search
/ would be. Instead, the Quickfix-list is populated with the
results and you can go through them with
bindings for that with tpope’s unimpaired).
Lastly, and most importantly, you’ll notice that vimgrep is remarkably slow. I don’t know why that is and I don’t particularly care since there are alternatives.
We can speed things up by using the
ack.vim plugin, which you can find
over on Github. I’m going to assume that
you’ve already got a preferred method of installing plugins, but in case you’re
new to them, here’s a helpful Stack Overflow question.
You’ll have to install the ack package for whatever OS you use, then you’ll be able to run commands like this in Vim:
This is just like running
ack FoobarQuux from the command line, but the
results get loaded into a Quickfix-list. This time however, there’s
a noticeable increase in performance.
Mr. ggreer once said “But who has the time?”, and lo and behold,
the_silver_searcher was created. If you’re not familiar with
ack but even quicker. What’s even nicer is that you don’t need
another plugin for using it (although you can get one), you just change the command which
runs. Add this to your
.vimrc and you’re good to go:
1 2 3
I like to use this solution because I’ve found that on distros like Debian,
ack is readily available in the repositories while
ag isn’t, so it’s a bit
Replacing the stuff you’ve found
So now your Quickfix-list list is filled with lines that contain some word or phrase you want to change. Here’s an interesting fact for you then:
bufdo for running a certain command in all open buffers. It also has
windo for all open windows,
tabdo for all tabs and
argdo for all files in
the arglist. All these
things, but no
bufdo doesn’t overlap with the files you wish to perform replacements on
(it sometimes does), you can use the following plugin to populate the arglist with the
contents of the Quickfix-list. The creator of the plugin
gives an example:
:Ack FoobarQuux :Qargs :argdo %s/FoobarQuux/Cranberries/gc :argdo update
That works, and lets us see every change due to the confirm flag at the end of the third line.
But what if there was one command for doing it all?
greplace – global search and replace
This plugin is pretty old and there might be newer alternatives, but it does what it should and that’s all I need.
From the help page:
1 2 3 4 5 6 7 8 9 10 11 12
So if you were to do
:Gsearch you’d get to input the regex, then input what files you wish to apply this to and then you’ll get an editable with every found line!
When you’re done editing the found line, you run
:Greplace and then you’ll get to approve each change in the actual file.
Regardless of how often this scenario pops up for you, you ought to try it to
see how nice this plugin is. I might also add that when you’re in that editing
buffer, you could probably improve your experience even more by using the
:Subvert command from tpope’s abolish plugin, functionality which is explained
in this Vimcast.