I recently started using some new tools: riprep as a (mostly) replacement for grep and fzf as a bash fuzzy finder. Fuzzy finder usually makes people think of files, but fzf does fuzzy filtering on any list you give it, which maybe doesn't sound amazing, but it has a vim integration that let's you quickly fuzzy find files, buffers, patterns, maps, etc. It's very useful.
If you're unfamiliar with vim's cfdo
you can peruse :h :cfdo
, or just keep reading since I'm going to tell you what it says:
:cfdo[!] {cmd} Execute {cmd} in each file in the quickfix list.
It's a more recent cousin of some old favorites, like :bufdo
, :tabdo
, and :argdo
. All the *do
commands execute a command against a set of buffers. :bufdo
for instance executes {cmd}
against all open buffers (with caveats). :cfdo
executes a command against entries in the quickfix list. cf
in vim is for quickfix . . . I can't find any explanation of why the abbreviations use c instead of q . . . possibly because :q
is for quitting and might be accidentally typed. Anyway, the quickfix list is populated by vim when certain external processes run, such as :make
and :grep
.
So how are these things related and why is it *magic*? Because I can never remember off the top of my head the syntax for sed to do a global find and replace in place. If you are a sed expert, please don't comment to say "but sed!" Yeah, I know, but sed is one of those things that for some reason I have a block on. Maybe it's because I don't use it that often.
At any rate, in vim, I have a mapping to grep the code base using ripgrep and then pipe it into vim's fzf interface: gr{motion}
. Most often I use this as griw
(grep inside word). All files with matches are populated in fzf's interface, where I use Tab to mark files I care about, and then press Enter to select those files. This adds those files (actually those specific grep matches) to the quickfix list. From there I can use :cfdo
to make the replacement across all the files I care about.
Let's say I wanted to replace "foo" with "bar" in my code base (but maybe not everywhere . . . just in some select places). I could find a file with "foo" in it to use my mapping, or I could just call my custom :Rip
command with the argument foo
, (if you're interested my in custom fzf stuff, see my dotstar vim files), hit Tab on the files I care about, then Enter, and then type :cfdo %s/foo/bar/ge | w
. Let me unpack that command a bit in case vimspeak is not your thing:
:cfdo -> Do the following on all the files in the quickfix list
% -> in the current buffer
s -> substitute
/foo/bar/ -> foo for bar
ge -> globally and ignore errors
| -> then
w -> write the changes to disk
Boom! Done. Yeah, it's not as clean as sed, and if I could ever remember sed, that would probably be a better tool, but I love how flexible the combination of fzf, ripgrep, and cfdo is. It's almost like . . . *MAGIC*1.
1 Not to be confused very magic (cf :h \v
)