DEV Community

Igor Irianto
Igor Irianto

Posted on • Updated on

How to search faster in Vim with FZF.vim

Follow @learnvim for more Vim tips and tricks!

One thing that modern text editors/ IDEs got right that Vim didn't is how easy it is to find files and to find in files with modern editors/IDEs. In this article, I will show you how to use FZF.vim to make searching in Vim as easy as searching in modern editors/IDEs.

Here are the things I will cover:

Warning: when using FZF, please fasten your seatbelt, because it can get REALLY fast. šŸš— šŸ”„ šŸ”„

Setup

Before we start, we need to download FZF and ripgrep. Follow the instruction on their github repo. If you have homebrew, you can run brew install fzf and brew install ripgrep. The commands fzf and rg should be now available.

In my .zshrc (.bashrc if you use bash), I have these:

if type rg &> /dev/null; then
  export FZF_DEFAULT_COMMAND='rg --files'
  export FZF_DEFAULT_OPTS='-m --height 50% --border'
fi
Enter fullscreen mode Exit fullscreen mode

FZF does not use ripgrep by default, so we need to tell FZF to use ripgrep with FZF_DEFAULT_COMMAND variable.

Pay attention to -m in FZF_DEFAULT_OPTS. This option allows us to make multiple selections (with Tab or Shift-Tab). You don't have to use it, but I think it is helpful to be able to select multiple files. It will come in handy when you want to perform search and replace in multiple files - which I'll cover in just a little bit :). The remaining options are optional. To learn more, check out fzf's repo or man fzf.

At minimum we should have export FZF_DEFAULT_COMMAND='rg'.

After installing fzf and rg, let's set up Vim. I am using vim-plug plugin manager in this example, but you can use anything.

To set up FZF in Vim, add these inside your .vimrc plugins. We will be using FZF.vim plugin (created by the same FZF author). The second line ensures that we have latest FZF.

Plug 'junegunn/fzf.vim'
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Enter fullscreen mode Exit fullscreen mode

For more info, you can check out this README page from FZF.vim repo.

FZF syntax

Let's go over syntax so we can search more efficiently. Fortunately for us, there aren't many to learn.

  • ^ is a prefix exact match. To search for phrase starting with "welcome", we do ^welcome.

  • $ is a suffix exact match. To search for phrase ending with "my friends", we do friends$.

  • ' is an exact match. To search for phrase "welcome my friends", we do 'welcome my friends.

  • | is an "or" match. To search for either "friends" or "foes", we can use friends | foes.

  • ! is an inverse match. To search for phrase containing "welcome" and not "friends", we can use welcome !friends

We can mix and match the above. For example, ^hello | ^welcome friends$ searches for phrase starting with either "welcome" or "hello" and ending with "friends".

Finding files

To search for files inside Vim using FZF.vim plugin, we can use :Files method. Run :Files from Vim and you'll be prompted with FZF search prompt. Pretty cool!

Files

FZF.vim file finder is best used with a mapping. I've used <Leader>f and Ctrl-p in the past and I am currently mapping it to Ctrl-f.

nnoremap <silent> <C-f> :Files<CR>
Enter fullscreen mode Exit fullscreen mode

Finding in files

To search inside files, we can use FZF.vim's :Rg command. Alternatively, we can use :Ag (The Silver Searcher). For this article, I will use :Rg.

Rg

Mine is mapped to <Leader>f.

nnoremap <silent> <Leader>f :Rg<CR>
Enter fullscreen mode Exit fullscreen mode

Side note: FZF.vim :Rg option also searches for file name in addition to the phrase. If you think this is an issue, check out this comment. I added this in my .vimrc:

command! -bang -nargs=* Rg call fzf#vim#grep("rg --column --line-number --no-heading --color=always --smart-case ".shellescape(<q-args>), 1, {'options': '--delimiter : --nth 4..'}, <bang>0)
Enter fullscreen mode Exit fullscreen mode

With the above, every time we invoke Rg, FZF + ripgrep will not consider filename as a match in Vim.

Other searches

FZF.vim provides many other search commands. You can check them out here.

Here's what my FZF mappings look like:

