Skip to content

Commit 34467fc

Browse files
authored
Use job API for system() calls in GUI Vim to avoid overhead (#1325)
In GUI Vim (gvim, MacVim), each system() call incurs significant overhead due to command prompt window creation, making operations like PlugDiff ~5x slower than in terminal Vim or Neovim. Add s:system_job() which uses job_start with file-based output to run commands without that overhead, and use it in s:system() when running in GUI Vim with job support. Since v:shell_error is read-only in Vim 9, introduce s:shell_error which is set by both code paths and used at all call sites. Fixes #1312
1 parent d72ac87 commit 34467fc

1 file changed

Lines changed: 35 additions & 11 deletions

File tree

plug.vim

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ let s:mac_gui = has('gui_macvim') && has('gui_running')
6868
let s:is_win = has('win32')
6969
let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win)
7070
let s:vim8 = has('patch-8.0.0039') && exists('*job_start')
71+
let s:shell_error = 0
7172
if s:is_win && &shellslash
7273
set noshellslash
7374
let s:me = resolve(expand('<sfile>:p'))
@@ -170,7 +171,7 @@ function! s:git_origin_branch(spec)
170171

171172
" The command may not return the name of a branch in detached HEAD state
172173
let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir))
173-
return v:shell_error ? '' : result[-1]
174+
return s:shell_error ? '' : result[-1]
174175
endfunction
175176

176177
if s:is_win
@@ -1097,7 +1098,7 @@ function! s:checkout(spec)
10971098
let credential_helper = s:disable_credential_helper() ? '-c credential.helper= ' : ''
10981099
let output = s:system(
10991100
\ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
1100-
let error = v:shell_error
1101+
let error = s:shell_error
11011102
endif
11021103
return [output, error]
11031104
endfunction
@@ -1303,23 +1304,23 @@ function! s:update_finish()
13031304
let tag = spec.tag
13041305
if tag =~ '\*'
13051306
let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir))
1306-
if !v:shell_error && !empty(tags)
1307+
if !s:shell_error && !empty(tags)
13071308
let tag = tags[0]
13081309
call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag))
13091310
call append(3, '')
13101311
endif
13111312
endif
13121313
call s:log4(name, 'Checking out '.tag)
13131314
let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir)
1314-
let error = v:shell_error
1315+
let error = s:shell_error
13151316
endif
13161317
if !error && filereadable(spec.dir.'/.gitmodules') &&
13171318
\ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
13181319
call s:log4(name, 'Updating submodules. This may take a while.')
13191320
let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir)
13201321
let error = v:shell_error
13211322
endif
1322-
let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
1323+
let msg = s:format_message(error ? 'x': '-', name, out)
13231324
if error
13241325
call add(s:update.errors, name)
13251326
call s:regress_bar()
@@ -1480,7 +1481,7 @@ function! s:spawn(name, spec, queue, opts)
14801481
endif
14811482
else
14821483
let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [argv, a:opts.dir] : [argv]))
1483-
let job.error = v:shell_error != 0
1484+
let job.error = s:shell_error != 0
14841485
let job.running = 0
14851486
endif
14861487
endfunction
@@ -2334,6 +2335,22 @@ function! s:with_cd(cmd, dir, ...)
23342335
return printf('%s %s %s %s', cd, plug#shellescape(a:dir, {'script': script, 'shell': &shell}), sep, a:cmd)
23352336
endfunction
23362337

2338+
function! s:system_job(cmd) abort
2339+
let tmp = tempname()
2340+
let job = job_start(['/bin/sh', '-c', a:cmd], {
2341+
\ 'out_io': 'file',
2342+
\ 'out_name': tmp,
2343+
\ 'err_io': 'out',
2344+
\})
2345+
while job_status(job) ==# 'run'
2346+
sleep 1m
2347+
endwhile
2348+
let s:shell_error = job_info(job).exitval
2349+
let result = filereadable(tmp) ? join(readfile(tmp, 'b'), "\n") : ''
2350+
silent! call delete(tmp)
2351+
return result
2352+
endfunction
2353+
23372354
function! s:system(cmd, ...)
23382355
let batchfile = ''
23392356
try
@@ -2343,7 +2360,9 @@ function! s:system(cmd, ...)
23432360
" but it cannot set the working directory for the command.
23442361
" Assume that the command does not rely on the shell.
23452362
if has('nvim') && a:0 == 0
2346-
return system(a:cmd)
2363+
let ret = system(a:cmd)
2364+
let s:shell_error = v:shell_error
2365+
return ret
23472366
endif
23482367
let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})'))
23492368
if s:is_powershell(&shell)
@@ -2358,7 +2377,12 @@ function! s:system(cmd, ...)
23582377
if s:is_win && type(a:cmd) != s:TYPE.list
23592378
let [batchfile, cmd] = s:batchfile(cmd)
23602379
endif
2361-
return system(cmd)
2380+
if s:vim8 && has('gui_running') && !s:is_win
2381+
return s:system_job(cmd)
2382+
endif
2383+
let ret = system(cmd)
2384+
let s:shell_error = v:shell_error
2385+
return ret
23622386
finally
23632387
let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
23642388
if s:is_win && filereadable(batchfile)
@@ -2369,7 +2393,7 @@ endfunction
23692393

23702394
function! s:system_chomp(...)
23712395
let ret = call('s:system', a:000)
2372-
return v:shell_error ? '' : substitute(ret, '\n$', '', '')
2396+
return s:shell_error ? '' : substitute(ret, '\n$', '', '')
23732397
endfunction
23742398

23752399
function! s:git_validate(spec, check_branch)
@@ -2412,7 +2436,7 @@ function! s:git_validate(spec, check_branch)
24122436
\ 'git', 'rev-list', '--count', '--left-right',
24132437
\ printf('HEAD...origin/%s', origin_branch)
24142438
\ ], a:spec.dir)), '\t')
2415-
if v:shell_error || len(ahead_behind) != 2
2439+
if s:shell_error || len(ahead_behind) != 2
24162440
let err = "Failed to compare with the origin. The default branch might have changed.\nPlugClean required."
24172441
else
24182442
let [ahead, behind] = ahead_behind
@@ -2562,7 +2586,7 @@ function! s:upgrade()
25622586

25632587
try
25642588
let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp])
2565-
if v:shell_error
2589+
if s:shell_error
25662590
return s:err('Error upgrading vim-plug: '. out)
25672591
endif
25682592

0 commit comments

Comments
 (0)