Skip to main content

How to Search Across an Entire Project in Vim

·1014 words·5 mins
Linux Learning Lab
Author
Linux Learning Lab
Writing about code, tools, and workflows.

The Problem
#

You’re working on a project with hundreds of files. You need to find where a function is called, where an error message is defined, or every file that references a configuration value. Opening files one by one and searching with / isn’t practical.

Vim has built-in project-wide search that populates the quickfix list — letting you jump between results across files instantly.

Method 1: :grep (External grep)
#

:grep runs an external search tool and loads results into the quickfix list.

Basic usage
#

" Search for a string in all files recursively
:grep "processOrder" **/*

" Search in specific file types
:grep "TODO" **/*.py
:grep "import" **/*.js

" Case-insensitive
:grep -i "error" **/*.log

After running :grep, use :copen to see all matches and :cnext/:cprev to jump between them.

Make it fast with ripgrep
#

The default grep is slow on large projects. Configure Vim to use ripgrep — it’s dramatically faster and respects .gitignore automatically:

" In your .vimrc
set grepprg=rg\ --vimgrep\ --smart-case
set grepformat=%f:%l:%c:%m

Now :grep uses ripgrep behind the scenes:

" Same command, 10-100x faster
:grep "processOrder"

" No need for **/* — ripgrep searches recursively by default
" No need for -i — smart-case handles it (case-insensitive unless you use uppercase)

Why ripgrep?
#

Featuregrepripgrep
SpeedSlow on large treesExtremely fast
.gitignoreDoesn’t respect itSkips ignored files automatically
RecursiveNeeds -r flagRecursive by default
Binary filesSearches themSkips them

Install it:

# Debian/Ubuntu
sudo apt install ripgrep

# Fedora/RHEL
sudo dnf install ripgrep

Method 2: :vimgrep (Built-in)
#

:vimgrep uses Vim’s own regex engine. Slower than external grep, but available everywhere and uses familiar Vim regex syntax.

" Search for a pattern
:vimgrep /pattern/ **/*.js

" Vim regex features work
:vimgrep /function\s\+\w\+/ **/*.js
:vimgrep /TODO\|FIXME\|HACK/ **/*

" Case-insensitive with \c
:vimgrep /\cerror/ **/*.py

When to use vimgrep over grep
#

  • When you need Vim’s regex syntax (lookaheads, \v very magic, etc.)
  • When you don’t have ripgrep or ag installed
  • When working on a remote system with limited tools

Navigating Results#

Both :grep and :vimgrep populate the quickfix list. Navigate with:

CommandAction
:copenOpen the quickfix window
:ccloseClose it
:cnext / :cnJump to next match
:cprev / :cpJump to previous match
:cfirstJump to first match
:clastJump to last match
:cc 5Jump to match #5

Recommended mappings#

" In .vimrc
nnoremap ]q :cnext<CR>
nnoremap [q :cprev<CR>
nnoremap <leader>co :copen<CR>
nnoremap <leader>cc :cclose<CR>

Filtering Results
#

Narrow down after searching
#

" Keep only results matching a second pattern
:cfilter /controller/

" Remove results matching a pattern
:cfilter! /test/
:cfilter! /node_modules/

Previous search results
#

Vim keeps a stack of quickfix lists:

:colder     " Go back to previous search results
:cnewer     " Go forward
:chistory   " Show all stored lists

Searching for the Word Under the Cursor
#

Quick project-wide search#

" Search for the exact word under cursor across the project
:grep <cword>

Map it for one-keystroke project search:

" In .vimrc — press <leader>g to grep for word under cursor
nnoremap <leader>g :grep <cword><CR>:copen<CR>

Now: cursor on processOrder, press <leader>g, and every occurrence across the project appears in the quickfix list.

Search and Replace Across a Project
#

Using grep + cfdo
#

" 1. Find all occurrences
:grep "oldFunction"

" 2. Replace in all files that matched
:cfdo %s/oldFunction/newFunction/gc | update

cfdo runs a command on every file in the quickfix list. The | update saves each file.

Using arglist
#

" 1. Load files into arglist
:args **/*.js

" 2. Replace across all of them
:argdo %s/oldFunction/newFunction/gc | update

With confirmation at each match
#

The c flag in both approaches asks you at each occurrence — safe for renaming where context matters.

Searching for Specific Patterns
#

Find function definitions
#

" JavaScript/TypeScript
:grep "function processOrder"
:grep "const processOrder ="

" Python
:grep "def process_order"

" C
:grep "int process_order("

Find TODO/FIXME comments
#

:grep "TODO\|FIXME\|HACK\|XXX"
:copen

Find imports/requires
#

" JavaScript
:grep "require.*database"
:grep "import.*from.*utils"

" Python
:grep "from.*import\|import.*"

Find configuration values
#

:grep "DATABASE_URL\|DB_HOST\|DB_PORT"

Live Search with :grep Workflow
#

A fast iterative workflow:

" 1. Search broadly
:grep "order"

" 2. Too many results — narrow down
:cfilter /process/

" 3. Still too many — remove test files
:cfilter! /test/

" 4. Now browse the focused results
:cnext
:cnext

Searching Within Specific Directories
#

" Only in src/
:grep "pattern" src/

" Only in tests
:grep "pattern" tests/

" Exclude a directory (ripgrep)
:grep "pattern" --glob "!node_modules"

" Only specific file types (ripgrep)
:grep "pattern" -t py
:grep "pattern" -t js

Using the Location List for Parallel Searches
#

The quickfix list is global — one list at a time. The location list is per-window, so you can have multiple searches open:

" Search in one split
:lgrep "functionA" **/*.js
:lopen

" Switch to another split, different search
:lgrep "functionB" **/*.js
:lopen

Navigate with :lnext/:lprev instead of :cnext/:cprev.

Complete .vimrc Setup for Project Search#

" Use ripgrep for :grep
set grepprg=rg\ --vimgrep\ --smart-case
set grepformat=%f:%l:%c:%m

" Quick search for word under cursor
nnoremap <leader>g :grep <cword><CR>:copen<CR>

" Prompt for a search term
nnoremap <leader>/ :grep ""<Left>

" Quickfix navigation
nnoremap ]q :cnext<CR>
nnoremap [q :cprev<CR>
nnoremap <leader>co :copen<CR>
nnoremap <leader>cc :cclose<CR>

With this setup:

  • <leader>g — instantly search for the word under your cursor
  • <leader>/ — type a search term and hit Enter
  • ]q / [q — step through results
  • <leader>co / <leader>cc — open/close the results window

Best Practices
#

  • Install ripgrep and set it as grepprg — the speed difference on real projects is enormous
  • Use :grep over :vimgrep for project searches — external tools are always faster
  • Map <leader>g to grep the word under cursor — it becomes your “find usages” shortcut
  • Use :cfilter to narrow results instead of re-running the search
  • Use :colder/:cnewer to keep previous search results accessible
  • Combine with :cfdo for project-wide find-and-replace
  • Use the location list (:lgrep) when you need multiple searches open in different splits
  • Add --smart-case to ripgrep so searches are case-insensitive unless you use uppercase