diff --git a/README.md b/README.md index 9da2c65..16cd9ee 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,7 @@ List of commands bindings to open in a new tab, a new split, or in a new vertical split. - Bang-versions of the commands (e.g. `Ag!`) will open fzf in fullscreen -Customization -------------- +### Customization ```vim " This is the default extra key bindings @@ -59,6 +58,35 @@ let g:fzf_action = { \ 'ctrl-v': 'vsplit' } ``` +Fuzzy completion helper +----------------------- + +`fzf#complete` is a helper function for creating custom fuzzy completion using +fzf. If the first parameter is a command string or a Vim list, it will be used +as the source. + +```vim +" Replace the default dictionary completion with fzf-based fuzzy completion +inoremap fzf#complete('cat /usr/share/dict/words') +``` + +For advanced uses, you can pass an options dictionary to the function. The set +of options is almost identical to that for `fzf#run`, except that it takes +`reducer` function instead of `sink` or `sink*`. + +```vim +" Reducer function transforms the output of fzf into a single string value +function! FirstLineInUpperCase(lines) + return toupper(a:lines[0]) +endfunction + +inoremap fzf#complete({ + \ 'source': 'cat /usr/share/dict/words', + \ 'reducer': function('FirstLineInUpperCase'), + \ 'options': '--no-multi --reverse --margin 15%,0', + \ 'left': 20}) +``` + License ------- diff --git a/plugin/fzf.vim b/plugin/fzf.vim index 464a533..37e509b 100644 --- a/plugin/fzf.vim +++ b/plugin/fzf.vim @@ -49,8 +49,10 @@ function! s:buflisted() return filter(range(1, bufnr('$')), 'buflisted(v:val)') endfunction +let s:default_window = {'down': '40%'} + function! s:fzf(opts, bang) - return fzf#run(extend(a:opts, a:bang ? {} : get(g:, 'fzf_window', {'down': '40%'}))) + return fzf#run(extend(a:opts, a:bang ? {} : get(g:, 'fzf_window', s:default_window))) endfunction let s:default_action = { @@ -398,19 +400,36 @@ command! -bang Commands call s:commands(0) inoremap (-fzf-complete-trigger) :call complete_trigger() function! s:complete_trigger() - call s:fzf({ - \ 'source': s:source, - \ 'sink': function('s:complete_insert'), - \ 'options': '+m -q '.shellescape(s:query)}, 0) + let opts = copy(s:opts) + let opts.options = printf('+m -q %s %s', shellescape(s:query), get(opts, 'options', '')) + let opts['sink*'] = function('s:complete_insert') + if has_key(opts, 'reducer') + let s:reducer = opts.reducer + call remove(opts, 'reducer') + else + let s:reducer = function('s:first_line') + endif + call fzf#run(opts) endfunction -function! s:complete_insert(data) +" The default reducer +function! s:first_line(lines) + return a:lines[0] +endfunction + +function! s:complete_insert(lines) + if empty(a:lines) + return + endif + let chars = strchars(s:query) - if chars == 0 | let del = '' + if chars == 0 | let del = '' elseif chars == 1 | let del = '"_x' else | let del = (chars - 1).'"_dvh' endif - execute 'normal!' (s:eol ? '' : 'h').del.(s:eol ? 'a': 'i').a:data + + let data = call(s:reducer, [a:lines]) + execute 'normal!' (s:eol ? '' : 'h').del.(s:eol ? 'a': 'i').data if has('nvim') call feedkeys('a') else @@ -418,8 +437,16 @@ function! s:complete_insert(data) endif endfunction -function! fzf#complete(source) - let s:source = a:source +function! fzf#complete(arg) + if type(a:arg) == type({}) + if has_key(a:arg, 'sink') || has_key(a:arg, 'sink*') + echoerr 'sink not allowed' + return '' + endif + let s:opts = a:arg + else + let s:opts = extend({'source': a:arg}, get(g:, 'fzf_window', s:default_window)) + endif let eol = col('$') let ve = &ve