Vim:全球搜索&替换,从光标位置开始

时间:2011-09-29 13:35:57

标签: search vim replace

当我用

搜索时
/\vSEARCHTERM

Vim从光标位置向下开始搜索,它将环绕到顶部。但是当搜索&替换使用

:%s/\vBEFORE/AFTER/gc

Vim从文件的顶部开始。

有没有办法让Vim搜索&从光标位置开始替换并在到达结尾时回绕到顶部?

6 个答案:

答案 0 :(得分:152)

您已使用范围%,这是1,$的简写,意味着整个文件。要从当前行转到结尾,请使用.,$。句点表示当前行,$表示最后一行。所以命令是:

:.,$s/\vBEFORE/AFTER/gc

但可以假设.或当前行可以删除:

:,$s/\vBEFORE/AFTER/gc

如需更多帮助,请参阅

:h range

答案 1 :(得分:45)

%1,$的快捷方式 (Vim help => :help :%等于1,$(整个文件)。)

.是光标位置,因此您可以

:.,$s/\vBEFORE/AFTER/gc

从文档的开头替换到光标

:1,.s/\vBEFORE/AFTER/gc

我强烈建议您阅读有关范围:help range的手册,因为几乎所有命令都适用于范围。

答案 2 :(得分:45)

使用两步替换来实现行为并不困难:

:,$s/BEFORE/AFTER/gc|1,''-&&

首先,从每个行开始执行替换命令 当前的一个直到文件结束。然后,:substitute命令是 用相同的搜索模式,替换字符串和标志重复, 使用:&命令。然而,后者执行替换 在从文件的第一行到行的行的范围 已设置上一个上下文标记,减去一个。自从第一次 :substitute命令在开始实际之前存储光标位置 替换,由''解决的行是该行 在该替换命令运行之前的当前一个。

请注意,第二个命令(在|符号之后)不需要任何命令 当第一个的模式或标志被改变时改变。

答案 3 :(得分:3)

我终于提出了一个解决方案,即在不编写巨大功能的情况下退出搜索包裹到文件的开头...

你不会相信我花了多长时间来提出这个问题。只需添加是否换行的提示:如果用户再次按下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)

答案 4 :(得分:1)

这里有一些非常粗略的内容,解决了使用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

这个函数使用了几个hack来检查我们是否应该回到顶部:

  • 如果用户按下&#34; l&#34;,&#34; q&#34;或者&#34; Esc&#34;,其中任何一个表示希望中止。
  • 通过将宏录制到&#34; s&#34;来检测最终按键。注册并检查它的最后一个字符。
  • 通过保存/恢复&#34;&#34;避免覆盖现有的宏。寄存器。
  • 如果您在使用该命令时已经录制了一个宏,则所有投注均已关闭。
  • 尝试用中断做正确的事。
  • 避免&#34;向后范围&#34;有明确警卫的警告。

答案 5 :(得分:1)

我参加聚会的时间很晚,但是我太过依赖如此晚的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