diff --git a/README.md b/README.md index 220a6cb..5de7303 100644 --- a/README.md +++ b/README.md @@ -320,10 +320,10 @@ customization. [fzf-config]: https://github.com/junegunn/fzf/wiki/Configuring-FZF-command-(vim) -#### `fzf#run([options])` +#### `fzf#run` -For more advanced uses, you can use `fzf#run()` function with the following -options. +For more advanced uses, you can use `fzf#run([options])` function with the +following options. | Option name | Type | Description | | -------------------------- | ------------- | ---------------------------------------------------------------- | @@ -342,6 +342,17 @@ options. Examples can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/Examples-(vim)). +#### `fzf#wrap` + +`fzf#wrap(name string, [opts dict, [fullscreen boolean]])` is a helper +function that decorates the options dictionary so that it understands +`g:fzf_layout`, `g:fzf_action`, and `g:fzf_history_dir` like `:FZF`. + +```vim +command! -bang MyStuff + \ call fzf#run(fzf#wrap('my-stuff', {'dir': '~/my-stuff'}, 0)) +``` + Tips ---- diff --git a/plugin/fzf.vim b/plugin/fzf.vim index 668af7d..53cb4cb 100644 --- a/plugin/fzf.vim +++ b/plugin/fzf.vim @@ -22,6 +22,7 @@ " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. let s:default_layout = { 'down': '~40%' } +let s:layout_keys = ['window', 'up', 'down', 'left', 'right'] let s:fzf_go = expand(':h:h').'/bin/fzf' let s:install = expand(':h:h').'/install' let s:installed = 0 @@ -104,6 +105,101 @@ function! s:warn(msg) echohl None endfunction +function! s:has_any(dict, keys) + for key in a:keys + if has_key(a:dict, key) + return 1 + endif + endfor + return 0 +endfunction + +function! s:open(cmd, target) + if stridx('edit', a:cmd) == 0 && fnamemodify(a:target, ':p') ==# expand('%:p') + return + endif + execute a:cmd s:escape(a:target) +endfunction + +function! s:common_sink(action, lines) abort + if len(a:lines) < 2 + return + endif + let key = remove(a:lines, 0) + let cmd = get(a:action, key, 'e') + if len(a:lines) > 1 + augroup fzf_swap + autocmd SwapExists * let v:swapchoice='o' + \| call s:warn('fzf: E325: swap file exists: '.expand('')) + augroup END + endif + try + let empty = empty(expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified + let autochdir = &autochdir + set noautochdir + for item in a:lines + if empty + execute 'e' s:escape(item) + let empty = 0 + else + call s:open(cmd, item) + endif + if exists('#BufEnter') && isdirectory(item) + doautocmd BufEnter + endif + endfor + finally + let &autochdir = autochdir + silent! autocmd! fzf_swap + endtry +endfunction + +" name string, [opts dict, [fullscreen boolean]] +function! fzf#wrap(name, ...) + if type(a:name) != type('') + throw 'invalid name type: string expected' + endif + let opts = copy(get(a:000, 0, {})) + let bang = get(a:000, 1, 0) + + " Layout: g:fzf_layout (and deprecated g:fzf_height) + if bang + for key in s:layout_keys + if has_key(opts, key) + call remove(opts, key) + endif + endfor + elseif !s:has_any(opts, s:layout_keys) + if !exists('g:fzf_layout') && exists('g:fzf_height') + let opts.down = g:fzf_height + else + let opts = extend(opts, get(g:, 'fzf_layout', s:default_layout)) + endif + endif + + " History: g:fzf_history_dir + let opts.options = get(opts, 'options', '') + if len(get(g:, 'fzf_history_dir', '')) + let dir = expand(g:fzf_history_dir) + if !isdirectory(dir) + call mkdir(dir, 'p') + endif + let opts.options = join(['--history', s:escape(dir.'/'.a:name), opts.options]) + endif + + " Action: g:fzf_action + if !s:has_any(opts, ['sink', 'sink*']) + let opts._action = get(g:, 'fzf_action', s:default_action) + let opts.options .= ' --expect='.join(keys(opts._action), ',') + function! opts.sink(lines) abort + return s:common_sink(self._action, a:lines) + endfunction + let opts['sink*'] = remove(opts, 'sink') + endif + + return opts +endfunction + function! fzf#run(...) abort try let oshell = &shell @@ -137,7 +233,7 @@ try call writefile(source, temps.input) let prefix = 'cat '.s:shellesc(temps.input).'|' else - throw 'Invalid source type' + throw 'invalid source type' endif else let prefix = '' @@ -435,60 +531,17 @@ function! s:callback(dict, lines) abort endfunction let s:default_action = { - \ 'ctrl-m': 'e', \ 'ctrl-t': 'tab split', \ 'ctrl-x': 'split', \ 'ctrl-v': 'vsplit' } -function! s:cmd_callback(lines) abort - if empty(a:lines) - return - endif - let key = remove(a:lines, 0) - let cmd = get(s:action, key, 'e') - if len(a:lines) > 1 - augroup fzf_swap - autocmd SwapExists * let v:swapchoice='o' - \| call s:warn('fzf: E325: swap file exists: '.expand('')) - augroup END - endif - try - let empty = empty(expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified - let autochdir = &autochdir - set noautochdir - for item in a:lines - if empty - execute 'e' s:escape(item) - let empty = 0 - else - execute cmd s:escape(item) - endif - if exists('#BufEnter') && isdirectory(item) - doautocmd BufEnter - endif - endfor - finally - let &autochdir = autochdir - silent! autocmd! fzf_swap - endtry -endfunction - function! s:cmd(bang, ...) abort - let s:action = get(g:, 'fzf_action', s:default_action) - let args = extend(['--expect='.join(keys(s:action), ',')], a:000) + let args = copy(a:000) let opts = {} - if len(args) > 0 && isdirectory(expand(args[-1])) + if len(args) && isdirectory(expand(args[-1])) let opts.dir = substitute(remove(args, -1), '\\\(["'']\)', '\1', 'g') endif - if !a:bang - " For backward compatibility - if !exists('g:fzf_layout') && exists('g:fzf_height') - let opts.down = g:fzf_height - else - let opts = extend(opts, get(g:, 'fzf_layout', s:default_layout)) - endif - endif - call fzf#run(extend({'options': join(args), 'sink*': function('cmd_callback')}, opts)) + call fzf#run(fzf#wrap('FZF', extend({'options': join(args)}, opts), a:bang)) endfunction command! -nargs=* -complete=dir -bang FZF call s:cmd(0, ) diff --git a/test/fzf.vader b/test/fzf.vader index 47f2bfc..bab5c16 100644 --- a/test/fzf.vader +++ b/test/fzf.vader @@ -1,5 +1,6 @@ Execute (Setup): let g:dir = fnamemodify(g:vader_file, ':p:h') + unlet! g:fzf_layout g:fzf_action g:fzf_history_dir Log 'Test directory: ' . g:dir Save &acd @@ -69,6 +70,79 @@ Execute (fzf#run with dir option and autochdir when final cwd is same as dir): " Working directory changed due to &acd AssertEqual '/', getcwd() +Execute (fzf#wrap): + AssertThrows fzf#wrap({'foo': 'bar'}) + + let opts = fzf#wrap('foobar') + Log opts + AssertEqual '~40%', opts.down + Assert opts.options =~ '--expect=' + Assert !has_key(opts, 'sink') + Assert has_key(opts, 'sink*') + + let opts = fzf#wrap('foobar', {}, 0) + Log opts + AssertEqual '~40%', opts.down + + let opts = fzf#wrap('foobar', {}, 1) + Log opts + Assert !has_key(opts, 'down') + + let opts = fzf#wrap('foobar', {'down': '50%'}) + Log opts + AssertEqual '50%', opts.down + + let opts = fzf#wrap('foobar', {'down': '50%'}, 1) + Log opts + Assert !has_key(opts, 'down') + + let opts = fzf#wrap('foobar', {'sink': 'e'}) + Log opts + AssertEqual 'e', opts.sink + Assert !has_key(opts, 'sink*') + + let opts = fzf#wrap('foobar', {'options': '--reverse'}) + Log opts + Assert opts.options =~ '--expect=' + Assert opts.options =~ '--reverse' + + let g:fzf_layout = {'window': 'enew'} + let opts = fzf#wrap('foobar') + Log opts + AssertEqual 'enew', opts.window + + let opts = fzf#wrap('foobar', {}, 1) + Log opts + Assert !has_key(opts, 'window') + + let opts = fzf#wrap('foobar', {'right': '50%'}) + Log opts + Assert !has_key(opts, 'window') + AssertEqual '50%', opts.right + + let opts = fzf#wrap('foobar', {'right': '50%'}, 1) + Log opts + Assert !has_key(opts, 'window') + Assert !has_key(opts, 'right') + + let g:fzf_action = {'a': 'tabe'} + let opts = fzf#wrap('foobar') + Log opts + Assert opts.options =~ '--expect=a' + Assert !has_key(opts, 'sink') + Assert has_key(opts, 'sink*') + + let opts = fzf#wrap('foobar', {'sink': 'e'}) + Log opts + AssertEqual 'e', opts.sink + Assert !has_key(opts, 'sink*') + + let g:fzf_history_dir = '/tmp' + let opts = fzf#wrap('foobar', {'options': '--color light'}) + Log opts + Assert opts.options =~ '--history /tmp/foobar' + Assert opts.options =~ '--color light' + Execute (Cleanup): unlet g:dir Restore