Answers:
1. 使用两步替换实现行为并不难:
:,$s/BEFORE/AFTER/gc|1,''-&&
首先,从当前行开始到文件末尾的每一行都执行替换命令:
,$s/BEFORE/AFTER/gc
然后,使用以下:substitute
命令以相同的搜索模式,替换字符串和标志重复该:&
命令:help :&
:
1,''-&&
但是,后者对从文件的第一行到设置了先前上下文标记的行的行范围(减一)执行替换。由于第一个:substitute
命令在开始实际替换之前就存储了光标位置,因此寻址到 ''
的行是运行该替换命令之前的当前行。(''
地址指的是
'
伪标记;有关详细信息,请参见:help :range
和:help ''
。)
请注意,当在第一个命令中更改了模式或标志时,第二个命令(在|
命令分隔符之后,请参阅
:help :bar
)不需要任何更改。
2. 为了节省一些键入内容,为了在命令行中显示上述替换命令的框架,可以定义一个普通模式映射,如下所示:
:noremap <leader>cs :,$s///gc\|1,''-&&<c-b><right><right><right><right>
尾部<c-b><right><right><right><right>
必须将光标移动到命令行的开头(<c-b>
),然后再将其向右移动四个字符(<right>
×4),从而将其置于前两个斜杠符号之间,以便用户开始键入搜索模式。一旦所需的样式和替换准备就绪,可以按来运行结果命令
Enter。
(如果不喜欢在上面的映射中,则可以考虑使用//
而不是///
上面的映射,然后自己键入分隔斜杠,然后输入替换字符串,而不是使用向右箭头将光标移到已经存在的分隔斜杠上替换零件。)
:noremap <leader>R :,$s///gc\|1,''-&&<c-b><right><right><right><right>
。它将:,$s///gc|1,''-&&
光标置于命令行的前两个斜杠符号之间。键入所需的模式并替换后,可以按Enter键运行结果命令。
您已经在使用范围,%
这是1,$
表示整个文件的简称。要从当前行到结尾使用.,$
。句号表示当前行,$
表示最后一行。因此,命令将是:
:.,$s/\vBEFORE/AFTER/gc
但是.
可以假定或当前行,因此可以将其删除:
:,$s/\vBEFORE/AFTER/gc
有关更多帮助,请参见
:h range
10:s/pattern/replace/gc
%
是1,$
(Vim help => :help :%
等于1 ,, $(整个文件)的快捷方式)。
.
是光标位置,因此您可以
:.,$s/\vBEFORE/AFTER/gc
从文档开头到光标的替换
:1,.s/\vBEFORE/AFTER/gc
等等
我强烈建议您阅读有关范围的手册,:help range
因为几乎所有命令都适用于范围。
我终于想出了一个解决方案,可以解决以下问题:退出搜索会绕到文件的开头,而无需编写巨大的功能...
您不会相信我花了多长时间才想到这个。只需添加一个提示是否换行即可:如果用户q
再次按下,则不要换行。因此,基本上,请点击qq
而不是来退出搜索q
。(如果您确实想换行,只需输入即可y
。)
:,$s/BEFORE/AFTER/gce|echo 'Continue at beginning of file? (y/q)'|if getchar()!=113|1,''-&&|en
我实际上已将此映射到热键。因此,例如,如果您要搜索并替换光标下的每个单词,请从当前位置开始,将其替换为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)
yyyq
,理想的方法是继续编辑,而不会使方向混乱。但是yyyqq
,如果您考虑使用双击,则几乎可以说是一次点击,效果几乎一样好。
q
第二次按下时处于该位置)。后者将光标留在被替换的最后一个出现的位置(恰好在第一次q
按下之前)。
这是一个非常粗糙的内容,它解决了使用两步方法(:,$s/BEFORE/AFTER/gc|1,''-&&
)或中间的“在文件开头继续吗?”将搜索包装起来的问题。方法:
" Define a mapping that calls a command.
nnoremap <Leader>e :Substitute/\v<<C-R>=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 <C-v><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
此函数使用了一些技巧来检查我们是否应该绕到顶部:
我参加晚会很晚,但是我经常依赖于如此晚的stackoverflow应答程序而不这样做。我收集了有关reddit和stackoverflow的提示,最好的选择是\%>...c
在搜索中使用模式,该模式仅在光标之后才匹配。
也就是说,它也弄乱了下一个替换步骤的模式,并且很难键入。为了应对这些影响,自定义功能必须随后过滤搜索模式,从而将其重置。见下文。
我已经为自己争辩了一个映射,该映射替换了下一次出现的内容,之后又跳到了下一个内容,而不是更多(无论如何是我的目标)。我相信,在此基础上,可以制定出全球替代方案。在针对某个解决方案进行工作时,请记住:%s/.../.../g
以下模式,该模式会过滤掉光标所在位置的所有行中的匹配项-但在单次替换完成后会被清除,因此在跳转到下一场比赛,因此能够一场又一场地进行所有比赛。
fun! g:CleanColFromPattern(prevPattern)
return substitute(a:prevPattern, '\V\^\\%>\[0-9]\+c', '', '')
endf
nmap <F3>n m`:s/\%><C-r>=col(".")-1<CR>c<C-.r>=g:CleanColFromPattern(getreg("/"))<CR>/~/&<CR>:call setreg("/", g:CleanColFromPattern(getreg("/")))<CR>``n
\vpattern
-“非常魔术”模式:非字母数字字符被解释为特殊的正则表达式符号(无需转义)