我参加聚会的时间很晚,但是我经常依赖于如此晚的stackoverflow回答者而不去做。我收集了有关reddit和stackoverflow的提示,最好的选择是在搜索中使用\%>...c
模式,该模式仅在光标之后才匹配。
当我搜索时
/\vSEARCHTERM
Vim从光标位置向下开始搜索,它将环绕到顶部。但是,当搜索和替换使用
时:%s/\vBEFORE/AFTER/gc
Vim从文件的开头开始。
是否有一种方法可以使Vim从光标位置开始进行搜索和替换和一旦到达末尾,它就会环绕到顶部?
使用两步替换实现行为并不难:
:,$s/BEFORE/AFTER/gc|1,''-&&
首先,从当前文件,直到文件末尾。然后,:substitute
命令是重复相同的搜索模式,替换字符串和标志,使用:&
命令(请参阅:help :&
)。
但是,后者在行范围内执行替换从文件的第一行到上一个上下文所在的行标记已设置,减一。自第一个:substitute
命令以来在开始实际替换之前存储光标位置,由''
寻址的行是之前的当前行该替代命令已运行。 (''
地址指的是'
伪标记;有关详细信息,请参见:help :range
和:help ''
。)
请注意,第二个命令(在|
命令分隔符之后,请参见:help :bar
)不需要任何更改时的模式或标志在第一个中被更改。
为了保存一些输入内容,以显示上面的框架在命令行中的替换命令,可以定义一个普通模式像这样映射:
:noremap <leader>R :,$s///gc\|1,''-&&<c-b><right><right><right><right>
尾随的<c-b><right><right><right><right>
部分是必需的将光标放在前两个斜杠符号之间,为用户开始输入搜索模式。一旦所需的模式并准备好替换,可以通过以下命令运行结果命令按Enter。
((在上面的映射中,您可能考虑使用//
而不是///
,如果喜欢键入图案,则键入分隔斜杠自己,然后是替换字符串,而不是使用向右箭头将光标移到已经存在的分隔符上斜线开始更换零件。)
您已经在使用范围%
,这是1,$
的缩写,表示整个文件。要从当前行到末尾,请使用.,$
。句点表示当前行,$
表示最后一行。因此命令将是:
:.,$s/\vBEFORE/AFTER/gc
但是可以假定.
或当前行,因此可以将其删除:
:,$s/\vBEFORE/AFTER/gc
有关更多帮助,请参阅
:h range
%
是1,$
的快捷方式(Vim帮助=> :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)
[这很粗糙,解决了使用the two-step approach(:,$s/BEFORE/AFTER/gc|1,''-&&
)或中间"Continue at beginning of file?"方法包装搜索的问题:
" 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
之类的解决方案进行工作时,请牢记以下模式会过滤掉光标位置左侧all
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
我参加聚会的时间很晚,但是我经常依赖于如此晚的stackoverflow回答者而不去做。我收集了有关reddit和stackoverflow的提示,最好的选择是在搜索中使用\%>...c
模式,该模式仅在光标之后才匹配。