How do I restrict a substitute command to only the text within my last visual selection using the percent-V atom?
Answer
:s/\%Vfoo/bar/
Explanation
The \%V atom in Vim's regex engine matches only within the area of the last visual selection. Unlike the '<,'> range that restricts which lines are affected, \%V restricts which columns are matched within those lines — making it essential for blockwise visual selections and for avoiding partial-line matches at the selection boundaries.
How it works
\%Vis a zero-width atom that asserts the current match position is inside the last visual selection- When combined with
:'<,'>s/\%Vpattern/replacement/, the range limits which lines are searched while\%Vlimits which columns match - Without
\%V, a line-range substitute can match anywhere on the selected lines, including text outside the visually selected columns in a blockwise selection
Example
Given this text with a blockwise (<C-v>) selection covering only the numbers:
foo 123 bar
baz 456 qux
Running :'<,'>s/\%V\d/X/g replaces only the visually selected digits:
foo XXX bar
baz XXX qux
Without \%V, the same command would also match any digits outside the selected columns.
Tips
\%Vuses the last visual selection — reusable even after leaving visual mode- Combine with
gvto reselect the last visual area before running the command - Works with all visual modes: characterwise
v, linewiseV, and blockwise<C-v> - Available in search patterns too:
/\%Vwordhighlights occurrences only within the visual area