From 950f9c49fce8c2a2283182416a5aee0c1525e80c Mon Sep 17 00:00:00 2001 From: Tim Pope Date: Mon, 30 May 2022 11:00:04 -0400 Subject: [PATCH] Introduce helper to normalize to backslashes on win32 FugitiveVimPath() is intended to potentially convert between UNIX and Windows paths in mixed environments. Let's separate uses that require that from those that simply normalize slashes. --- autoload/fugitive.vim | 83 ++++++++++++++++++++++++++----------------- plugin/fugitive.vim | 32 ++++++++++++----- 2 files changed, 74 insertions(+), 41 deletions(-) diff --git a/autoload/fugitive.vim b/autoload/fugitive.vim index 2b59973..fe5e222 100644 --- a/autoload/fugitive.vim +++ b/autoload/fugitive.vim @@ -139,15 +139,29 @@ function! s:Mods(mods, ...) abort endfunction if exists('+shellslash') + let s:dir_commit_file = '\c^fugitive://\%(/\a\@=\)\=\(.\{-\}\)//\%(\(\x\{40,\}\|[0-3]\)\(/.*\)\=\)\=$' + function! s:Slash(path) abort return tr(a:path, '\', '/') endfunction + + function! s:VimSlash(path) abort + return tr(a:path, '\/', &shellslash ? '//' : '\\') + endfunction + else + let s:dir_commit_file = '\c^fugitive://\(.\{-\}\)//\%(\(\x\{40,\}\|[0-3]\)\(/.*\)\=\)\=$' + function! s:Slash(path) abort return a:path endfunction + + function! s:VimSlash(path) abort + return a:path + endfunction + endif function! s:AbsoluteVimPath(...) abort @@ -169,7 +183,7 @@ endfunction function! s:Resolve(path) abort let path = resolve(a:path) if has('win32') - let path = FugitiveVimPath(fnamemodify(fnamemodify(path, ':h'), ':p') . fnamemodify(path, ':t')) + let path = s:VimSlash(fnamemodify(fnamemodify(path, ':h'), ':p') . fnamemodify(path, ':t')) endif return path endfunction @@ -181,9 +195,9 @@ endfunction function! s:cpath(path, ...) abort if s:FileIgnoreCase(0) - let path = FugitiveVimPath(tolower(a:path)) + let path = s:VimSlash(tolower(a:path)) else - let path = FugitiveVimPath(a:path) + let path = s:VimSlash(a:path) endif return a:0 ? path ==# s:cpath(a:1) : path endfunction @@ -433,7 +447,7 @@ endfunction function! s:UserCommandCwd(dir) abort let tree = s:Tree(a:dir) - return len(tree) ? FugitiveVimPath(tree) : getcwd() + return len(tree) ? s:VimSlash(tree) : getcwd() endfunction function! s:UserCommandList(...) abort @@ -728,7 +742,7 @@ function! fugitive#PrepareJob(...) abort let dict.cwd = getcwd() call extend(cmd, ['--git-dir=' . FugitiveGitPath(dir)], 'keep') else - let dict.cwd = FugitiveVimPath(tree) + let dict.cwd = s:VimSlash(tree) call extend(cmd, ['-C', FugitiveGitPath(tree)], 'keep') if !s:cpath(tree . '/.git', dir) || len($GIT_DIR) call extend(cmd, ['--git-dir=' . FugitiveGitPath(dir)], 'keep') @@ -1674,7 +1688,7 @@ function! fugitive#Real(url) abort let [dir, commit, file] = s:DirCommitFile(a:url) if len(dir) let tree = s:Tree(dir) - return FugitiveVimPath((len(tree) ? tree : s:GitDir(dir)) . file) + return s:VimSlash((len(tree) ? tree : s:GitDir(dir)) . file) endif let pre = substitute(matchstr(a:url, '^\a\a\+\ze:'), '^.', '\u&', '') if len(pre) && pre !=? 'fugitive' && exists('*' . pre . 'Real') @@ -1682,7 +1696,7 @@ function! fugitive#Real(url) abort else let url = fnamemodify(a:url, ':p' . (a:url =~# '[\/]$' ? '' : ':s?[\/]$??')) endif - return FugitiveVimPath(empty(url) ? a:url : url) + return s:VimSlash(empty(url) ? a:url : url) endfunction function! fugitive#Path(url, ...) abort @@ -1742,17 +1756,17 @@ endfunction function! fugitive#Find(object, ...) abort if type(a:object) == type(0) let name = bufname(a:object) - return FugitiveVimPath(name =~# '^$\|^/\|^\a\+:' ? name : getcwd() . '/' . name) + return s:VimSlash(name =~# '^$\|^/\|^\a\+:' ? name : getcwd() . '/' . name) elseif a:object =~# '^[~$]' let prefix = matchstr(a:object, '^[~$]\i*') let owner = expand(prefix) - return FugitiveVimPath((len(owner) ? owner : prefix) . strpart(a:object, len(prefix))) + return s:VimSlash(FugitiveVimPath((len(owner) ? owner : prefix) . strpart(a:object, len(prefix)))) endif let rev = s:Slash(a:object) if rev =~# '^$\|^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '') - return FugitiveVimPath(a:object) + return s:VimSlash(a:object) elseif rev =~# '^\.\.\=\%(/\|$\)' - return FugitiveVimPath(simplify(getcwd() . '/' . a:object)) + return s:VimSlash(simplify(getcwd() . '/' . a:object)) endif let dir = call('s:GitDir', a:000) if empty(dir) @@ -1778,7 +1792,7 @@ function! fugitive#Find(object, ...) abort elseif cdir !=# fdir && ( \ f =~# '^\%(config\|hooks\|info\|logs/refs\|objects\|refs\|worktrees\)\%(/\|$\)' || \ f !~# '^\%(index$\|index\.lock$\|\w*MSG$\|\w*HEAD$\|logs/\w*HEAD$\|logs$\|rebase-\w\+\)\%(/\|$\)' && - \ getftime(FugitiveVimPath(fdir . f)) < 0 && getftime(FugitiveVimPath(cdir . f)) >= 0) + \ getftime(fdir . f) < 0 && getftime(cdir . f) >= 0) let f = simplify(cdir . f) else let f = simplify(fdir . f) @@ -1859,7 +1873,7 @@ function! fugitive#Find(object, ...) abort endif endif endif - return FugitiveVimPath(f) + return s:VimSlash(f) endfunction function! s:Generate(object, ...) abort @@ -1868,10 +1882,10 @@ function! s:Generate(object, ...) abort if !empty(f) return f elseif a:object ==# ':/' - return len(dir) ? FugitiveVimPath(s:DirUrlPrefix(dir) . '0') : '.' + return len(dir) ? s:VimSlash(s:DirUrlPrefix(dir) . '0') : '.' endif let file = matchstr(a:object, '^\%(:\d:\|[^:]*:\)\zs.*') - return fnamemodify(FugitiveVimPath(len(file) ? file : a:object), ':p') + return fnamemodify(s:VimSlash(len(file) ? file : a:object), ':p') endfunction function! s:DotRelative(path, ...) abort @@ -2126,11 +2140,11 @@ function! fugitive#simplify(url) abort if len(tree) let path = simplify(tree . file) if strpart(path . '/', 0, len(tree) + 1) !=# tree . '/' - return FugitiveVimPath(path) + return s:VimSlash(path) endif endif endif - return FugitiveVimPath('fugitive://' . simplify(dir) . '//' . commit . simplify(file)) + return s:VimSlash('fugitive://' . simplify(dir) . '//' . commit . simplify(file)) endfunction function! fugitive#resolve(url) abort @@ -2308,7 +2322,7 @@ function! fugitive#glob(url, ...) abort call filter(files, 'v:val =~# pattern') let prepend = s:DirUrlPrefix(dir) . substitute(commit, '^:', '', '') . '/' call sort(files) - call map(files, 'FugitiveVimPath(prepend . v:val . append)') + call map(files, 's:VimSlash(prepend . v:val . append)') call extend(results, files) endfor if a:0 > 1 && a:2 @@ -3274,8 +3288,8 @@ augroup END function! s:AskPassArgs(dir) abort if (len($DISPLAY) || len($TERM_PROGRAM) || has('gui_running')) && \ empty($GIT_ASKPASS) && empty($SSH_ASKPASS) && empty(fugitive#ConfigGetAll('core.askpass', a:dir)) - if s:executable(FugitiveVimPath(s:ExecPath() . '/git-gui--askpass')) - return ['-c', 'core.askPass=' . s:ExecPath() . '/git-gui--askpass'] + if s:executable(s:VimExecPath() . '/git-gui--askpass') + return ['-c', 'core.askPass=' . s:ExecPath()[0] . '/git-gui--askpass'] elseif s:executable('ssh-askpass') return ['-c', 'core.askPass=ssh-askpass'] endif @@ -3709,8 +3723,8 @@ function! fugitive#Command(line1, line2, range, bang, mods, arg, ...) abort return (empty(cmd) ? 'exe' : cmd) . after endif let alias = FugitiveConfigGet('alias.' . get(args, 0, ''), config) - if get(args, 1, '') !=# '--help' && alias !~# '^$\|^!\|[\"'']' && !filereadable(FugitiveVimPath(s:ExecPath() . '/git-' . args[0])) - \ && !(has('win32') && filereadable(FugitiveVimPath(s:ExecPath() . '/git-' . args[0] . '.exe'))) + if get(args, 1, '') !=# '--help' && alias !~# '^$\|^!\|[\"'']' && !filereadable(s:VimExecPath() . '/git-' . args[0]) + \ && !(has('win32') && filereadable(s:VimExecPath() . '/git-' . args[0] . '.exe')) call remove(args, 0) call extend(args, split(alias, '\s\+'), 'keep') endif @@ -3923,11 +3937,16 @@ let s:exec_paths = {} function! s:ExecPath() abort let git = s:GitShellCmd() if !has_key(s:exec_paths, git) - let s:exec_paths[git] = get(s:JobExecute(s:GitCmd() + ['--exec-path'], {}, [], [], {}).stdout, 0, '') + let path = get(s:JobExecute(s:GitCmd() + ['--exec-path'], {}, [], [], {}).stdout, 0, '') + let s:exec_paths[git] = [path, FugitiveVimPath(path)] endif return s:exec_paths[git] endfunction +function! s:VimExecPath() abort + return s:ExecPath()[1] +endfunction + let s:subcommands_before_2_5 = [ \ 'add', 'am', 'apply', 'archive', 'bisect', 'blame', 'branch', 'bundle', \ 'checkout', 'cherry', 'cherry-pick', 'citool', 'clean', 'clone', 'commit', 'config', @@ -3940,7 +3959,7 @@ let s:subcommands_before_2_5 = [ \ ] let s:path_subcommands = {} function! s:CompletableSubcommands(dir) abort - let c_exec_path = s:cpath(s:ExecPath()) + let c_exec_path = s:cpath(s:VimExecPath()) if !has_key(s:path_subcommands, c_exec_path) if fugitive#GitVersion(2, 18) let [lines, exec_error] = s:LinesError([a:dir, '--list-cmds=list-mainporcelain,nohelpers,list-complete']) @@ -4035,7 +4054,7 @@ function! fugitive#Cd(path, ...) abort exe s:DirCheck(dir) let path = (empty(s:Tree(dir)) ? dir : s:Tree(dir)) . '/' . path endif - return (a:0 && a:1 ? 'lcd ' : 'cd ') . s:fnameescape(FugitiveVimPath(path)) + return (a:0 && a:1 ? 'lcd ' : 'cd ') . fnameescape(s:VimSlash(path)) endfunction " Section: :Gstatus @@ -4955,13 +4974,13 @@ function! s:StageDelete(lnum1, lnum2, count) abort call s:TreeChomp('clean', '-f', '--', info.paths[0]) elseif a:count == 2 if get(b:fugitive_files['Staged'], info.filename, {'status': ''}).status ==# 'D' - call delete(FugitiveVimPath(info.paths[0])) + call delete(info.paths[0]) else call s:TreeChomp('checkout', '--ours', '--', info.paths[0]) endif elseif a:count == 3 if get(b:fugitive_files['Unstaged'], info.filename, {'status': ''}).status ==# 'D' - call delete(FugitiveVimPath(info.paths[0])) + call delete(info.paths[0]) else call s:TreeChomp('checkout', '--theirs', '--', info.paths[0]) endif @@ -4977,7 +4996,7 @@ function! s:StageDelete(lnum1, lnum2, count) abort continue endif elseif info.status ==# 'U' - call delete(FugitiveVimPath(info.paths[0])) + call delete(info.paths[0]) elseif info.status ==# 'A' call s:TreeChomp('rm', '-f', '--', info.paths[0]) elseif info.section ==# 'Unstaged' @@ -5336,7 +5355,7 @@ function! s:ToolItems(state, from, to, offsets, text, ...) abort endif let item = { \ 'valid': a:0 ? a:1 : 1, - \ 'filename': diff.filename . FugitiveVimPath(path), + \ 'filename': diff.filename . s:VimSlash(path), \ 'lnum': matchstr(get(a:offsets, i), '\d\+'), \ 'text': a:text} if len(get(diff, 'module', '')) @@ -5626,7 +5645,7 @@ function! s:GrepOptions(args, dir) abort let options = {'name_only': 0, 'name_count': 0, 'line_number': 0} let tree = s:Tree(a:dir) let prefix = empty(tree) ? fugitive#Find(':0:', a:dir) : - \ s:cpath(getcwd(), tree) ? '' : FugitiveVimPath(tree . '/') + \ s:cpath(getcwd(), tree) ? '' : s:VimSlash(tree . '/') let options.prefix = prefix for arg in a:args if arg ==# '--' @@ -5850,7 +5869,7 @@ function! s:LogParse(state, dir, prefix, line) abort call add(a:state.queue, { \ 'valid': 1, \ 'context': context, - \ 'filename': FugitiveVimPath(a:state.base . '/' . a:state.to), + \ 'filename': s:VimSlash(a:state.base . '/' . a:state.to), \ 'module': a:state.base_module . ':' . a:state.to, \ 'lnum': offsets[-1], \ 'text': a:state.message . matchstr(a:line, ' @@\+ .\+')}) @@ -6551,7 +6570,7 @@ function! fugitive#Diffsplit(autodir, keepfocus, mods, arg, ...) abort endif let spec = s:Generate(file) if spec =~# '^fugitive:' && empty(s:DirCommitFile(spec)[2]) - let spec = FugitiveVimPath(spec . s:Relative('/')) + let spec = s:VimSlash(spec . s:Relative('/')) endif exe pre let restore = s:diff_restore() diff --git a/plugin/fugitive.vim b/plugin/fugitive.vim index 7d39f8f..464d766 100644 --- a/plugin/fugitive.vim +++ b/plugin/fugitive.vim @@ -58,7 +58,7 @@ function! FugitiveReal(...) abort if type(file) ==# type({}) let dir = FugitiveGitDir(file) let tree = s:Tree(dir) - return FugitiveVimPath(empty(tree) ? dir : tree) + return s:VimSlash(empty(tree) ? dir : tree) elseif file =~# '^\a\a\+:' || a:0 > 1 return call('fugitive#Real', [file] + a:000[1:-1]) elseif file =~# '^/\|^\a:\|^$' @@ -490,28 +490,42 @@ function! FugitiveDetect(...) abort return '' endfunction -function! FugitiveVimPath(path) abort - if exists('+shellslash') && !&shellslash - return tr(a:path, '/', '\') - else - return a:path - endif -endfunction - function! FugitiveGitPath(path) abort return s:Slash(a:path) endfunction if exists('+shellslash') + let s:dir_commit_file = '\c^fugitive://\%(/\a\@=\)\=\(.\{-\}\)//\%(\(\x\{40,\}\|[0-3]\)\(/.*\)\=\)\=$' + function! s:Slash(path) abort return tr(a:path, '\', '/') endfunction + + function! s:VimSlash(path) abort + return tr(a:path, '\/', &shellslash ? '//' : '\\') + endfunction + + function FugitiveVimPath(path) abort + return tr(a:path, '\/', &shellslash ? '//' : '\\') + endfunction + else + let s:dir_commit_file = '\c^fugitive://\(.\{-\}\)//\%(\(\x\{40,\}\|[0-3]\)\(/.*\)\=\)\=$' + function! s:Slash(path) abort return a:path endfunction + + function! s:VimSlash(path) abort + return a:path + endfunction + + function! FugitiveVimPath(path) abort + return a:path + endfunction + endif function! s:ProjectionistDetect() abort