Koko
Koko

Reputation: 563

Using vim's f command over multiple lines

Anyone knows how to quickly find the next occurrence of a character (like the f command) but multi-line? I.e. to quickly jump to the next occurrence of some character in a file?

Upvotes: 55

Views: 18207

Answers (8)

Dan Lowe
Dan Lowe

Reputation: 56518

One approach to this problem is to use the easymotion plugin.

This lets you use motions such as f across the entire visible window of text. You trigger the plugin, then enter f and the character you are looking for. It highlights each position that the character appears on screen in a highlight color (e.g. red), and shows the position using a letter (a, b, c, d, ...). You simply press the letter corresponding to the position you wish to jump to.

The plugin's README on Github includes an animation that visually demonstrates how it works.

Upvotes: 5

Mateusz Piotrowski
Mateusz Piotrowski

Reputation: 9117

The most reasonable way of doing this at the moment is hidden in this comment.

You just have to install two plugins: vim-repeat and vim-fanfingtastic.

Personally, I use Vundle to manage my vim plugins.

So this is the way you can make f, F, etc. work as desired:

  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!

Upvotes: 4

q335r49
q335r49

Reputation: 648

Aaaa, vimscript. It takes about 2 years to write 20 lines well :D. Here is the latest, sleekest version: works for ALL modes: visual, operator pending, normal.

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)

Or the super garbled:

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)

Upvotes: 5

Ingo Karkat
Ingo Karkat

Reputation: 172520

Christian Brabandt's ft_improved plugin extends the built-in f / t commands to search in following lines, too.

Upvotes: 11

thagorn
thagorn

Reputation: 747

Having wanted to know exactly the same thing I looked through the answers here. None of are exactly what I wanted so I cobbled together a few of them.

q335's answer was the closest because it handles omaps properly which is necessary to do something like dt} (delete everything up to, but not including the next curly brace) but Curt's answer handles special character searching and uses a single function which to me is much more preferable so I'm not adding too much to my .vimrc

Here is my result:

"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

Upvotes: 7

Curt Nelson
Curt Nelson

Reputation: 3192

There's an example of redefining f to ignore case in the 'eval.txt' help file.

:h eval.txt

(search for "ignore case" a couple times)

We can modify that to do what you want. Add the following function to your ~/.vimrc file or better yet, create a plugin file: ~/.vim/plugin/find-char.vim (create the directories if you do not already have them).

function FindChar()
    let c = nr2char( getchar() )
    let match = search('\V' . c)
endfunction

Then, in your ~/.vimrc add the following line:

nmap f :call FindChar()<CR>

Now, f should work like you want it to.

BTW, this was tested with Vim 7.2 on Ubuntu 10.04.

Upvotes: 13

Kimball Robinson
Kimball Robinson

Reputation: 3387

You could make a mapping to go to the next character under the cursor:

:map f yl/\V<c-r>"<c-m>

the \V will make sure symbols are matched literally.

Also, you may want to look at the * and # commands, which are already defined for you, although they might not be what you want.

Upvotes: 2

Peter Recore
Peter Recore

Reputation: 14187

Isn't that what "/" does?

If you're looking for the next "x" then do /x while in command mode.

Then you can hit "n" to advance to the next x, and then the next x, etc.

There are lots of vim cheat sheets out there with all kinds of tips.

Upvotes: 52

Related Questions