How to search and replace globally, starting from the cursor position and wrapping around the end of file, in a single command invocation in Vim?
When I search with the /
Normal-mode command:
/\vSEARCHTERM
Vim starts the search from the cursor position and continues downwards, wrapping around to the top. However, when I search and replace using the :substitute
command:
:%s/\vBEFORE/AFTER/gc
Vim starts at the top of the file, instead.
Is there a way to make Vim perform search and replace starting from the cursor position and wrapping around to the top once it reaches the end?
You are already using a range, %
, which is short hand for 1,$
meaning the entire file. To go from the current line to the end you use .,$
. The period means current line and $
means the last line. So the command would be:
:.,$s/\vBEFORE/AFTER/gc
But the .
or current line can be assumed therefore can be removed:
:,$s/\vBEFORE/AFTER/gc
For more help see
:h range
1. It is not hard to achieve the behavior in question using a two-step substitution:
:,$s/BEFORE/AFTER/gc|1,''-&&
First, the substitution command is run for each line starting from the current one until the end of file:
,$s/BEFORE/AFTER/gc
Then, that :substitute
command is repeated with the same search
pattern, replacement string, and flags, using the :&
command
(see :help :&
):
1,''-&&
The latter, however, performs the substitution on the range of lines
from the first line of the file to the line where the previous context
mark has been set, minus one. Since the first :substitute
command
stores the cursor position before starting actual replacements, the
line addressed by ''
is the line that was the current one before
that substitution command was run. (The ''
address refers to the
'
pseudo-mark; see :help :range
and :help ''
for details.)
Note that the second command (after the |
command separator—see
:help :bar
) does not require any change when the pattern or flags
are altered in the first one.
2. To save some typing, in order to bring up the skeleton of the above substitution command in the command line, one can define a Normal-mode mapping, like so:
:noremap <leader>cs :,$s///gc\|1,''-&&<c-b><right><right><right><right>
The trailing <c-b><right><right><right><right>
part is necessary
to move the cursor to the beginning of the command line (<c-b>
) and
then four characters to the right (<right>
× 4),
thus putting it between the first two slash signs, ready for the user
to start typing the search pattern. Once the desired pattern and the
replacement are ready, the resulting command can be run by pressing
Enter.
(One might consider having //
instead of ///
in the mapping above,
if one prefers to type the pattern, then type the separating slash
oneself, followed by the replacement string, instead of using the
right arrow to move the cursor over an already present separating
slash starting the replacement part.)
%
is a shortcut for 1,$
( Vim help => :help :%
equal to 1,$ (the entire file).)
.
is the cursor postion so you can do
:.,$s/\vBEFORE/AFTER/gc
To replace from the beginning of the document till the cursor
:1,.s/\vBEFORE/AFTER/gc
etc
I strongly suggest you read the manual about range :help range
as pretty much all commands work with a range.
I've FINALLY come up with a solution to the fact that quitting the search wraps around to the beginning of the file without writing an enormous function...
You wouldn't believe how long it took me to come up with this. Simply add a prompt whether to wrap: if the user presses q
again, don't wrap. So basically, quit search by tapping qq
instead of q
! (And if you do want to wrap, just type y
.)
:,$s/BEFORE/AFTER/gce|echo 'Continue at beginning of file? (y/q)'|if getchar()!=113|1,''-&&|en
I actually have this mapped to a hotkey. So, for example, if you want to search and replace every word under the cursor, starting from the current position, with q*
:
exe 'nno q* :,$s/\<<c-r>=expand("<cword>")<cr>\>//gce\|echo "Continue at beginning of file? (y/q)"\|if getchar()==121\|1,''''-&&\|en'.repeat('<left>',77)