Skip to main content

Search and Replace

Neovim's search-and-replace system is one of the most powerful in any text editor. It uses regular expressions, supports range-limited substitution, can operate across multiple files, and provides real-time preview with inccommand.

Core Idea

The substitute command syntax is: :[range]s/pattern/replacement/[flags] Once you know the parts, you can build any substitution precisely.

Basic Searching

/pattern          → Search forward
?pattern → Search backward
n → Next match
N → Previous match
* → Search word under cursor (forward)
# → Search word under cursor (backward)
:noh → Clear search highlight

Enable Live Search Highlight

~/.config/nvim/lua/config/options.lua
vim.opt.incsearch = true     -- show matches as you type
vim.opt.hlsearch = true -- highlight all matches
vim.opt.ignorecase = true -- case insensitive by default
vim.opt.smartcase = true -- case sensitive if uppercase in pattern

The Substitute Command

:[range]s/{pattern}/{replacement}/[flags]

Ranges

RangeMeaning
(none)Current line only
%Entire file
1,10Lines 1 to 10
'<,'>Visual selection (auto-inserted)
.,+5Current line + 5 lines
.,/end/Current line to line containing "end"

Flags

FlagMeaning
gGlobal — replace all on each line
iCase insensitive
ICase sensitive (override smartcase)
cConfirm each replacement
eSuppress "no match" errors
nCount matches (no replacement)

Common Examples

" Replace first occurrence on current line
:s/old/new/

" Replace all on current line
:s/old/new/g

" Replace all in entire file
:%s/old/new/g

" Replace all, case insensitive
:%s/old/new/gi

" Replace all, ask for confirmation on each
:%s/old/new/gc (y/n/q/a/l at each prompt)

" Replace only in lines 10-20
:10,20s/old/new/g

" Replace in visual selection
:'<,'>s/old/new/g

" Count how many matches (no change)
:%s/pattern//gn

Live Substitution Preview

Enable real-time preview of replacements:

~/.config/nvim/lua/config/options.lua
vim.opt.inccommand = "split"   -- preview in split window
-- or
vim.opt.inccommand = "nosplit" -- preview inline only

Now as you type :%s/old/new, you see the changes highlighted in the buffer before pressing Enter.

Neovim uses a variant of regex called "very magic mode":

" Standard (limited special chars)
/word

" Very magic: \v enables most regex chars without escaping
/\vword|other

" Examples with \v (very magic):
/\v\d{3}-\d{4} → phone number pattern
/\vfunction\s+\w+\(function declaration
/\v(foo|bar)baz → foo or bar followed by baz

" Capture groups in substitute:
:%s/\v(\w+), (\w+)/\2 \1/g → swap "Last, First" to "First Last"

Substitute with Capture Groups

" Swap order: "LastName, FirstName" → "FirstName LastName"
:%s/\(\w\+\), \(\w\+\)/\2 \1/g

" In very magic mode (\v):
:%s/\v(\w+), (\w+)/\2 \1/g (cleaner)

" Add quotes around a value:
:%s/\vkey: (\w+)/key: "\1"/g

" Replace function calls:
:%s/\vlog\((.{-})\)/console.log(\1)/g

Global Command (:g)

:g runs a command on every line matching a pattern:

" Delete all blank lines
:g/^$/d

" Delete all lines containing "TODO"
:g/TODO/d

" Copy all lines matching pattern to end of file
:g/pattern/t$

" Run a substitute on all lines containing "import"
:g/import/s/from '/from "

" Print all matching lines (like grep)
:g/error/p

" Reverse: run command on non-matching lines
:v/pattern/command (or :g!/pattern/command)

Multi-File Search and Replace

Built-in Approach

" Open all JS files
:args **/*.js

" Do the replacement across all open args
:argdo %s/oldFunc/newFunc/ge | update

" Or for quickfix results from :vimgrep:
:vimgrep /pattern/ **/*.js
:cfdo %s/pattern/replacement/g | update
<leader>sr   → Telescope live grep + replace (with plugin)

Search and Replace with Confirmation

:%s/old/new/gc

At each match, you see:

replace with new (y/n/q/a/l/?)?
y - yes for this one
n - skip this one
q - quit
a - all remaining (no more asking)
l - yes for this one then quit (last)

The cgn Pattern (Modern Replace Workflow)

A modern alternative to global substitute using gn and the dot command:

/target       → search for the target word
cgn → change the next match (enters insert)
replacement → type the replacement
Esc → back to Normal
n. → find next match, apply same change
n.n.n. → repeat as needed

This is the "find, change, repeat" workflow. It's better than %s when you want to selectively replace some (not all) occurrences.

What's Next