Finding Things in Vim

May 15, 2019

Vim is a very competent text editor and although it comes with tools to find files, these tools feel a little primitive for modern text editing and a bit slow sometimes. In this post, I will go over different search tools and demonstrate how I replaced two different plugins with only a single plugin that rules them all.

Finding Files by Content

To search files, Vim has a :vimgrep command that finds files that contain a certain pattern. It populates a quickfix list with all the matches. You can cycle between the matches using :cnext and :cprev, or open the quickfix list in a split window using :copen.

Here’s how you can find files and have the search result open in a quickfix list so you can scroll up and down the result using the keyboard:

:vimgrep {pattern} {files} | :copen

Vim also has a :grep command that works similarly to :vimgrep but, according to the documentation (try :h grep), it relies on the external grep command on your system. :vimgrep loads the files in memory so it might be slower than :grep. Just like :vimgrep, :grep will populate a quickfix list and will not open a quickfix list with the results by default. Here’s how to use it in the command mode:

:grep {pattern} {files}

For more convenience, and much faster search, I have been using a tool as a replacement for grep called Ag, the silver searcher and a Vim interface for it through ag.vim (which has been unmaintained for a while now). ag.vim opens the results in a quickfix list by default and you can navigate and choose which file to open using the keyboard just like with :vimgrep. ag.vim, however, adds some nice key maps for better navigation of the results. This is my configuration for ag.vim:

"search from the project root instead of cwd
let g:ag_working_path_mode="r"
"start a search query by pressing \
nnoremap \  :Ag!<space>
"search for word under cursor by pressing |
nnoremap \| :Ag! "\b<C-R><C-W>\b"<cr>:cw<cr>

It’s worth noting that it’s possible to make :grep use Ag by default instead of relying on the default grep program, and thus, having much faster search results while keeping the same interface/command that Vim provides out of the box:

if executable('ag')
  set grepprg=ag\ --nogroup\ --nocolor
endif

"call :grep normally

Finding Files by Name

For finding files by searching for their names, I’ve been using a great plugin called ctrlp.vim. It is a fuzzy file searcher written in pure Vimscript. You only need to type some parts of the name of the file you’re searching for, and you’ll get back a list of the best matches. It can search files by file name, by current open buffers, by generated tags, or by line content. My setup for ctrlp.vim looks like this:

if executable('ag')
  let g:ctrlp_user_command='ag %s -l --nocolor --hidden -g ""'
  let g:ctrlp_use_caching=0
endif

"clear ctrlp.vim key maps
let g:ctrlp_map = ''
"search project files
nnoremap <leader>p :CtrlP<cr>
"search project files by lines of code
nnoremap <leader>o :CtrlPLine<cr>
"search project files by tags (requirs ctags to be installed)
nnoremap <leader>t :CtrlPTag<cr>
"search all open files/buffers
nnoremap <leader>r :CtrlPBuffer<cr>

ctrlp.vim has served me very well for the past couple of years. However, it can get really slow while indexing files under large directories. For example, searching files in my home directory, ~/, renders ctrlp.vim almost useless as it takes some time to index each file.

Enter fzf

fzf is a very fast command-line fuzzy finder written in Go. Despite its increasing popularity, I have tried to avoid using it for as long as possible since I like keeping my dependencies minimal and prefer to use pure Vimscript plugins to having to install any external dependencies. However, I had to install it since it’s a dependency for this really cool command-line file manager, cfiles. So I decided I’d give it a try anyway. I was mind blown!

aaku git commit

Not only can you fuzzy search for your files in the terminal, fzf also has a counterpart Vim plugin called fzf.vim which is a complementary interface to fzf in Vim with so many awesome features. One of the great features it provides is that it comes with an interface for popular search tools such as Ag and ripgrep and you can fuzzy search the search results! So now I could remove the ag.vim plugin and replace ctrlp.vim with only fzf.

Here’s my current setup for finding files in Vim:

"search project files
nnoremap <leader>p :FZF<cr>
"search project files by lines of code
nnoremap <leader>o :Lines<cr>
"search project files by tags (requirs ctags to be installed)
nnoremap <leader>t :Tags<cr>
"search all open files/buffers
nnoremap <leader>r :Buffers<cr>

"start a search query by pressing \
nnoremap \  :Ag<space>
"search for word under cursor by pressing |
nnoremap \| :Ag <C-R><C-W><cr>:cw<cr>

You need to setup fzf’s default find command by setting an env variable in your shell config:

export FZF_DEFAULT_COMMAND='ag -l --nogroup  --nocolor --hidden -g ""'

The four key maps above are all on one row in the keyboard so it’s much easier to reach out to each of them. Besides, I set my leader to the space bar, so it’s even better.

fzf.vim also provides more useful commands such as :GFiles? and :Commits for Git users. You can learn more about its commands here.

You can find my vimrc in my dotfiles repository on GitHub.