Vim Search Across Multiple Files in Project
Last updated: November 03, 2022I frequently find myself needing to search for a string across all files in the current project. Before I switched to Vim, I would use TextMate’s Find in Project… feature to do this, which is highly efficient. After switching to Vim, however, for years I found myself without an equally effective solution and often capitulated by launching TextMate when I needed to search across multiple files. I would find the relevant file(s), open in Vim, and then continue editing there — clearly not an ideal solution.
Before you shout, “Why don’t you just :grep -R your-search-term .
?” … Well, I tried that, but I didn’t find the output to be very useful. So I finally decided to do something about it and devised a solution that works well for me.
The first step is deciding what “engine” you want to power your searches. My order of preference is:
- ripgrep (A.K.A.
rg
) - The Silver Searcher (A.K.A.
ag
) - ack
Once you have at least one of the above search tools installed, the next step is to install the ack.vim plugin via your preferred Vim plugin installation method.
To tell ack.vim to use my order of preference above, put the following in your .vimrc
:
" Prefer rg > ag > ack
if executable('rg')
let g:ackprg = 'rg -S --no-heading --vimgrep'
elseif executable('ag')
let g:ackprg = 'ag --vimgrep'
endif
To begin searching, :cd
to the desired directory and invoke ack.vim to search across all the files in that directory:
:cd ~/projects/yourproject
:Ack your-search-term
If you’d rather not have to :cd
every time to set the search scope, install the Rooter Vim plugin to automatically set the working directory to the current project root.
I prefer to type a single keystroke to begin searching. The following Vim configuration aliases the backslash key to :Ack
:
" Backslash invokes ack.vim
nnoremap \ :Ack<SPACE>
Search results will be presented in a location list, with the first result shown in the current buffer. You can tap the Return key to select that first result and dismiss the location list window, or you can use the usual movement keys (or search commands) to highlight other items in the list. Once you have highlighted the desired result, there are a number of keys you can use to open that file in a new tab or split.
Recursive Search & Replace
I haven’t found a great way to do recursive search and replace operations within Vim, so I currently use fd and sd for this purpose. For example, I recently wanted to replace a bunch of single-quotation marks to double-quotation marks, changing all occurrences of:
{% include 'path/to/django/templates/some-template.html' %}
… to:
{% include "path/to/django/templates/some-template.html" %}
I restricted the list of files to those with .html
extensions and then used a regular-expression capture group to effect the changes:
sd "include '(.*)'" 'include "$1"' $(fd -e html)
Have a better solution?
I’m curious how other folks are searching across multiple files from within Vim, so please let me know how you handle this use case.
If you found this article to be useful, please follow me on Twitter and be sure to check out Fortressa!