diff --git a/README.md b/README.md index 3c6ac955..e830c29a 100644 --- a/README.md +++ b/README.md @@ -290,9 +290,11 @@ TODO :smiley: Usage as Vim plugin ------------------- -### `:FZF` +(fzf is a command-line utility, naturally it is only accessible in terminal Vim) -If you have set up fzf as a Vim plugin, `:FZF` command will be added. +### `:FZF[!]` + +If you have set up fzf for Vim, `:FZF` command will be added. ```vim " Look for files under current directory @@ -305,9 +307,13 @@ If you have set up fzf as a Vim plugin, `:FZF` command will be added. :FZF --no-sort -m /tmp ``` -Note that environment variables `FZF_DEFAULT_COMMAND` and `FZF_DEFAULT_OPTS` +Note that the environment variables `FZF_DEFAULT_COMMAND` and `FZF_DEFAULT_OPTS` also apply here. +If you're on a tmux session, `:FZF`, will launch fzf in a new split-window whose +height can be adjusted with `g:fzf_tmux_height` (default: 15). However, the bang +version (`:FZF!`) will always start in fullscreen. + ### `fzf#run([options])` For more advanced uses, you can call `fzf#run()` function which returns the list @@ -323,6 +329,7 @@ of the selected items. | `sink` | funcref | Reference to function to process each selected item | | `options` | string | Options to fzf | | `dir` | string | Working directory | +| `tmux` | number | Use tmux split if possible with the given height | #### Examples @@ -344,13 +351,14 @@ We can also use a Vim list as the source as follows: ```vim " Choose a color scheme with fzf -call fzf#run({ +nnoremap C :call fzf#run({ \ 'source': \ map(split(globpath(&rtp, "colors/*.vim"), "\n"), \ "substitute(fnamemodify(v:val, ':t'), '\\..\\{-}$', '', '')"), \ 'sink': 'colo', -\ 'options': '+m' -\ }) +\ 'options': '+m', +\ 'tmux': 15 +\ }) ``` `sink` option can be a function reference. The following example creates a @@ -369,10 +377,11 @@ function! g:bufopen(e) execute 'buffer '. matchstr(a:e, '^[ 0-9]*') endfunction -nnoremap :call fzf#run({ +nnoremap :call fzf#run({ \ 'source': g:buflist(), \ 'sink': function('g:bufopen'), \ 'options': '+m +s', +\ 'tmux': 15 \ }) ``` diff --git a/plugin/fzf.vim b/plugin/fzf.vim index c55deb1b..ba0d0b33 100644 --- a/plugin/fzf.vim +++ b/plugin/fzf.vim @@ -21,6 +21,9 @@ " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +let s:min_tmux_height = 3 +let s:default_tmux_height = 15 + let s:cpo_save = &cpo set cpo&vim @@ -37,16 +40,23 @@ else let s:exec = 'fzf' endif +function! s:shellesc(arg) + return '"'.substitute(a:arg, '"', '\\"', 'g').'"' +endfunction + function! s:escape(path) return substitute(a:path, ' ', '\\ ', 'g') endfunction function! fzf#run(...) abort + if has('gui_running') + echohl Error + echo 'GVim is not supported' + return [] + endif let dict = exists('a:1') ? a:1 : {} - let temps = [tempname()] - let result = temps[0] + let temps = { 'result': tempname() } let optstr = get(dict, 'options', '') - let cd = has_key(dict, 'dir') if has_key(dict, 'source') let source = dict.source @@ -54,60 +64,124 @@ function! fzf#run(...) abort if type == 1 let prefix = source.'|' elseif type == 3 - let input = add(temps, tempname())[-1] - call writefile(source, input) - let prefix = 'cat '.s:escape(input).'|' + let temps.input = tempname() + call writefile(source, temps.input) + let prefix = 'cat '.s:shellesc(temps.input).'|' else throw 'Invalid source type' endif else let prefix = '' endif + let command = prefix.s:exec.' '.optstr.' > '.temps.result - try - if cd - let cwd = getcwd() - execute 'chdir '.s:escape(dict.dir) - endif - silent !clear - execute 'silent !'.prefix.s:exec.' '.optstr.' > '.result - redraw! - if v:shell_error - return [] - endif + if exists('$TMUX') && has_key(dict, 'tmux') && + \ dict.tmux > 0 && winheight(0) >= s:min_tmux_height + return s:execute_tmux(dict, command, temps) + else + return s:execute(dict, command, temps) + endif +endfunction + +function! s:pushd(dict) + if has_key(a:dict, 'dir') + let a:dict.prev_dir = getcwd() + execute 'chdir '.s:escape(a:dict.dir) + endif +endfunction + +function! s:popd(dict) + if has_key(a:dict, 'prev_dir') + execute 'chdir '.s:escape(remove(a:dict, 'prev_dir')) + endif +endfunction + +function! s:execute(dict, command, temps) + call s:pushd(a:dict) + silent !clear + execute 'silent !'.a:command + redraw! + if v:shell_error + return [] + else + return s:callback(a:dict, a:temps, 0) + endif +endfunction + +function! s:execute_tmux(dict, command, temps) + if has_key(a:dict, 'dir') + let command = 'cd '.s:escape(a:dict.dir).' && '.a:command + else + let command = a:command + endif + let height = a:dict.tmux + let s:pane = substitute( + \ system( + \ printf( + \ 'tmux split-window -l %d -P -F "#{pane_id}" %s', + \ height, s:shellesc(command))), '\n', '', 'g') + let s:dict = a:dict + let s:temps = a:temps + + augroup fzf_tmux + autocmd! + autocmd VimResized * nested call s:tmux_check() + augroup END +endfunction + +function! s:tmux_check() + let panes = split(system('tmux list-panes -a -F "#{pane_id}"'), '\n') + + if index(panes, s:pane) < 0 + augroup fzf_tmux + autocmd! + augroup END + + call s:callback(s:dict, s:temps, 1) + redraw + endif +endfunction - let lines = readfile(result) +function! s:callback(dict, temps, cd) + if !filereadable(a:temps.result) + let lines = [] + else + if a:cd | call s:pushd(a:dict) | endif - if has_key(dict, 'sink') + let lines = readfile(a:temps.result) + if has_key(a:dict, 'sink') for line in lines - if type(dict.sink) == 2 - call dict.sink(line) + if type(a:dict.sink) == 2 + call a:dict.sink(line) else - execute dict.sink.' '.s:escape(line) + execute a:dict.sink.' '.s:escape(line) endif endfor endif - return lines - finally - if cd - execute 'chdir '.s:escape(cwd) - endif - for tf in temps - silent! call delete(tf) - endfor - endtry + endif + + for tf in values(a:temps) + silent! call delete(tf) + endfor + + call s:popd(a:dict) + + return lines endfunction -function! s:cmd(...) +function! s:cmd(bang, ...) abort let args = copy(a:000) let opts = {} if len(args) > 0 && isdirectory(expand(args[-1])) let opts.dir = remove(args, -1) endif + if !a:bang + let opts.tmux = get(g:, 'fzf_tmux_height', s:default_tmux_height) + endif call fzf#run(extend({ 'sink': 'e', 'options': join(args) }, opts)) endfunction -command! -nargs=* -complete=dir FZF call s:cmd() +command! -nargs=* -complete=dir -bang FZF call s:cmd('' == '!', ) let &cpo = s:cpo_save unlet s:cpo_save