[vim] Try to make 'dir' option compatible with &autochdir

When 'dir' option is passed to fzf#run(), the current working directory
is temporarily changed to the given directory, and restored at the end.
However, this behavior is not compatible with &autochdir. This commit
introduces a heuristic to determine whether or not to restore the
previous working directory.

Related: https://github.com/junegunn/fzf.vim/issues/70
This commit is contained in:
Junegunn Choi 2016-01-12 01:15:36 +09:00
parent b18db4733c
commit 0f281ef894
2 changed files with 57 additions and 19 deletions

View File

@ -1,4 +1,4 @@
" Copyright (c) 2015 Junegunn Choi
" Copyright (c) 2016 Junegunn Choi
"
" MIT License
"
@ -139,17 +139,13 @@ try
let tmux = !has('nvim') && s:tmux_enabled() && s:splittable(dict)
let command = prefix.(tmux ? s:fzf_tmux(dict) : fzf_exec).' '.optstr.' > '.temps.result
try
if tmux
return s:execute_tmux(dict, command, temps)
elseif has('nvim')
return s:execute_term(dict, command, temps)
else
return s:execute(dict, command, temps)
endif
finally
call s:popd(dict)
endtry
if has('nvim')
return s:execute_term(dict, command, temps)
endif
let ret = tmux ? s:execute_tmux(dict, command, temps) : s:execute(dict, command, temps)
call s:popd(dict, ret)
return ret
finally
let &shell = oshell
endtry
@ -193,16 +189,28 @@ function! s:pushd(dict)
return 1
endif
let a:dict.prev_dir = cwd
execute 'chdir '.s:escape(a:dict.dir)
execute 'chdir' s:escape(a:dict.dir)
let a:dict.dir = getcwd()
return 1
endif
return 0
endfunction
function! s:popd(dict)
if has_key(a:dict, 'prev_dir') && getcwd() ==# a:dict.dir
execute 'chdir '.s:escape(remove(a:dict, 'prev_dir'))
function! s:popd(dict, lines)
" Since anything can be done in the sink function, there is no telling that
" the change of the working directory was made by &autochdir setting.
"
" We use the following heuristic to determine whether to restore CWD:
" - Always restore the current directory when &autochdir is disabled.
" FIXME This makes it impossible to change directory from inside the sink
" function when &autochdir is not used.
" - In case of an error or an interrupt, a:lines will be empty.
" And it will be an array of a single empty string when fzf was finished
" without a match. In these cases, we presume that the change of the
" directory is not expected and should be undone.
if has_key(a:dict, 'prev_dir') &&
\ (!&autochdir || (empty(a:lines) || len(a:lines) == 1 && empty(a:lines[0])))
execute 'chdir' s:escape(remove(a:dict, 'prev_dir'))
endif
endfunction
@ -314,7 +322,6 @@ endfunction
function! s:execute_term(dict, command, temps)
call s:split(a:dict)
call s:pushd(a:dict)
let fzf = { 'buf': bufnr('%'), 'dict': a:dict, 'temps': a:temps, 'name': 'FZF' }
let s:command = a:command
@ -338,7 +345,7 @@ function! s:execute_term(dict, command, temps)
call s:pushd(self.dict)
try
call s:callback(self.dict, self.temps)
let ret = s:callback(self.dict, self.temps)
if inplace && bufnr('') == self.buf
execute "normal! \<c-^>"
@ -348,11 +355,13 @@ function! s:execute_term(dict, command, temps)
endif
endif
finally
call s:popd(self.dict)
call s:popd(self.dict, ret)
endtry
endfunction
call s:pushd(a:dict)
call termopen(a:command, fzf)
call s:popd(a:dict, [])
setlocal nospell
setf fzf
startinsert

View File

@ -1,6 +1,7 @@
Execute (Setup):
let g:dir = fnamemodify(g:vader_file, ':p:h')
Log 'Test directory: ' . g:dir
Save &acd
Execute (fzf#run with dir option):
let cwd = getcwd()
@ -35,6 +36,34 @@ Execute (fzf#run with string source):
let result = sort(fzf#run({ 'source': 'echo hi', 'options': '-f i' }))
AssertEqual ['hi'], result
Execute (fzf#run with dir option and noautochdir):
set noacd
let cwd = getcwd()
call fzf#run({'source': ['/foobar'], 'sink': 'e', 'dir': '/tmp', 'options': '-1'})
" No change in working directory
AssertEqual cwd, getcwd()
Execute (Incomplete fzf#run with dir option and autochdir):
set acd
let cwd = getcwd()
call fzf#run({'source': [], 'sink': 'e', 'dir': '/tmp', 'options': '-0'})
" No change in working directory even if &acd is set
AssertEqual cwd, getcwd()
Execute (fzf#run with dir option and autochdir):
set acd
let cwd = getcwd()
call fzf#run({'source': ['/foobar'], 'sink': 'e', 'dir': '/tmp', 'options': '-1'})
" Working directory changed due to &acd
AssertEqual '/', getcwd()
Execute (fzf#run with dir option and autochdir when final cwd is same as dir):
set acd
cd /tmp
call fzf#run({'source': ['/foobar'], 'sink': 'e', 'dir': '/', 'options': '-1'})
" Working directory changed due to &acd
AssertEqual '/', getcwd()
Execute (Cleanup):
unlet g:dir
Restore