ProbableOdyssey

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.

Reply to this post by email ↪