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)