使用vim的f命令跨越多行

54

有没有人知道如何快速找到下一个字符的位置(类似于f命令),但是针对多行?也就是说,如何快速跳转到文件中某个字符的下一个出现位置?


1
我在这里总结了一些替代方案,包括我帮助编写的一个可以在屏幕上闪烁计数的方案!(链接:http://vi.stackexchange.com/questions/4486/expand-f-and-t-motion-to-next-lines#4525) - joeytwiddle
我也曾为此苦恼。在查看了这篇文章后,我得出了以下结论:如果只相隔一行,使用 jk 要比 f{char} 更便宜或者等价。如果相隔多于一行,则使用 / 更便宜。你也可以跳转到该行,然后使用 f 命令(#G f{char})。 - young_souvlaki
8个回答

52

这不是“/”的作用吗?

如果你想要查找下一个"x",那么在命令模式下执行 /x 命令。

然后你可以按 "n" 键来移动到下一个 x,然后下一个 x,等等。

有很多vim 速查表提供各种技巧。


1
谢谢!我确实知道 /,但我想知道你是否知道更快的方法(抱歉我没有在最初的问题中提到)。除了键入 / 然后需要键入 <enter> 之外,是否有一种单键方式来完成?谢谢! - Koko
12
嗯......"fx"只需要两个击键。而输入"/x<enter>"则需要三个击键。在第一次匹配之后,无论是哪种情况下,你都可以通过再按一个键来进行后续匹配。除了vi(m)用户以外,谁会对这样的效率低下感到不满呢 :) 你是否总是在搜索相同的字符或字符类型?如果是这样的话,可能会有一个解决方案适合你,特别是当它与编程有关时,比如"移动到下一个大括号"。 - Peter Recore
@Koko - 答案是肯定的,有一个字符可以找到下一个出现的位置,那就是字母'n'。这也包含在答案中。 - KevinDTimm
8
你在回答中没有涉及一个重要的区别,dfx会删除从光标所在位置一直到包括x这个字符。而另一方面,d/x<enter>会删除从光标所在位置一直到但不包括x这个字符。 - Andy E

13

'eval.txt' 帮助文件中有一个重新定义 f 忽略大小写的示例。

:h eval.txt

在几个地方搜索"忽略大小写"。

我们可以修改它以满足您的需求。将以下函数添加到您的~/.vimrc文件中,或更好的方法是创建一个插件文件:~/.vim/plugin/find-char.vim(如果没有则创建目录)。

function FindChar()
    let c = nr2char( getchar() )
    let match = search('\V' . c)
endfunction
然后,在您的~/.vimrc文件中添加以下行:
nmap f :call FindChar()<CR>

现在,f 应该像你想要的那样工作。

顺便提一下,这是在 Ubuntu 10.04 上测试过 Vim 7.2 的。


1
如果你在其中搜索 $, \, ^/ 等字符(可能还包括星号、问号等),那么这种方法将行不通。你可以尝试使用 let command = "normal! /\V" . c . "^M",但这对于反斜杠也无法正常工作。 - Benoit
2
@Benoit:啊,特殊字符,我应该知道得更好。我已经更新了响应。 - Curt Nelson
1
如果你在每个搜索模式前加上 '\V',就可以跳过 if 语句 -- 函数 FindChar() 让 c = nr2char( getchar() ) 让 match = search('\V' . c) endfunction - Kimball Robinson
如果你想把“上次搜索”设为该字符,可以在函数中添加以下内容:调用 setreg("/", c) 函数 -- 这样,“n”和“p”命令之后就会非常有用。 - Kimball Robinson
1
您可能还想编写函数和映射,以使 F、t 和 T 命令受到类似的影响。 - Kimball Robinson

11

Christian Brabandt的ft_improved插件扩展了内置的f/t命令,使其可以在下一行中搜索。


7
谢谢,我试过了。但最终我使用了http://github.com/dahu/vim-fanfingtastic(如果你也安装了http://github.com/tpope/vim-repeat,它可以与“.”命令一起使用)。9个月没有任何投诉! - joeytwiddle
至少对我来说,improvedft和vim-fanfingtastic都无法处理像2f,这样的内容,人们可能期望将光标跳转到下一个逗号后面。前者插件在遇到这个序列时什么也不做,而后者只会简单地跳过一个逗号,而不是两个。 - koyae

7

我曾经也想知道同样的问题,所以我查看了这里的答案。但是,没有一个答案完全符合我的要求,所以我将它们拼凑在一起。

q335的答案最接近,因为它正确处理了omaps,这对于像dt}(删除到下一个花括号,但不包括它)这样的操作是必要的。但是,Curt的答案处理特殊字符搜索并使用单个函数,这对我来说更加可取,因此我不会在我的.vimrc文件中添加太多内容。

以下是我的结果:

"Makes f and t work across multiple lines
nmap <silent> f :call FindChar(0, 0, 0)<cr>
omap <silent> f :call FindChar(0, 1, 0)<cr>
nmap <silent> F :call FindChar(1, 0, 0)<cr>
omap <silent> F :call FindChar(1, 1, 0)<cr>
nmap <silent> t :call FindChar(0, 0, 1)<cr>
omap <silent> t :call FindChar(0, 0, 0)<cr>
nmap <silent> T :call FindChar(1, 0, 1)<cr>
omap <silent> T :call FindChar(1, 0, 0)<cr>

