Table of Contents
- fzf.vim repository
- Basic tutorial
- locate command integration
- Open files in splits
- Choose color scheme
- Select buffer
- Simple MRU search
- Jump to tags (simple)
- Jump to tags
- Jump to tags in the current buffer
- Search lines in all open vim buffers
- Narrow ag results within vim
- Fuzzy search files in parent directory of current file
- Fuzzy search the arglist (:Args)
- Fuzzy search netrwhist (:Netrwhist)
- Fuzzy file template search
fzf.vim
repository
If you're not familiar with Vimscript or don't have time to write your own commands, check out fzf.vim project which provides a set of ready-made commands.
Basic tutorial
See https://github.com/junegunn/fzf/blob/master/README-VIM.md
locate
command integration
command! -nargs=1 -bang Locate call fzf#run(fzf#wrap(
\ {'source': 'locate <q-args>', 'options': '-m'}, <bang>0))
:Locate /
will list every file on the system. So make sure that you're using Go version of fzf which is significantly faster than the old Ruby version.
Open files in splits
Consider using CTRL-X/V/T
key bindings of the default :FZF
command instead.
" Open files in horizontal split
nnoremap <silent> <Leader>s :call fzf#run({
\ 'down': '40%',
\ 'sink': 'botright split' })<CR>
" Open files in vertical horizontal split
nnoremap <silent> <Leader>v :call fzf#run({
\ 'right': winwidth('.') / 2,
\ 'sink': 'vertical botright split' })<CR>
Choose color scheme
nnoremap <silent> <Leader>C :call fzf#run({
\ 'source':
\ map(split(globpath(&rtp, "colors/*.vim"), "\n"),
\ "substitute(fnamemodify(v:val, ':t'), '\\..\\{-}$', '', '')"),
\ 'sink': 'colo',
\ 'options': '+m',
\ 'left': 30
\ })<CR>
Select buffer
function! s:buflist()
redir => ls
silent ls
redir END
return split(ls, '\n')
endfunction
function! s:bufopen(e)
execute 'buffer' matchstr(a:e, '^[ 0-9]*')
endfunction
nnoremap <silent> <Leader><Enter> :call fzf#run({
\ 'source': reverse(<sid>buflist()),
\ 'sink': function('<sid>bufopen'),
\ 'options': '+m',
\ 'down': len(<sid>buflist()) + 2
\ })<CR>
Simple MRU search
v:oldfiles
command! FZFMru call fzf#run({
\ 'source': v:oldfiles,
\ 'sink': 'e',
\ 'options': '-m -x +s',
\ 'down': '40%'})
Filtered v:oldfiles
and open buffers
command! FZFMru call fzf#run({
\ 'source': reverse(s:all_files()),
\ 'sink': 'edit',
\ 'options': '-m -x +s',
\ 'down': '40%' })
function! s:all_files()
return extend(
\ filter(copy(v:oldfiles),
\ "v:val !~ 'fugitive:\\|NERD_tree\\|^/tmp/\\|.git/'"),
\ map(filter(range(1, bufnr('$')), 'buflisted(v:val)'), 'bufname(v:val)'))
endfunction
Jump to tags (simple)
command! -bar Tags if !empty(tagfiles()) | call fzf#run({
\ 'source': "sed '/^\\!/d;s/\t.*//' " . join(tagfiles()) . ' | uniq',
\ 'sink': 'tag',
\ }) | else | echo 'Preparing tags' | call system('ctags -R') | FZFTag | endif
Jump to tags
This version better handles same tags across different files.
function! s:tags_sink(line)
let parts = split(a:line, '\t\zs')
let excmd = matchstr(parts[2:], '^.*\ze;"\t')
execute 'silent e' parts[1][:-2]
let [magic, &magic] = [&magic, 0]
execute excmd
let &magic = magic
endfunction
function! s:tags()
if empty(tagfiles())
echohl WarningMsg
echom 'Preparing tags'
echohl None
call system('ctags -R')
endif
call fzf#run({
\ 'source': 'cat '.join(map(tagfiles(), 'fnamemodify(v:val, ":S")')).
\ '| grep -v -a ^!',
\ 'options': '+m -d "\t" --with-nth 1,4.. -n 1 --tiebreak=index',
\ 'down': '40%',
\ 'sink': function('s:tags_sink')})
endfunction
command! Tags call s:tags()
Jump to tags in the current buffer
function! s:align_lists(lists)
let maxes = {}
for list in a:lists
let i = 0
while i < len(list)
let maxes[i] = max([get(maxes, i, 0), len(list[i])])
let i += 1
endwhile
endfor
for list in a:lists
call map(list, "printf('%-'.maxes[v:key].'s', v:val)")
endfor
return a:lists
endfunction
function! s:btags_source()
let lines = map(split(system(printf(
\ 'ctags -f - --sort=no --excmd=number --language-force=%s %s',
\ &filetype, expand('%:S'))), "\n"), 'split(v:val, "\t")')
if v:shell_error
throw 'failed to extract tags'
endif
return map(s:align_lists(lines), 'join(v:val, "\t")')
endfunction
function! s:btags_sink(line)
execute split(a:line, "\t")[2]
endfunction
function! s:btags()
try
call fzf#run({
\ 'source': s:btags_source(),
\ 'options': '+m -d "\t" --with-nth 1,4.. -n 1 --tiebreak=index',
\ 'down': '40%',
\ 'sink': function('s:btags_sink')})
catch
echohl WarningMsg
echom v:exception
echohl None
endtry
endfunction
command! BTags call s:btags()
Search lines in all open vim buffers
(require set hidden
to prevent vim unload buffer)
function! s:line_handler(l)
let keys = split(a:l, ':\t')
exec 'buf' keys[0]
exec keys[1]
normal! ^zz
endfunction
function! s:buffer_lines()
let res = []
for b in filter(range(1, bufnr('$')), 'buflisted(v:val)')
call extend(res, map(getbufline(b,0,"$"), 'b . ":\t" . (v:key + 1) . ":\t" . v:val '))
endfor
return res
endfunction
command! FZFLines call fzf#run({
\ 'source': <sid>buffer_lines(),
\ 'sink': function('<sid>line_handler'),
\ 'options': '--extended --nth=3..',
\ 'down': '60%'
\})
Narrow ag results within vim
CTRL-X
,CTRL-V
,CTRL-T
to open in a new split, vertical split, tab respectively.CTRL-A
to select all matches and list them in quickfix windowCTRL-D
to deselect all
Ag
without argument will list all the lines
function! s:ag_to_qf(line)
let parts = split(a:line, ':')
return {'filename': parts[0], 'lnum': parts[1], 'col': parts[2],
\ 'text': join(parts[3:], ':')}
endfunction
function! s:ag_handler(lines)
if len(a:lines) < 2 | return | endif
let cmd = get({'ctrl-x': 'split',
\ 'ctrl-v': 'vertical split',
\ 'ctrl-t': 'tabe'}, a:lines[0], 'e')
let list = map(a:lines[1:], 's:ag_to_qf(v:val)')
let first = list[0]
execute cmd escape(first.filename, ' %#\')
execute first.lnum
execute 'normal!' first.col.'|zz'
if len(list) > 1
call setqflist(list)
copen
wincmd p
endif
endfunction
command! -nargs=* Ag call fzf#run({
\ 'source': printf('ag --nogroup --column --color "%s"',
\ escape(empty(<q-args>) ? '^(?=.)' : <q-args>, '"\')),
\ 'sink*': function('<sid>ag_handler'),
\ 'options': '--ansi --expect=ctrl-t,ctrl-v,ctrl-x --delimiter : --nth 4.. '.
\ '--multi --bind=ctrl-a:select-all,ctrl-d:deselect-all '.
\ '--color hl:68,hl+:110',
\ 'down': '50%'
\ })
Fuzzy search files in parent directory of current file
This command is very handy if you want to explore or edit the surrounding/neigbouring files of the file your currently editing. (e.g. files in the same directory)
function! s:fzf_neighbouring_files()
let current_file =expand("%")
let cwd = fnamemodify(current_file, ':p:h')
let command = 'ag -g "" -f ' . cwd . ' --depth 0'
call fzf#run({
\ 'source': command,
\ 'sink': 'e',
\ 'options': '-m -x +s',
\ 'window': 'enew' })
endfunction
command! FZFNeigh call s:fzf_neighbouring_files()
Fuzzy search the arglist (:Args
)
command! -bang Args call fzf#run(fzf#wrap('args',
\ {'source': map([argidx()]+(argidx()==0?[]:range(argc())[0:argidx()-1])+range(argc())[argidx()+1:], 'argv(v:val)')}, <bang>0))
Fuzzy search netrwhist (:Netrwhist
)
function! MyUniq(lst)
return filter(a:lst, 'count(a:lst, v:val) == 1')
endfunction
command! -bang Netrwhist call fzf#run(fzf#wrap('netrw_dirhist',
\ {'source':
\ !exists('g:netrw_dirhist_cnt')
\ ?"tail -n +3 ".g:netrw_home.".netrwhist | cut -d \"'\" -f2- | rev | cut -d \"'\" -f2- | rev | awk '!seen[$0]++'"
\ :MyUniq(map(range(1,g:netrw_dirhist_cnt), 'g:netrw_dirhist_{v:val}'))
\ }, <bang>0))
This uses the command line tools tail
, cut
, rev
, and awk
which are most likely available under macOS and Linux.
The default value of g:netrw_dirhistmax
is 10. You might be interested in increasing this value when you use this feature more often:
let g:netrw_dirhistmax = 1000
Fuzzy file template search
Say you have a folder with files in it you use for templating, and you want a fzf menu to choose which file will be loaded:
"
" choose from templates and apply to file
"
function! s:read_template_into_buffer(template)
" has to be a function to avoid the extra space fzf#run insers otherwise
execute '0r ~/.vim/templates/'.a:template
endfunction
command! -bang -nargs=* LoadTemplate call fzf#run({
\ 'source': 'ls -1 ~/.config/nvim/templates',
\ 'down': 20,
\ 'sink': function('<sid>read_template_into_buffer')
\ })