Account for escaping differences when expanding on win32

The core issue here is that on UNIX, fnameescape() always doubles
backslashes, while on Windows, it leaves them be.  But it's a lot more
complicated than that, because Vim also avoids escaping other
characters, to minimize ambiguity.  This can mostly be compensated for
by removing the corresponding characters from s:fnameescape, including
backslash itself.  I'm a little worried that removing backslash will
have other implications, so keep an eye out for regressions here.

One character we can't remove is "!", because we need a way to escape
our own "the commit owning the current file" syntax.  As a long term
strategy to address this, I'm introducing new !% and !# variations to
replace it.  This is a bit on the ugly side, so I'm going to hold off on
committing to it as a documented interface until I've had more time to
muse on alternatives.

See also 6356bbc4a7, wherein I fixed a bug
with the exact same description.  This suggests a change in Vim prompted
this new variation, but then again, there's no proof that I actually
tested my fix on Windows.

Resolves: https://github.com/tpope/vim-fugitive/issues/2071
This commit is contained in:
Tim Pope
2022-10-31 22:17:14 -04:00
parent 09908d82ef
commit 01f3e0af92

View File

@@ -470,7 +470,7 @@ function! s:GitCmd() abort
let string = strpart(string, len(arg)) let string = strpart(string, len(arg))
let arg = substitute(arg, '^\s\+', '', '') let arg = substitute(arg, '^\s\+', '', '')
let arg = substitute(arg, let arg = substitute(arg,
\ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand, \ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|' . s:commit_expand . '\)\|' . s:expand,
\ '\=submatch(0)[0] ==# "\\" ? submatch(0)[1] : submatch(0)[1:-2]', 'g') \ '\=submatch(0)[0] ==# "\\" ? submatch(0)[1] : submatch(0)[1:-2]', 'g')
call add(list, arg) call add(list, arg)
endwhile endwhile
@@ -1958,6 +1958,7 @@ endfunction
let s:var = '\%(<\%(cword\|cWORD\|cexpr\|cfile\|sfile\|slnum\|afile\|abuf\|amatch' . (has('clientserver') ? '\|client' : '') . '\)>\|%\|#<\=\d\+\|##\=\)' let s:var = '\%(<\%(cword\|cWORD\|cexpr\|cfile\|sfile\|slnum\|afile\|abuf\|amatch' . (has('clientserver') ? '\|client' : '') . '\)>\|%\|#<\=\d\+\|##\=\)'
let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)' let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)'
let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)' let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)'
let s:commit_expand = '!\\\@!#\=\d*\|!%'
function! s:BufName(var) abort function! s:BufName(var) abort
if a:var ==# '%' if a:var ==# '%'
@@ -1978,8 +1979,8 @@ function! s:ExpandVar(other, var, flags, esc, ...) abort
return substitute(a:other[1:-2], "''", "'", "g") return substitute(a:other[1:-2], "''", "'", "g")
elseif a:other =~# '^"' elseif a:other =~# '^"'
return substitute(a:other[1:-2], '""', '"', "g") return substitute(a:other[1:-2], '""', '"', "g")
elseif a:other =~# '^!' elseif a:other =~# '^[!`]'
let buffer = s:BufName(len(a:other) > 1 ? '#'. a:other[1:-1] : '%') let buffer = s:BufName(a:other =~# '[0-9#]' ? '#' . matchstr(a:other, '\d\+') : '%')
let owner = s:Owner(buffer) let owner = s:Owner(buffer)
return len(owner) ? owner : '@' return len(owner) ? owner : '@'
elseif a:other =~# '^\~[~.]$' elseif a:other =~# '^\~[~.]$'
@@ -2033,7 +2034,11 @@ function! s:ExpandVar(other, var, flags, esc, ...) abort
return join(files, "\1") return join(files, "\1")
endfunction endfunction
if has('win32')
let s:fnameescape = " \t\n*?`%#'\"|!<"
else
let s:fnameescape = " \t\n*?[{`$\\%#'\"|!<" let s:fnameescape = " \t\n*?[{`$\\%#'\"|!<"
endif
function! s:Expand(rev, ...) abort function! s:Expand(rev, ...) abort
if a:rev =~# '^>' && s:Slash(@%) =~# '^fugitive://' && empty(s:DirCommitFile(@%)[1]) if a:rev =~# '^>' && s:Slash(@%) =~# '^fugitive://' && empty(s:DirCommitFile(@%)[1])
@@ -2060,13 +2065,13 @@ function! s:Expand(rev, ...) abort
let file = a:rev let file = a:rev
endif endif
return substitute(file, return substitute(file,
\ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\|^\~[~.]\)\|' . s:expand, \ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|' . s:commit_expand . '\|^\~[~.]\)\|' . s:expand,
\ '\=tr(s:ExpandVar(submatch(1),submatch(2),submatch(3),"", a:0 ? a:1 : getcwd()), "\1", " ")', 'g') \ '\=tr(s:ExpandVar(submatch(1),submatch(2),submatch(3),"", a:0 ? a:1 : getcwd()), "\1", " ")', 'g')
endfunction endfunction
function! fugitive#Expand(object) abort function! fugitive#Expand(object) abort
return substitute(a:object, return substitute(a:object,
\ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\|^\~[~.]\)\|' . s:expand, \ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|' . s:commit_expand . '\|^\~[~.]\)\|' . s:expand,
\ '\=tr(s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5)), "\1", " ")', 'g') \ '\=tr(s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5)), "\1", " ")', 'g')
endfunction endfunction
@@ -2087,7 +2092,7 @@ function! s:SplitExpandChain(string, ...) abort
\ '\=s:DotRelative(s:Slash(simplify(getcwd() . "/" . submatch(0))), cwd)', '') \ '\=s:DotRelative(s:Slash(simplify(getcwd() . "/" . submatch(0))), cwd)', '')
endif endif
let arg = substitute(arg, let arg = substitute(arg,
\ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\|^\~[~]\|^\~\w*\|\$\w\+\)\|' . s:expand, \ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|' . s:commit_expand . '\|^\~[~]\|^\~\w*\|\$\w\+\)\|' . s:expand,
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5), cwd)', 'g') \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5), cwd)', 'g')
call extend(list, split(arg, "\1", 1)) call extend(list, split(arg, "\1", 1))
if arg ==# '--' if arg ==# '--'