" PLUGIN: FZF
nnoremap <silent> <Leader>b :Buffers<CR>
nnoremap <silent> <C-f> :Files<CR>
nnoremap <silent> <Leader>f :Rg<CR>
nnoremap <silent> <Leader>/ :BLines<CR>
nnoremap <silent> <Leader>' :Marks<CR>
nnoremap <silent> <Leader>g :Commits<CR>
nnoremap <silent> <Leader>H :Helptags<CR>
nnoremap <silent> <Leader>hh :History<CR>
nnoremap <silent> <Leader>h: :History:<CR>
nnoremap <silent> <Leader>h/ :History/<CR> 
Enter fullscreen mode Exit fullscreen mode

Replacing grep with rg

Internally, Vim has two ways to search in files: :vimgrep and :grep. :vimgrep uses vim's built-in grep and :grep uses external tool which you can reassign using 'grepprg'.

For example, if we want to search for "iggy" with :grep, we can run :grep "iggy" . -R (you may notice that Vim's :grep syntax is similar to terminal grep command; this is because :grep by default runs grep -n $* /dev/null on unix-based machine). The command above will search for string "iggy" recursively (-R) from current location (.).

Vim allows us to change the program used by :grep. We can tell Vim to use ripgrep instead of grep by adding this inside our vimrc:

set grepprg=rg\ --vimgrep\ --smart-case\ --follow
Enter fullscreen mode Exit fullscreen mode

Now when we run :grep inside Vim, it will run rg --vimgrep --smart-case --follow instead. For more information what the options above mean, check out man rg. I can now run a more succinct command :grep "iggy" instead of :grep "iggy" . -R.

Vim :grep command uses quickfix to display results. I won't go over quickfix here because it's outside this article's scope. We can use :copen to display quickfix window and :cclose to close quickfix window. Try it!

You might wonder, "Well, this is nice but I never used :grep in Vim, plus can't we just use :Rg to find string in files? When will I ever need to use :grep?"

That is a very good question. The answer to "why do we need grep in Vim?" is that it will let us to do what I'll going to cover next: search and replace in multiple files.

Search and replace in multiple files

Modern text editors like VSCode makes it very easy to search and replace string across multiple files. If I may confess, in the beginning when I had to search/replace string in multiple files, I used VSCode because doing it in Vim, although possible, takes too long... until now.

I will show you two different tricks to easily do search and replace phrases across multiple files in Vim.

The first method is to replace ALL matching phrases in our project. We will need to use :grep. Let's say you want to replace all instance of "pizza" with "donut". Here's what you do:

:grep "pizza"
:cfdo %s/pizza/donut/g | update
Enter fullscreen mode Exit fullscreen mode

That's it? Yup! That's it. Let me break down the steps:

  1. :grep pizza uses ripgrep to succinctly search for all instances of "pizza". By the way, this would still work even if we didn't reassign ripgrep to replace default grep. We would have to do :grep "pizza" . -R instead of :grep "pizza".
  2. We run :cfdo because :grep uses quickfix.:cfdo executes any command we pass (in this case, our command is %s/pizza/donut/g) on all entries in our quickfix list. To run multiple commands, we can chain it with pipe (|). The first command we are executing is pizza-donut substitution: %s/pizza/donut/g. The second command, update, saves each file after the first is finished.

Let's discuss the second way.

The second method is to search and replace in select multiple files instead of all files using buffers. Here we can choose which files we want to perform select and replace.

  1. Clear our buffers (:Buffers) first. Our buffers list should contain only the needed files. We can clear it with %bd | e# | bd# (or restart Vim).
  2. Run :Files.
  3. Select all files you want to perform search and replace on. To select multiple files, use Tab / Shift+Tab. This is only possible if we have -m in FZF_DEFAULT_OPTS.
  4. Run :bufdo %s/pizza/donut/g | update.

Our command :bufdo %s/pizza/donut/g | update looks similar to :cfdo %s/pizza/donut/g | update. That's because they are. Instead of performing substitution on all quickfix (cfdo) entries, we perform our substitution on all buffer (bufdo) entries.

Conclusion

FZF.vim is a game-changer. I can't imagine using Vim without it. This article shows how to set up necessary tools and configs to get FZF running in Vim. I also shared some tips to perform more complicated searches, like search-and-replace.

Once everything is set up, we can now search quickly in Vim like modern editors/ IDEs.

Hope you find this helpful. Keep improving. Keep hacking. Keep inventing.

Happy coding!

Resources

Shameless disclaimer: if you enjoy this, there's a 98.39% (not scientifically proven) you will also enjoy my Vim Cookbook: Gourmet Vim. Check it out!

Top comments (20)

Collapse
 
coreyja profile image
Corey Alexander

Great article! I've been a fzf devotee for a while, but I really like how you laid out the syntax for it in a easy reference way! Will likely refer back to it for that in the future!

Shameless Plug:
Check out my fork of fzf.vim that adds devicons for file types to the fzf results!
fzf.devicon.vim

Collapse
 
skipadu profile image
Sami Korpela

I have used FZF and l’m loving it. Instead of ripgrep I use fd

I have just started my Vim journey few months ago, so I’m really liking your articles šŸ™‚

Collapse
 
iggredible profile image
Igor Irianto

Interesting. I actually never heard of fd before. Just checked fd GH repo - thanks for the mention!

Collapse
 
wulymammoth profile image
David

I believe thatfd and rg (ripgrep) are very different utilities, Sami. fd is more of a find substitute and rg is a substitute for grep or ag (The Silver Searcher). I've had no need to install fd using fzf. Check out videos from The Primeagen and Thoughtbot on Vim. Cheers and enjoy the journey!

Collapse
 
skipadu profile image
Sami Korpela • Edited

Yes, that is true. I just use it with the FZF for finding files as it is faster than find (or at least it feels so).

But I might add ripgrep to get faster grep, haven’t yet used used it so much.

And the ripgrep author suggests to use fd instead when finding files.
Source

Collapse
 
wulymammoth profile image
David

Nice write-up, Igor. I'd been using fzf for a couple years in Vim, but it was only recently (beginning of the year) that I discovered its powers as a CLI utility. If you're using bash along with fzf's bash/zsh-completion, it provides globbed search that you can prefix with many Unix utilities including ripgrep:

  1. open file in vim that is somewhere from current working dir: $ vim ** (followed by tab)
  2. open file in vim anchoring from a directory: $ vim foo/bar/** (followed by tab)
  3. search all files in current working directory (with ripgrep): $ rg --files | fzf
  4. change to a project directory (can't remember where): $ cd ** (followed by tab)

You're probably familiar with these as one of your links discusses fzf and ripgrep.

I just learned how to better to search/find-and-replace in multiple files from you. Thanks for that! I was just about to adopt more sed into my workflow to do this sort of thing, but the situation hasn't come up yet

Collapse
 
richardmelko profile image
Richard Melkonian

when I use add this to my .vimrc
if type rg &> /dev/null; then
export FZF_DEFAULT_COMMAND='rg --files'
export FZF_DEFAULT_OPTS='-m --height 50% --border'
fi
I get errors when reloading vimrc. Anything I'm missing?

Collapse
 
wesleycheek profile image
Wesley Cheek

This needs to be added to your .bashrc or .zshrc, not your .vimrc!

Collapse
 
ahsoromdoni profile image
Ahmad Solehudin Romdoni

Hi Wesley Cheek, how about fish shell? i got the error also

Collapse
 
iggredible profile image
Igor Irianto

Lol good suggestion. Edited the post.

seatbelt

Collapse
 
coreyja profile image
Corey Alexander

This gave me a chuckle when I read it at first lol Love that it came from the comment thread!

Collapse
 
redeemefy profile image
redeemefy

How do you go by ignoring directories such as node_modules or .github

Collapse
 
fwolfst profile image
Felix Wolfsteller

if you use :GFiles instead of :Files it will ignore everything that is in .gitignore. That should solve the cases you mentioned, although it does not provide a general answer.

Collapse
 
l00sed profile image
Daniel Tompkins

Awesome intro to fzf. Thanks for the help getting everything setup. Haven't familiarized myself with these tools before, but I can definitely see the use-value!

Collapse
 
deven96 profile image
Diretnan Domnan

I just recently discovered FZF. This article however exposed a lot I'm not using. Thanks for upping my vim game a notch

Collapse
 
iggredible profile image
Igor Irianto

You are very welcome. I am glad you found it helpful. Best of luck!

Collapse
 
adds68 profile image
Adam Jones

Thanks Igor, this was super helpful!

Collapse
 
richardmelko profile image
Richard Melkonian

Thanks for making this!