"Functions
fun! FindChar(back, inclusive, exclusive)
  let flag = 'W' 
  if a:back
    let flag = 'Wb'
  endif
  if search('\V' . nr2char(getchar()), flag)
    if a:inclusive
      norm! l
    endif
    if a:exclusive
      norm! h
    endif
  endif
endfun

看起来工作正常,但它没有保留搜索的字符以供“;”和“,”使用。 - joeytwiddle
从光标位置到闭合大括号:]} - 如果你想删除:d]} - 对于圆括号也适用)] - 移动到开放大括号是[{。 - Lucas Hoepner
1
当以数字为前缀时,对我无效,例如 c2fa 只会更改到第一个 a - instanceof me

5

嗨,vimscript。要写好20行代码需要大约2年时间:D。这里是最新、最流畅的版本:适用于所有模式:可视、操作挂起、普通。

let prvft='f'
let prvftc=32
fun! MLvF(c,...)
    let [g:prvftc,g:prvft]=[a:c,a:0? 'f':'F']
    let pos=searchpos('\C\V'.nr2char(g:prvftc),'bW')
    call setpos("'x", pos==[0,0]? [0,line('.'),col('.'),0] : [0,pos[0],pos[1],0])
    return "`x"
endfun
fun! MLvf(c,...)
    let [g:prvftc,g:prvft]=[a:c,a:0? 'F':'f']
    let pos=searchpos('\C\V'.nr2char(g:prvftc).(mode(1)=='no'? '\zs' : ''),'W')
    call setpos("'x", pos==[0,0]? [0,line('.'),col('.'),0] : [0,pos[0],pos[1],0])
    return "`x"
endfun
fun! MLvT(c,...)
    let [g:prvftc,g:prvft]=[a:c,a:0? 't':'T']
    let pos=searchpos('\C\V'.nr2char(g:prvftc).'\zs','bW')
    call setpos("'x", pos==[0,0]? [0,line('.'),col('.'),0] : [0,pos[0],pos[1],0])
    return "`x"
endfun
fun! MLvt(c,...)
    let [g:prvftc,g:prvft]=[a:c,a:0? 'T':'t']
    let pos=searchpos('\C\V\_.'.(mode(1)=='no'? '\zs' : '').nr2char(g:prvftc),'W')
    call setpos("'x", pos==[0,0]? [0,line('.'),col('.'),0] : [0,pos[0],pos[1],0])
    return "`x"
endfun
no <expr> F MLvF(getchar())
no <expr> f MLvf(getchar())
no <expr> T MLvT(getchar())
no <expr> t MLvt(getchar())
no <expr> ; MLv{prvft}(prvftc)
no <expr> , MLv{prvft<#'Z'? tolower(prvft) : toupper(prvft)}(prvftc,1)

或者超级混乱的:
let [pvft,pvftc]=[1,32]
fun! Multift(x,c,i)
    let [g:pvftc,g:pvft]=[a:c,a:i]
    let pos=searchpos((a:x==2? mode(1)=='no'? '\C\V\_.\zs' : '\C\V\_.' : '\C\V').(a:x==1 && mode(1)=='no' || a:x==-2? nr2char(g:pvftc).'\zs' : nr2char(g:pvftc)),a:x<0? 'bW':'W')
    call setpos("'x", pos[0]? [0,pos[0],pos[1],0] : [0,line('.'),col('.'),0]) 
    return "`x"
endfun
no <expr> F Multift(-1,getchar(),-1)
no <expr> f Multift(1,getchar(),1)
no <expr> T Multift(-2,getchar(),-2)
no <expr> t Multift(2,getchar(),2)
no <expr> ; Multift(pvft,pvftc,pvft)
no <expr> , Multift(-pvft,pvftc,pvft)

5
这个问题的一种解决方法是使用 easymotion 插件。
这使您可以在整个可见文本窗口中使用 f 等运动。您触发插件,然后输入要查找的字符和 f。它会用高亮颜色(例如红色)突出显示屏幕上每个字符出现的位置,并使用字母(abcd 等)显示位置。您只需按下对应于所需跳转位置的字母即可。
该插件在 Github 上的 README 中包含一个演示动画,直观地展示了其工作原理。

3
目前最合理的做法已经在这个评论中隐藏起来。 您只需安装两个插件:vim-repeatvim-fanfingtastic。 个人建议使用Vundle 来管理 vim 插件。 这就是让fF等按预期工作的方法:
  1. Install Vundle.
  2. Add these lines to your .vimrc:

    Plugin 'tpope/vim-repeat'
    Plugin 'dahu/vim-fanfingtastic'
    
  3. Run $ vim +PluginInstall +qall in your shell.
  4. Voila!

2
您可以创建一个映射,以便移动到光标下一个字符:
:map f yl/\V<c-r>"<c-m>
将确保符号被逐字匹配。此外,您可能需要查看已为您定义的*#命令,尽管它们可能不是您想要的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接