Related
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?
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.
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.
Christian Brabandt's ft_improved plugin extends the built-in f / t commands to search in following lines, too.
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
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)
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.
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.
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:
Install Vundle.
Add these lines to your .vimrc:
Plugin 'tpope/vim-repeat'
Plugin 'dahu/vim-fanfingtastic'
Run $ vim +PluginInstall +qall in your shell.
Voila!
I picked up this handy function for skipping up and down closed folds in vim:
let mapleader = ","
nnoremap <silent> <leader>zj :call NextClosedFold('j')<cr>
nnoremap <silent> <leader>zk :call NextClosedFold('k')<cr>
function! NextClosedFold(dir)
let cmd = 'norm!z' . a:dir
let view = winsaveview()
let [l0, l, open] = [0, view.lnum, 1]
while l != l0 && open
exe cmd
let [l0, l] = [l, line('.')]
let open = foldclosed(l) < 0
endwhile
if open
call winrestview(view)
endif
endfunction
As you can see, my leader key is set to ,.
So now if I issue the command ,zj my cursor is moved to the next closed fold. However, what I want is to have the zj command defaulted to moving to the next closed fold, and I want ,zj to move to the next fold (open or closed).
What is the most elegant way to write the remapping so my vim behaves the way I want it to?
It sounds like you want this.
nnoremap <silent> <leader>zj zj
nnoremap <silent> <leader>zk zk
nnoremap <silent> zj :call NextClosedFold('j')<cr>
nnoremap <silent> zk :call NextClosedFold('k')<cr>
function! NextClosedFold(dir)
let cmd = 'norm!z' . a:dir
let view = winsaveview()
let [l0, l, open] = [0, view.lnum, 1]
while l != l0 && open
exe cmd
let [l0, l] = [l, line('.')]
let open = foldclosed(l) < 0
endwhile
if open
call winrestview(view)
endif
endfunction
nnoremap makes mapping non recursive so even if you redefine what zj and zk do you can always get back their default behavior. Then we just map zj and zk to the behavior you want.
For example, I want to temporarily map to fxsj. That is, when I press q, VIM will perform fxsqj. When I press k, VIM will perform fxskj. And so on.
You can use getchar(), for example:
nnoremap <F2> :call Fun()<CR>
function! Fun()
let c = nr2char(getchar())
if c=='q' || c=='k'
exec 'normal fxs'.c.'j'
endif
endfunction
I want to push one button in Vim and fold all the code so only code up to a specific (and variable) indent level is showing. Very useful when I want to only see method names for example and not their indented routines.
The “Vim: Fold top level folds only” question has a solution to an indent level, but it requires an environment set each time you change levels.
When my cursor is at an indent level (say level 2), I want the entire file to fold to that indent level across all methods.
Is this built into Vim somewhere? Does anyone know of a good plugin that does this?
Configure folding to be defined by indentation:
:setl foldmethod=indent
and try the following command:
:let &l:foldlevel = indent('.') / &shiftwidth
To quickly access this command, create a mapping for it as follows:
:nnoremap <silent> <leader>z :let&l:fdl=indent('.')/&sw<cr>
Because foldnestmax doesn't apply when foldmethod is expr, I looked for something else when I came across your question. Here is what I came up with, which doubtless can be improved:
function! <sid>CloseFoldOpens(opens_level)
let lineno = 2
let last = line("$")
while lineno < last
if foldclosed(lineno) != -1
let lineno = foldclosedend(lineno) + 1
elseif foldlevel(lineno) > foldlevel(lineno - 1)
\ && foldlevel(lineno) == a:opens_level
execute lineno."foldclose"
let lineno = foldclosedend(lineno) + 1
else
let lineno = lineno + 1
end
endwhile
endfunction
nnoremap <silent> z1 :%foldclose<cr>
nnoremap <silent> z2 :call <sid>CloseFoldOpens(2)<cr>
nnoremap <silent> z3 :call <sid>CloseFoldOpens(3)<cr>
nnoremap <silent> z4 :call <sid>CloseFoldOpens(4)<cr>
nnoremap <silent> z5 :call <sid>CloseFoldOpens(5)<cr>
I prefer the numbered maps, but for yours based on indentation of the current line, something along these lines:
nnoremap <silent> z. :call <sid>CloseFoldOpens(foldlevel('.'))<cr>zv
No need of a plugin, it is builtin in Vim.
'foldlevel' (or shorter 'fdl') and 'foldnestmax' ('fdn') seems to be what we were looking for. You only have to set the 'foldmethod' (or shorter 'fdm') and a 'foldnestmax' (or 'fdn') in you .vimrc file:
set foldmethod=indent foldlevelstart=2 foldnestmax=2
OR the shorter version:
set fdm=indent fdls=2 fdn=2
Then you can change the fold level with direct commands: zm or zr.
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?
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.
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.
Christian Brabandt's ft_improved plugin extends the built-in f / t commands to search in following lines, too.
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
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)
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.
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.
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:
Install Vundle.
Add these lines to your .vimrc:
Plugin 'tpope/vim-repeat'
Plugin 'dahu/vim-fanfingtastic'
Run $ vim +PluginInstall +qall in your shell.
Voila!