vimgrep
is a built in vim functionality that few people know about. They belongs to advanced magic, but they are actually easy to use and extremely powerful.
It is a search spell that casts your vision across multiple dimensions of files, revealing the hidden patterns of the text within. Together with commands like :copen
, :cdo
, you can easily transform your code with minimal effort.
Assume you are in a magical world with tens or hundreds or even more magical modules. You want to replace a function let’s say Resource
with Asset
in all of them, or along the way moving some modules in and out.
You can do it manually, but it will take a lot of time and effort, consulting magical compilers and spell checkers multiple times along the way.
Some wizards know that we can use grep to examine the modules with given pattern, like this
grep -Irl "Resource" .
this is cool and does give you all modules that contain the word Resource
, but you will have to turn back to your magical editor in and out.
Now if you have vimgrep, you can do it in a few keystrokes, within the magical editor vim itself, type one of the following commands
:vimgrep /Resource/ **/*.hs
:vimgrep /Resource/ `find . -type f`
" for only searching the current file, use `%` for the current file
:vimgrep /Resource/ %
Yes, you can match a pattern across a lot of files, the vimgrep accepts a pattern and a list of files, which can be generated by a matching pattern or command.
When you press Enter
, you will immediately be brought to the pattern it finds, it opens the correct file and places the cursor at the correct position! You can now do the work you want to do, like replacing Resource
with Asset
. When you think you are done with the current magical module, you can press :cn
to go to the next match, or :cp
to go to the previous match.
But that is still a pretty manual process, no worry, we have more magic to help you.
The :copen
command opens a quickfix list window, that shows all the matches, you can easily navigate to the match you want to work on.
Now if you put :cdo s/Resource/Asset
or :cfdo %s/Resource/Asset/g
in the command line, it will replace all Resource
with Asset
in all the matches in the quickfix window, at once! If you want to confirm each replacement, you can use :cdo s/Resource/Asset/c
or :cfdo %s/Resource/Asset/gc
cdo
and cfdo
:cdo
will apply the command to all the matches in the quickfix window.
:cfdo
will apply the command to all the files in the quickfix window.
There is much more you can do with these commands than just replacing strings, any commands can be used in :cdo {cmd}
.
For example you can even run a macro on all matches!
:cdo normal @a
Another example, adding a comment (in haskell its --
) above all matching lines
:cdo normal O-- This is a comment
Or even delete all matching lines, very easily
:cdo normal dd
This also means you can match a very big pattern for example all functions starting with a given name, and perform replacements inside these functions only!
If you want to update changes immediately, you can pipe the cdo
command to update
command, like this
:cdo %s/Resource/Asset/g | update
Or you can just save all changes using :wa
.
If you are not satisfied with just searching a keyword, you can use regular expressions to search for more complex patterns. Here are some common patterns you can use:
\b
matches a word boundary,
\bResource\b
will match the word Resource
but not Resources
or SomeResource
.\<
and \>
: word boundary, match the beginning and end of a word
.
: match any one character,
a.b
will match a1b
, a2b
foo.
will match foo1
, foo2
, etc.^
and $
: match the beginning and end of a line,
return 0;$
will match return 0;
at the end of a line.*
, +
, ?
: match zero or more, one or more, zero or one of the preceding character, but in vim you need to escape them with \
, like \*
, \+
, \?
.
\s
, \S
for whitespace, non-whitespace,
\d
, \D
for digit, non-digit,
\d
will match 1
, 2
, etc.\w
, \W
for word character, non-word character,
\w
will match a
, b
, etc.[]
: match any one character in the brackets,
\[abc\]
will match a
, b
, or c
.{}
: match the preceding character for a specific number of times,
a\{3\}
will match aaa
.()
and |
: group characters, match one of the characters,
\(a\|b\)
will match a
or b
.Note that there are some differences between the regular expressions in vim and other tools, this is because vim want to priotize actual characters like ()[]{}*+?
, so the characters representing regular expressions need to be escaped. You can check :help pattern
for more details.
(to be added)