Vim:全局搜索和替换,从光标位置开始

当我搜索时

/\vSEARCHTERM

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

:%s/\vBEFORE/AFTER/gc

Vim从文件的顶部开始。

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

basteln asked 2019-07-15T10:41:44Z
6个解决方案
151 votes

您已经在使用范围.,这是1,$的简短描述整个文件。 要从当前行转到结束,请使用.,$.期间表示当前行,$表示最后一行。 所以命令是:

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

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

:,$s/\vBEFORE/AFTER/gc

有关更多帮助请参阅

:h range
Peter Rincker answered 2019-07-15T10:42:59Z
45 votes

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

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

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

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

ib. answered 2019-07-15T10:42:20Z
44 votes

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

:help range是光标位置,所以你可以这样做

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

从文档的开头替换到光标

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

等等

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

mb14 answered 2019-07-15T10:43:56Z
3 votes

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

你不会相信我花了多长时间来想出这个。 只需添加是否换行的提示:如果用户再次按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)
q335r49 answered 2019-07-15T10:44:40Z
1 votes

这里有一些非常粗略的内容,通过两步法(:,$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

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

  • 如果用户按下“l”,“q”或“Esc”,则无包装,其中任何一个都表示要中止。
  • 通过将宏记录到“s”寄存器并检查其最后一个字符来检测最后一次按键。
  • 通过保存/恢复“s”寄存器来避免覆盖现有的宏。
  • 如果您在使用该命令时已经录制了一个宏,则所有投注都将关闭。
  • 尝试用中断做正确的事情。
  • 使用明确的警卫避免“向后射程”警告。
wincent answered 2019-07-15T10:45:59Z
1 votes

我迟到了,但我经常依赖这种迟到的stackoverflow应答器来做不到。 我收集了关于reddit和stackoverflow的提示,最好的选择是在搜索中使用\%>...c模式,它只在光标后匹配。

也就是说,它也会破坏下一个替换步骤的模式,并且很难打字。 为了抵消这些影响,自定义函数必须在之后过滤搜索模式,从而重置它。 见下文。

我用自己的一个映射来代替下一个出现并跳转到下面的内容,而不是更多(无论如何都是我的目标)。 我相信,在此基础上,可以制定出全球替代品。 在处理针对像 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 之类的解决方案时,请记住,下面的模式会过滤掉留在光标位置的所有行中的匹配 - 但在单个替换完成后会被清除,因此它会在之后直接丢失该效果,跳转到 下一场比赛,因此能够一个接一个地完成所有比赛。

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

simlei answered 2019-07-15T10:46:52Z
translate from https://stackoverflow.com:/questions/7598133/vim-global-search-replace-starting-from-the-cursor-position