-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Examples (vim)
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.
fzf#run()
function is the core of Vim integration. It takes a single
dictionary argument. At the very least, specify sink
option to tell what it
should do with the selected entry.
call fzf#run({'sink': 'e'})
Without source
, fzf will use find command (or $FZF_DEFAULT_COMMAND
if
defined) to list the files under the current directory. When you select one,
it will open it with :e
command. If you want to open it in a new tab, you
can pass :tabedit
command instead as the sink.
call fzf#run({'sink': 'tabedit'})
fzf allows you to select multiple entries with --multi
(or -m
) option, and
you can change its bottom-up layout with --reverse
option. Such options can
be specified as options
.
call fzf#run({'sink': 'tabedit', 'options': '--multi --reverse'})
Instead of using the default find command, you can use any shell command as the source. This will list the files managed by git.
call fzf#run({'source': 'git ls-files', 'sink': 'e'})
If you use tmux, or use Neovim, you can open fzf in a tmux pane or a split window so that it doesn't take up the entire screen.
" up / down / left / right are allowed
call fzf#run({'source': 'git ls-files', 'sink': 'e', 'right': '40%'})
source
doesn't have to be an external shell command, you can pass a Vim
array as the source. In the following example, we use the names of the open
buffers as the source.
call fzf#run({'source': map(filter(range(1, bufnr('$')), 'buflisted(v:val)'),
\ 'bufname(v:val)'),
\ 'sink': 'e', 'down': '30%'})
Or the names of color schemes.
call fzf#run({'source': map(split(globpath(&rtp, 'colors/*.vim')),
\ 'fnamemodify(v:val, ":t:r")'),
\ 'sink': 'colo', 'left': '25%'})
The following table shows the available options.
Option name | Type | Description |
---|---|---|
source |
string | External command to generate input to fzf (e.g. find . ) |
source |
list | Vim list as input to fzf |
sink |
string | Vim command to handle the selected item (e.g. e , tabe ) |
sink |
funcref | Reference to function to process each selected item |
sink* |
funcref | Similar to sink , but takes the list of output lines at once |
options |
string | Options to fzf |
dir |
string | Working directory |
up /down /left /right
|
number/string | Use tmux pane with the given size (e.g. 20 , 50% ) |
window (Neovim only) |
string | Command to open fzf window (e.g. vertical aboveleft 30new ) |
launcher |
string | External terminal emulator to start fzf with (GVim only) |
launcher |
funcref | Function for generating launcher string (GVim only) |
:FZF
command provided by default knows how to handle CTRL-T
, CTRL-X
, and
CTRL-V
and opens the selected file in a new tab, in a horizontal split, or
in a vertical split respectively. And these key bindings can be configured via
g:fzf_action
. This is implemented using --expect
option of fzf and
the smart sink function. It also understands g:fzf_layout
and
g:fzf_history_dir
. With fzf#wrap
function, you can make your command
support the options.
" Usage:
" fzf#wrap([name string,] [opts dict,] [fullscreen boolean])
" This command now supports CTRL-T, CTRL-V, and CTRL-X key bindings
" and opens fzf according to g:fzf_layout setting.
command! Buffers call fzf#run(fzf#wrap(
\ {'source': map(range(1, bufnr('$')), 'bufname(v:val)')}))
" This extends the above example to open fzf in fullscreen
" when the command is run with ! suffix (Buffers!)
command! -bang Buffers call fzf#run(fzf#wrap(
\ {'source': map(range(1, bufnr('$')), 'bufname(v:val)')}, <bang>0))
" You can optionally pass the name of the command as the first argument to
" fzf#wrap to make it work with g:fzf_history_dir
command! -bang Buffers call fzf#run(fzf#wrap('buffers',
\ {'source': map(range(1, bufnr('$')), 'bufname(v:val)')}, <bang>0))
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.
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>
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>
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>
command! FZFMru call fzf#run({
\ 'source': v:oldfiles,
\ 'sink': 'e',
\ 'options': '-m -x +s',
\ 'down': '40%'})
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
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
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()
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()
(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%'
\})
-
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 window-
CTRL-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%'
\ })
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()