Asynchronous Vim
In my last post I explored using Python and nccurses
to asynchronously render
streamed content from curl
. Upon a re-think of my strategy, I wondered why I
even needed Python at all. In modern versions of Vim, I knew that asynchronous
abilities were available, but I had no idea how to use it at all. After quite a
bit of experimentation and reading the docs, this is what I learned:
Jobs
The job_start
command can execute a shell command and intercept the results
with callbacks. You can even create partial functions and use them as callbacks!
1" job_demo.vim
2function! OutCb(bufchannel, msg) abort
3 call append('$', a:msg)
4endfunction
5
6function! ExitCb(foo, job, status) abort
7 call append('$', "Status: ".a:status)
8 call append("$", a:foo)
9endfunction
10
11let cmd = ['/bin/bash', '-c', 'for i in 1 2 3; do echo $i; sleep 1; done']
12let callbacks = {'out_cb': function("OutCb"), 'exit_cb': function("ExitCb", [456])}
13let job_id = job_start(cmd, callbacks)
Write this script to as buffer and running :w job_demo.vim | so %
will append the script with
1
2
3
Status: 0
456
as the job progresses. With this working example, I was able to understand :h job
much more!
Timers
Sometimes you just need a function to call every N seconds or so.
1" timer_demo.vim
2let g:counter = 0
3
4function! MyFunc(msg, timer_id) abort
5 if g:counter > 3
6 return
7 endif
8 let g:counter += 1
9 call append("$",a:msg)
10 let n_msecs = 1000
11 call timer_start(n_msecs, function('MyFunc', [1000 * n_msecs]))
12endfunction
13
14let timer_id = timer_start(1000, function('MyFunc', ["timer go!"]))
This appends the text
timer go!
1000
2000
3000
As the timer executes. Check out :h timer
for more info.
And both these methods don’t block other actions in Vim! You can keep editing or processing text in another buffer without interruption.