[vim] Use backslash for Windows filepaths (#896)
- Fix shellescaping issues for filepaths - Supports both forward slashes or backslashes - Paths with spaces - Use jobstart for neovim in s:execute (Windows) - https://github.com/neovim/neovim/pull/6497 - Make 2 s:fzf_shellescape functions - (Windows) Substitute \" with \\" to escape the last backslash - (Default) Regular shellescape - Support list 'options' - Add "@echo off" to the batchfile used to execute fzf
This commit is contained in:
parent
a5862d4b9c
commit
7a11a06cbd
135
plugin/fzf.vim
135
plugin/fzf.vim
@ -26,10 +26,56 @@ if exists('g:loaded_fzf')
|
|||||||
endif
|
endif
|
||||||
let g:loaded_fzf = 1
|
let g:loaded_fzf = 1
|
||||||
|
|
||||||
|
let s:is_win = has('win32') || has('win64')
|
||||||
|
if s:is_win && &shellslash
|
||||||
|
set noshellslash
|
||||||
|
let s:base_dir = expand('<sfile>:h:h')
|
||||||
|
set shellslash
|
||||||
|
else
|
||||||
|
let s:base_dir = expand('<sfile>:h:h')
|
||||||
|
endif
|
||||||
|
if s:is_win
|
||||||
|
function! s:fzf_call(fn, ...)
|
||||||
|
let shellslash = &shellslash
|
||||||
|
try
|
||||||
|
set noshellslash
|
||||||
|
return call(a:fn, a:000)
|
||||||
|
finally
|
||||||
|
let &shellslash = shellslash
|
||||||
|
endtry
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:fzf_shellescape(path)
|
||||||
|
return substitute(s:fzf_call('shellescape', a:path), '[^\\]\zs\\"$', '\\\\"', '')
|
||||||
|
endfunction
|
||||||
|
else
|
||||||
|
function! s:fzf_call(fn, ...)
|
||||||
|
return call(a:fn, a:000)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:fzf_shellescape(path)
|
||||||
|
return shellescape(a:path)
|
||||||
|
endfunction
|
||||||
|
endif
|
||||||
|
|
||||||
|
function! s:fzf_getcwd()
|
||||||
|
return s:fzf_call('getcwd')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:fzf_fnamemodify(fname, mods)
|
||||||
|
return s:fzf_call('fnamemodify', a:fname, a:mods)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:fzf_expand(fmt)
|
||||||
|
return s:fzf_call('expand', a:fmt)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:fzf_tempname()
|
||||||
|
return s:fzf_call('tempname')
|
||||||
|
endfunction
|
||||||
|
|
||||||
let s:default_layout = { 'down': '~40%' }
|
let s:default_layout = { 'down': '~40%' }
|
||||||
let s:layout_keys = ['window', 'up', 'down', 'left', 'right']
|
let s:layout_keys = ['window', 'up', 'down', 'left', 'right']
|
||||||
let s:is_win = has('win32') || has('win64')
|
|
||||||
let s:base_dir = expand('<sfile>:h:h')
|
|
||||||
let s:fzf_go = s:base_dir.'/bin/fzf'
|
let s:fzf_go = s:base_dir.'/bin/fzf'
|
||||||
let s:fzf_tmux = s:base_dir.'/bin/fzf-tmux'
|
let s:fzf_tmux = s:base_dir.'/bin/fzf-tmux'
|
||||||
let s:install = s:base_dir.'/install'
|
let s:install = s:base_dir.'/install'
|
||||||
@ -41,7 +87,7 @@ set cpo&vim
|
|||||||
function! s:fzf_exec()
|
function! s:fzf_exec()
|
||||||
if !exists('s:exec')
|
if !exists('s:exec')
|
||||||
if executable(s:fzf_go)
|
if executable(s:fzf_go)
|
||||||
let s:exec = s:fzf_go
|
let s:exec = s:fzf_expand(s:fzf_go)
|
||||||
elseif executable('fzf')
|
elseif executable('fzf')
|
||||||
let s:exec = 'fzf'
|
let s:exec = 'fzf'
|
||||||
elseif s:is_win
|
elseif s:is_win
|
||||||
@ -62,7 +108,7 @@ function! s:fzf_exec()
|
|||||||
throw 'fzf executable not found'
|
throw 'fzf executable not found'
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
return s:shellesc(s:exec)
|
return s:is_win ? s:exec : s:shellesc(s:exec)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:tmux_enabled()
|
function! s:tmux_enabled()
|
||||||
@ -133,7 +179,7 @@ function! s:has_any(dict, keys)
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:open(cmd, target)
|
function! s:open(cmd, target)
|
||||||
if stridx('edit', a:cmd) == 0 && fnamemodify(a:target, ':p') ==# expand('%:p')
|
if stridx('edit', a:cmd) == 0 && s:fzf_fnamemodify(a:target, ':p') ==# s:fzf_expand('%:p')
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
execute a:cmd s:escape(a:target)
|
execute a:cmd s:escape(a:target)
|
||||||
@ -148,11 +194,11 @@ function! s:common_sink(action, lines) abort
|
|||||||
if len(a:lines) > 1
|
if len(a:lines) > 1
|
||||||
augroup fzf_swap
|
augroup fzf_swap
|
||||||
autocmd SwapExists * let v:swapchoice='o'
|
autocmd SwapExists * let v:swapchoice='o'
|
||||||
\| call s:warn('fzf: E325: swap file exists: '.expand('<afile>'))
|
\| call s:warn('fzf: E325: swap file exists: '.s:fzf_expand('<afile>'))
|
||||||
augroup END
|
augroup END
|
||||||
endif
|
endif
|
||||||
try
|
try
|
||||||
let empty = empty(expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified
|
let empty = empty(s:fzf_expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified
|
||||||
let autochdir = &autochdir
|
let autochdir = &autochdir
|
||||||
set noautochdir
|
set noautochdir
|
||||||
for item in a:lines
|
for item in a:lines
|
||||||
@ -202,6 +248,11 @@ function! s:validate_layout(layout)
|
|||||||
return a:layout
|
return a:layout
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:evaluate_opts(options)
|
||||||
|
return type(a:options) == type([]) ?
|
||||||
|
\ join(map(copy(a:options), 's:fzf_shellescape(v:val)')) : a:options
|
||||||
|
endfunction
|
||||||
|
|
||||||
" [name string,] [opts dict,] [fullscreen boolean]
|
" [name string,] [opts dict,] [fullscreen boolean]
|
||||||
function! fzf#wrap(...)
|
function! fzf#wrap(...)
|
||||||
let args = ['', {}, 0]
|
let args = ['', {}, 0]
|
||||||
@ -238,15 +289,16 @@ function! fzf#wrap(...)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
" Colors: g:fzf_colors
|
" Colors: g:fzf_colors
|
||||||
let opts.options = s:defaults() .' '. get(opts, 'options', '')
|
let opts.options = s:defaults() .' '. s:evaluate_opts(get(opts, 'options', ''))
|
||||||
|
|
||||||
" History: g:fzf_history_dir
|
" History: g:fzf_history_dir
|
||||||
if len(name) && len(get(g:, 'fzf_history_dir', ''))
|
if len(name) && len(get(g:, 'fzf_history_dir', ''))
|
||||||
let dir = expand(g:fzf_history_dir)
|
let dir = s:fzf_expand(g:fzf_history_dir)
|
||||||
if !isdirectory(dir)
|
if !isdirectory(dir)
|
||||||
call mkdir(dir, 'p')
|
call mkdir(dir, 'p')
|
||||||
endif
|
endif
|
||||||
let opts.options = join(['--history', s:escape(dir.'/'.name), opts.options])
|
let history = s:is_win ? s:fzf_shellescape(dir.'\'.name) : s:escape(dir.'/'.name)
|
||||||
|
let opts.options = join(['--history', history, opts.options])
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" Action: g:fzf_action
|
" Action: g:fzf_action
|
||||||
@ -262,19 +314,6 @@ function! fzf#wrap(...)
|
|||||||
return opts
|
return opts
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! fzf#shellescape(path)
|
|
||||||
if s:is_win
|
|
||||||
let shellslash = &shellslash
|
|
||||||
try
|
|
||||||
set noshellslash
|
|
||||||
return shellescape(a:path)
|
|
||||||
finally
|
|
||||||
let &shellslash = shellslash
|
|
||||||
endtry
|
|
||||||
endif
|
|
||||||
return shellescape(a:path)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! fzf#run(...) abort
|
function! fzf#run(...) abort
|
||||||
try
|
try
|
||||||
let oshell = &shell
|
let oshell = &shell
|
||||||
@ -295,8 +334,8 @@ try
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
let dict = exists('a:1') ? s:upgrade(a:1) : {}
|
let dict = exists('a:1') ? s:upgrade(a:1) : {}
|
||||||
let temps = { 'result': tempname() }
|
let temps = { 'result': s:fzf_tempname() }
|
||||||
let optstr = get(dict, 'options', '')
|
let optstr = s:evaluate_opts(get(dict, 'options', ''))
|
||||||
try
|
try
|
||||||
let fzf_exec = s:fzf_exec()
|
let fzf_exec = s:fzf_exec()
|
||||||
catch
|
catch
|
||||||
@ -304,11 +343,11 @@ try
|
|||||||
endtry
|
endtry
|
||||||
|
|
||||||
if has('nvim') && !has_key(dict, 'dir')
|
if has('nvim') && !has_key(dict, 'dir')
|
||||||
let dict.dir = getcwd()
|
let dict.dir = s:fzf_getcwd()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if !has_key(dict, 'source') && !empty($FZF_DEFAULT_COMMAND)
|
if !has_key(dict, 'source') && !empty($FZF_DEFAULT_COMMAND)
|
||||||
let temps.source = tempname().(s:is_win ? '.bat' : '')
|
let temps.source = s:fzf_tempname().(s:is_win ? '.bat' : '')
|
||||||
call writefile((s:is_win ? ['@echo off'] : []) + split($FZF_DEFAULT_COMMAND, "\n"), temps.source)
|
call writefile((s:is_win ? ['@echo off'] : []) + split($FZF_DEFAULT_COMMAND, "\n"), temps.source)
|
||||||
let dict.source = (empty($SHELL) ? &shell : $SHELL) . (s:is_win ? ' /c ' : ' ') . s:shellesc(temps.source)
|
let dict.source = (empty($SHELL) ? &shell : $SHELL) . (s:is_win ? ' /c ' : ' ') . s:shellesc(temps.source)
|
||||||
endif
|
endif
|
||||||
@ -319,7 +358,7 @@ try
|
|||||||
if type == 1
|
if type == 1
|
||||||
let prefix = source.'|'
|
let prefix = source.'|'
|
||||||
elseif type == 3
|
elseif type == 3
|
||||||
let temps.input = tempname()
|
let temps.input = s:fzf_tempname()
|
||||||
call writefile(source, temps.input)
|
call writefile(source, temps.input)
|
||||||
let prefix = (s:is_win ? 'type ' : 'cat ').s:shellesc(temps.input).'|'
|
let prefix = (s:is_win ? 'type ' : 'cat ').s:shellesc(temps.input).'|'
|
||||||
else
|
else
|
||||||
@ -333,7 +372,7 @@ try
|
|||||||
let use_height = has_key(dict, 'down') &&
|
let use_height = has_key(dict, 'down') &&
|
||||||
\ !(has('nvim') || s:is_win || s:present(dict, 'up', 'left', 'right')) &&
|
\ !(has('nvim') || s:is_win || s:present(dict, 'up', 'left', 'right')) &&
|
||||||
\ executable('tput') && filereadable('/dev/tty')
|
\ executable('tput') && filereadable('/dev/tty')
|
||||||
let use_term = has('nvim')
|
let use_term = has('nvim') && !s:is_win
|
||||||
let use_tmux = (!use_height && !use_term || prefer_tmux) && s:tmux_enabled() && s:splittable(dict)
|
let use_tmux = (!use_height && !use_term || prefer_tmux) && s:tmux_enabled() && s:splittable(dict)
|
||||||
if prefer_tmux && use_tmux
|
if prefer_tmux && use_tmux
|
||||||
let use_height = 0
|
let use_height = 0
|
||||||
@ -394,13 +433,13 @@ endfunction
|
|||||||
|
|
||||||
function! s:pushd(dict)
|
function! s:pushd(dict)
|
||||||
if s:present(a:dict, 'dir')
|
if s:present(a:dict, 'dir')
|
||||||
let cwd = getcwd()
|
let cwd = s:fzf_getcwd()
|
||||||
if get(a:dict, 'prev_dir', '') ==# cwd
|
if get(a:dict, 'prev_dir', '') ==# cwd
|
||||||
return 1
|
return 1
|
||||||
endif
|
endif
|
||||||
let a:dict.prev_dir = cwd
|
let a:dict.prev_dir = cwd
|
||||||
execute 'lcd' s:escape(a:dict.dir)
|
execute 'lcd' s:escape(a:dict.dir)
|
||||||
let a:dict.dir = getcwd()
|
let a:dict.dir = s:fzf_getcwd()
|
||||||
return 1
|
return 1
|
||||||
endif
|
endif
|
||||||
return 0
|
return 0
|
||||||
@ -464,6 +503,23 @@ function! s:execute(dict, command, use_height, temps) abort
|
|||||||
else
|
else
|
||||||
let command = a:use_height ? a:command : escaped
|
let command = a:use_height ? a:command : escaped
|
||||||
endif
|
endif
|
||||||
|
if s:is_win
|
||||||
|
let batchfile = s:fzf_tempname().'.bat'
|
||||||
|
call writefile(['@echo off', command], batchfile)
|
||||||
|
let command = batchfile
|
||||||
|
if has('nvim')
|
||||||
|
let s:dict = a:dict
|
||||||
|
let s:temps = a:temps
|
||||||
|
let fzf = {}
|
||||||
|
function! fzf.on_exit(job_id, exit_status, event) dict
|
||||||
|
let lines = s:collect(s:temps)
|
||||||
|
call s:callback(s:dict, lines)
|
||||||
|
endfunction
|
||||||
|
let cmd = 'start /wait cmd /c '.command
|
||||||
|
call jobstart(cmd, fzf)
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
endif
|
||||||
if a:use_height
|
if a:use_height
|
||||||
let stdin = has_key(a:dict, 'source') ? '' : '< /dev/tty'
|
let stdin = has_key(a:dict, 'source') ? '' : '< /dev/tty'
|
||||||
call system(printf('tput cup %d > /dev/tty; tput cnorm > /dev/tty; %s %s 2> /dev/tty', &lines, command, stdin))
|
call system(printf('tput cup %d > /dev/tty; tput cnorm > /dev/tty; %s %s 2> /dev/tty', &lines, command, stdin))
|
||||||
@ -675,19 +731,24 @@ let s:default_action = {
|
|||||||
|
|
||||||
function! s:shortpath()
|
function! s:shortpath()
|
||||||
let short = pathshorten(fnamemodify(getcwd(), ':~:.'))
|
let short = pathshorten(fnamemodify(getcwd(), ':~:.'))
|
||||||
return empty(short) ? '~/' : short . (short =~ '/$' ? '' : '/')
|
let slash = (s:is_win && !&shellslash) ? '\' : '/'
|
||||||
|
return empty(short) ? '~'.slash : short . (short =~ slash.'$' ? '' : slash)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:cmd(bang, ...) abort
|
function! s:cmd(bang, ...) abort
|
||||||
let args = copy(a:000)
|
let args = copy(a:000)
|
||||||
let opts = { 'options': '--multi ' }
|
let opts = { 'options': ['--multi'] }
|
||||||
if len(args) && isdirectory(expand(args[-1]))
|
if len(args) && isdirectory(expand(args[-1]))
|
||||||
let opts.dir = substitute(substitute(remove(args, -1), '\\\(["'']\)', '\1', 'g'), '[/\\]*$', '/', '')
|
let opts.dir = substitute(substitute(remove(args, -1), '\\\(["'']\)', '\1', 'g'), '[/\\]*$', '/', '')
|
||||||
let opts.options .= ' --prompt '.fzf#shellescape(opts.dir)
|
if s:is_win && !&shellslash
|
||||||
else
|
let opts.dir = substitute(opts.dir, '/', '\\', 'g')
|
||||||
let opts.options .= ' --prompt '.fzf#shellescape(s:shortpath())
|
|
||||||
endif
|
endif
|
||||||
let opts.options .= ' '.join(args)
|
let prompt = opts.dir
|
||||||
|
else
|
||||||
|
let prompt = s:shortpath()
|
||||||
|
endif
|
||||||
|
call extend(opts.options, ['--prompt', prompt])
|
||||||
|
call extend(opts.options, args)
|
||||||
call fzf#run(fzf#wrap('FZF', opts, a:bang))
|
call fzf#run(fzf#wrap('FZF', opts, a:bang))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user