s:system supports list type for command

Objective is to reduce batchfiles on Windows.
List type gives more flexibility on s:system()
on how to pass the shell command to the builtin system().
If system() supports list type for command
and there is no working directory, run it directly on system().
Targets Neovim only.
Else, convert the list to an escaped command
so that the user's shell can execute it.
This commit is contained in:
Jan Edmund Lazo 2020-04-04 19:08:54 -04:00
parent c3b6b7c297
commit eb00506f36
No known key found for this signature in database
GPG Key ID: 64915E6E9F735B15

View File

@ -372,7 +372,7 @@ endfunction
function! s:git_version_requirement(...) function! s:git_version_requirement(...)
if !exists('s:git_version') if !exists('s:git_version')
let s:git_version = map(split(split(s:system('git --version'))[2], '\.'), 'str2nr(v:val)') let s:git_version = map(split(split(s:system(['git', '--version']))[2], '\.'), 'str2nr(v:val)')
endif endif
return s:version_requirement(s:git_version, a:000) return s:version_requirement(s:git_version, a:000)
endfunction endfunction
@ -898,7 +898,7 @@ function! s:regress_bar()
endfunction endfunction
function! s:is_updated(dir) function! s:is_updated(dir)
return !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', a:dir)) return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir))
endfunction endfunction
function! s:do(pull, force, todo) function! s:do(pull, force, todo)
@ -961,7 +961,7 @@ endfunction
function! s:checkout(spec) function! s:checkout(spec)
let sha = a:spec.commit let sha = a:spec.commit
let output = s:system('git rev-parse HEAD', a:spec.dir) let output = s:system(['git', 'rev-parse', 'HEAD'], a:spec.dir)
if !v:shell_error && !s:hash_match(sha, s:lines(output)[0]) if !v:shell_error && !s:hash_match(sha, s:lines(output)[0])
let output = s:system( let output = s:system(
\ 'git fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) \ 'git fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
@ -2055,6 +2055,19 @@ function! s:shellesc_sh(arg)
return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'"
endfunction endfunction
" Escape the shell argument based on the shell.
" Vim and Neovim's shellescape() are insufficient.
" 1. shellslash determines whether to use single/double quotes.
" Double-quote escaping is fragile for cmd.exe.
" 2. It does not work for powershell.
" 3. It does not work for *sh shells if the command is executed
" via cmd.exe (ie. cmd.exe /c sh -c command command_args)
" 4. It does not support batchfile syntax.
"
" Accepts an optional dictionary with the following keys:
" - shell: same as Vim/Neovim 'shell' option.
" If unset, fallback to 'cmd.exe' on Windows or 'sh'.
" - script: If truthy and shell is cmd.exe, escape for batchfile syntax.
function! plug#shellescape(arg, ...) function! plug#shellescape(arg, ...)
let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {}
let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh')
@ -2105,8 +2118,24 @@ function! s:system(cmd, ...)
let batchfile = '' let batchfile = ''
try try
let [sh, shellcmdflag, shrd] = s:chsh(1) let [sh, shellcmdflag, shrd] = s:chsh(1)
let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd if type(a:cmd) == s:TYPE.list
if s:is_win " Neovim's system() supports list argument to bypass the shell
" but it cannot set the working directory for the command.
" Assume that the command does not rely on the shell.
if has('nvim') && a:0 == 0
return system(a:cmd)
endif
let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})'))
if &shell =~# 'powershell\.exe'
let cmd = '& ' . cmd
endif
else
let cmd = a:cmd
endif
if a:0 > 0
let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list)
endif
if s:is_win && type(a:cmd) != s:TYPE.list
let [batchfile, cmd] = s:batchfile(cmd) let [batchfile, cmd] = s:batchfile(cmd)
endif endif
return system(cmd) return system(cmd)
@ -2159,9 +2188,10 @@ function! s:git_validate(spec, check_branch)
\ branch, a:spec.branch) \ branch, a:spec.branch)
endif endif
if empty(err) if empty(err)
let [ahead, behind] = split(s:lastline(s:system(printf( let [ahead, behind] = split(s:lastline(s:system([
\ 'git rev-list --count --left-right HEAD...origin/%s', \ 'git', 'rev-list', '--count', '--left-right',
\ a:spec.branch), a:spec.dir)), '\t') \ printf('HEAD...origin/%s', a:spec.branch)
\ ], a:spec.dir)), '\t')
if !v:shell_error && ahead if !v:shell_error && ahead
if behind if behind
" Only mention PlugClean if diverged, otherwise it's likely to be " Only mention PlugClean if diverged, otherwise it's likely to be
@ -2185,7 +2215,9 @@ endfunction
function! s:rm_rf(dir) function! s:rm_rf(dir)
if isdirectory(a:dir) if isdirectory(a:dir)
call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . plug#shellescape(a:dir)) call s:system(s:is_win
\ ? 'rmdir /S /Q '.plug#shellescape(a:dir)
\ : ['rm', '-rf', a:dir])
endif endif
endfunction endfunction
@ -2294,7 +2326,7 @@ function! s:upgrade()
let new = tmp . '/plug.vim' let new = tmp . '/plug.vim'
try try
let out = s:system(printf('git clone --depth 1 %s %s', plug#shellescape(s:plug_src), plug#shellescape(tmp))) let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp])
if v:shell_error if v:shell_error
return s:err('Error upgrading vim-plug: '. out) return s:err('Error upgrading vim-plug: '. out)
endif endif
@ -2489,11 +2521,13 @@ function! s:diff()
call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:') call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
for [k, v] in plugs for [k, v] in plugs
let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..' let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..'
let cmd = 'git log --graph --color=never ' let cmd = ['git', 'log', '--graph', '--color=never']
\ . (s:git_version_requirement(2, 10, 0) ? '--no-show-signature ' : '') if s:git_version_requirement(2, 10, 0)
\ . join(map(['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range], 'plug#shellescape(v:val)')) call add(cmd, '--no-show-signature')
endif
call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range])
if has_key(v, 'rtp') if has_key(v, 'rtp')
let cmd .= ' -- '.plug#shellescape(v.rtp) call extend(cmd, ['--', v.rtp])
endif endif
let diff = s:system_chomp(cmd, v.dir) let diff = s:system_chomp(cmd, v.dir)
if !empty(diff) if !empty(diff)
@ -2561,7 +2595,7 @@ function! s:snapshot(force, ...) abort
let names = sort(keys(filter(copy(g:plugs), let names = sort(keys(filter(copy(g:plugs),
\'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')))
for name in reverse(names) for name in reverse(names)
let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir) let sha = s:system_chomp(['git', 'rev-parse', '--short', 'HEAD'], g:plugs[name].dir)
if !empty(sha) if !empty(sha)
call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
redraw redraw