Last active
November 30, 2020 11:45
-
-
Save airblade/4585556f713f0f9cc537d14054056a3b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let s:async_sync_id = 0 | |
let s:async_sync_outputs = {} | |
function! s:next_async_sync_id() | |
let async_sync_id = s:async_sync_id | |
let s:async_sync_id += 1 | |
return async_sync_id | |
endfunction | |
function! s:async_sync_output(async_sync_id, output) | |
if type(a:output) == v:t_list | |
" Ensure empty list becomes an empty string. | |
let output = join(a:output, "\n") | |
else | |
let output = a:output | |
endif | |
let s:async_sync_outputs[a:async_sync_id] = output " job can now be garbage collected | |
endfunction | |
" Executes `cmd` asynchronously but looks synchronous to the caller. | |
function! Async_sync(cmd) | |
let async_sync_id = s:next_async_sync_id() | |
let s:async_sync_outputs[async_sync_id] = Async_execute(a:cmd, function('s:async_sync_output', [async_sync_id])) | |
while type(s:async_sync_outputs[async_sync_id]) == v:t_job | |
call job_status(s:async_sync_outputs[async_sync_id]) | |
sleep 10m | |
endwhile | |
let output = s:async_sync_outputs[async_sync_id] | |
unlet s:async_sync_outputs[async_sync_id] | |
return output | |
endfunction | |
" Optional argument is data (JSON) to pass to cmd's stdin. | |
function! Async_execute(cmd, handler, ...) | |
let options = { | |
\ 'stdoutbuffer': [], | |
\ 'handler': a:handler, | |
\ } | |
let command = s:build_command(a:cmd) | |
if has('nvim') | |
let jobid = jobstart(command, extend(options, { | |
\ 'on_stdout': function('s:on_stdout_nvim'), | |
\ 'on_exit': function('s:on_exit_nvim') | |
\ })) | |
if a:0 | |
call chansend(jobid, a:1) | |
call chanclose(jobid, 'stdin') | |
endif | |
else | |
let job = job_start(command, { | |
\ 'out_cb': function('s:on_stdout_vim', options), | |
\ 'close_cb': function('s:on_close_vim', options) | |
\ }) | |
if a:0 | |
let channel = job_getchannel(job) | |
call ch_sendraw(channel, a:1) | |
call ch_close_in(channel) | |
endif | |
return job | |
endif | |
endfunction | |
function! s:build_command(cmd) | |
if has('nvim') | |
if has('unix') | |
return ['sh', '-c', a:cmd] | |
elseif has('win64') || has('win32') | |
return ['cmd.exe', '/c', a:cmd] | |
else | |
throw 'unknown os' | |
endif | |
else | |
if has('unix') | |
return ['sh', '-c', a:cmd] | |
elseif has('win64') || has('win32') | |
return 'cmd.exe /c '.a:cmd | |
else | |
throw 'unknown os' | |
endif | |
endif | |
endfunction | |
function! s:on_stdout_vim(_channel, data) dict | |
" a:data - an output line | |
call add(self.stdoutbuffer, a:data) | |
endfunction | |
function! s:on_close_vim(channel) dict | |
call self.handler(self.stdoutbuffer) | |
endfunction | |
function! s:on_stdout_nvim(_job_id, data, event) dict | |
if empty(self.stdoutbuffer) | |
let self.stdoutbuffer = a:data | |
else | |
let self.stdoutbuffer = self.stdoutbuffer[:-2] + | |
\ [self.stdoutbuffer[-1] . a:data[0]] + | |
\ a:data[1:] | |
endif | |
endfunction | |
function! s:on_exit_nvim(_job_id, _data, _event) dict | |
call map(self.stdoutbuffer, 'substitute(v:val, "\r$", "", "")') | |
call self.handler(self.stdoutbuffer) | |
endfunction |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The problem is that, under certain circumstances, a job's
close_cb
isn't called until I press ctrl-c. (This callback is the one that gathers the data received so that it can be returned to the original caller.) When I use the channel log, I seechannel_select_check(): Read EOF from ch_part[2], closing
looking for messages on channels
Invoking channel callback 61_on_stdout_vim [many times]
Job ended
looking for messages on channels
ui_delay(10)
channel_select_check(): Read EOF from ch_part[1], closing
looking for messages on channels
ui_delay(10)
looking for messages on channels
ui_delay(10)
looking for messages on channels
ui_delay(10)
...
Then I press ctrl-c:
Closing channel because all readable fds are closed
Closing channel
Invoking callbacks and flushing buffers before clsoing
Invoking callback 61_on_close_vim
At which point things go back to normal. (Although I never see messages like "Job ended", "Freeing job", "Closing channel", "Clearing channel", "Freeing channel" which I do normally.)
Maybe there's a race condition which I can't see?