I have been trying to run a job asynchronously and capture the output, but despite reading the docs many times I can't get it to work.
Ideally I want all the diff output in one go, rather than line by line, so I first tried using only an on_exit callback:
let cmd = 'diff A B'
let job = job_start(cmd, {'exit_cb': 'MyExitHandler'})
And then:
function! MyExitHandler(job, exit_status)
let data = ch_read(a:job)
echom data
endfunction
I would expect the handler to be called just about immediately but nothing happens. After 30s of waiting I get bored and move the cursor (if I'm in normal mode) / press <Escape> (if I'm in insert mode), at which point "E906: not an open channel" is echoed.
From this I deduce that the handler isn't called when the job exits, but instead some time later as and when vim checks up on the job.
So I tried using an on_stdout callback as well:
let job = job_start(cmd, {'on_stdout': 'MyOutHandler', 'exit_cb': 'MyExitHandler'})
And also:
function! MyOutHandler(channel, message)
echom a:message
endfunction
This handler gets called just about immediately, as expected, but a:message is always 'DETACH', not the diff output. And then a few seconds later the exit handler echoes its E906 error.
Please could somebody tell me what I'm doing wrong?
I'm using Vim v7.4.1770.
Many thanks in advance,
Andrew Stewart
I know this isn't the place for MacVim-specific discussion, but as it's related I thought it worth mentioning that MacVim never starts looking for messages on channels:
> Note that adding "DETACH" was removed, so you can drop this check.
[snip]
> A couple of problems have been fixed. Please give it another try and
> report back if you still see a problem.
[snip]
> > I also implemented my job_start using an out_cb handler again instead
> > of a close_cb handler. The occasional-missing-callback problem has
> > disappeared and it all worked perfectly. Thanks!
>
> That is the preferred way. Reading the output in the close callback
> might only work if there isn't much to read.
I upgraded from Vim 7.4.1795 to 7.4.1816 and found DETACH was no longer sent to the `out_cb` handler to demarcate the end of my job's output on stdout.
So I added a `close_cb` handler which does what my `out_cb` handler used to do on receiving DETACH: it gathers together all the preceding job output and then processes it.
When I edit a file my job runs. When I trigger a second run, Vim always crashes with a segfault:
Vim: Caught deadly signal SEGV
Vim: Finished.
Segmentation fault: 11
Here's my code:
let job = job_start([&shell, &shellcmdflag, a:cmd], {
\ 'out_cb': 'OutHandler',
\ 'close_cb': 'CloseHandler'
\ })
" Channel is in NL mode.
function! OutHandler(channel, line)
let channel_id = matchstr(a:channel, '\d\+')
call s:accumulate_job_output(channel_id, a:line)
endfunction
function! CloseHandler(channel)
let channel_id = matchstr(a:channel, '\d\+')
call ProcessOutput(s:job_output(channel_id))
call s:job_finished(channel_id)
" redraw!
endfunction
function! s:job_started(id)
let s:jobs[a:id] = 1
endfunction
function! s:is_job_started(id)
return has_key(s:jobs, a:id)
endfunction
function! s:accumulate_job_output(id, line)
if has_key(s:jobs, a:id)
let s:jobs[a:id] = add(s:jobs[a:id], a:line)
else
let s:jobs[a:id] = [a:line]
endif
endfunction
" Returns a string
function! s:job_output(id)
if has_key(s:jobs, a:id)
return join(s:jobs[a:id], "\n")."\n"
else
return ""
endif
endfunction
function! s:job_finished(id)
if has_key(s:jobs, a:id)
unlet s:jobs[a:id]
endif
endfunction
Regarding redrawing: adding a redraw! as the last line of my close handler doesn't redraw the screen. Should I be doing this somewhere else?
I've just realised that every time a job runs, a new process is spawned as a child of vim's process, and they're all in the zombie state. They only disappear once I quit Vim or it crashes.
In other words does Vimscript code with callbacks have to be thread-safe?