Vim:全局search和replace,从光标位置开始
当我search
/\vSEARCHTERM
Vim从光标位置向下开始search,并且会绕到顶端。 但是,当search和replace使用
:%s/\vBEFORE/AFTER/gc
Vim从文件的顶部开始。
有没有办法让Vim从光标位置开始search和replace,并且一旦到达最后,就会绕到顶端?
用两步替代来实现行为并不困难:
:,$s/BEFORE/AFTER/gc|1,''-&&
首先,从当前文件开始直到文件结束,对每行执行一个replace命令。 然后,使用:&
命令,用相同的search模式,replacestring和标志重复replace命令。 然而,后者在从文件的第一行到设置了前一个上下文标记的行的行的范围上执行replace,减去1。 由于第一个:substitute
命令在开始实际replace之前存储了光标位置,因此由''
指定的行是在replace命令运行之前的当前行。
请注意,第二个命令(在|
符号之后)在第一个命令的模式或标志被更改时不需要任何更改。
您已经在使用范围, %
,这是1,$
缩写1,$
意思是整个文件。 从当前行到最后使用.,$
。 期间表示当前行, $
表示最后一行。 所以命令是:
:.,$s/\vBEFORE/AFTER/gc
但是.
或者可以假定当前行可以被删除:
:,$s/\vBEFORE/AFTER/gc
更多帮助请看
:h range
%
是1,$
的快捷方式
(Vim help => :help :%
等于1,$(整个文件)。)
.
是游标的位置,所以你可以做
:.,$s/\vBEFORE/AFTER/gc
从文档开始处直到光标处进行replace
:1,.s/\vBEFORE/AFTER/gc
等等
我强烈build议你阅读有关范围:help range
的手册,因为几乎所有的命令都使用范围。
我终于想出了一个解决scheme,即不用编写一个巨大的函数就可以将search循环放到文件的开头。
你不会相信我花了多长时间才能拿出来。 只需添加一个提示是否包装:如果用户再次按q
,不要包装。 所以基本上,通过点击qq
而不是q
! (如果你想包装,只需键入y
。)
:,$s/BEFORE/AFTER/gce|echo 'Continue at beginning of file? (y/q)'|if getchar()!=113|1,''-&&|en
我其实已经把它映射到一个热键。 因此,例如,如果要search并replace光标下的每个单词,从当前位置开始,使用q*
:
exe 'nno q* :,$s/\<<cr>=expand("<cword>")<cr>\>//gce\|echo "Continue at beginning of file? (y/q)"\|if getchar()==121\|1,''''-&&\|en'.repeat('<left>',77)
这里有一些非常粗糙的问题,解决了使用两步方法 ( :,$s/BEFORE/AFTER/gc|1,''-&&
)或使用中间文件“Continue at beginning of file? 做法:
" Define a mapping that calls a command. nnoremap <Leader>e :Substitute/\v<<CR>=expand('<cword>')<CR>>//<Left> " And that command calls a script-local function. command! -nargs=1 Substitute call s:Substitute(<q-args>) function! s:Substitute(patterns) if getregtype('s') != '' let l:register=getreg('s') endif normal! qs redir => l:replacements try execute ',$s' . a:patterns . 'gce#' catch /^Vim:Interrupt$/ return finally normal! q let l:transcript=getreg('s') if exists('l:register') call setreg('s', l:register) endif endtry redir END if len(l:replacements) > 0 " At least one instance of pattern was found. let l:last=strpart(l:transcript, len(l:transcript) - 1) " Note: type the literal <Esc> (^[) here with <Cv><Esc>: if l:last ==# 'l' || l:last ==# 'q' || l:last ==# '^[' " User bailed. return endif endif " Loop around to top of file and continue. " Avoid unwanted "Backwards range given, OK to swap (y/n)?" messages. if line("''") > 1 1,''-&&" endif endfunction
这个函数使用一些黑客来检查我们是否应该绕到顶端:
- 如果用户按下“l”,“q”或“Esc”,则不打包,其中任何一个都表示要中止。
- 通过将macroslogging到“s”寄存器并检查最后一个字符来检测最后的按键。
- 避免通过保存/恢复“s”寄存器覆盖现有的macros。
- 如果您在使用该命令时已经录制了一个macros,则所有投注都将closures。
- 试图用中断做正确的事情。
- 避免“后退范围”的警告与明确的后卫。