async.vim is the module that handles asynchronous jobs for both vim (>= 8.1) and neovim.

What is a job?

In this context, with job is meant an external process that is started within vim, with different purposes (building an executable, running tests, performing a lint on the current buffer, executing a script, etc).

As you maybe will know, vim has a command (:make) that executes a system process, defined by the makeprg option, then captures its output with a format defined by the errorformat option, and fills the quickfix window with the results (possibly build errors, or of any other type). The default makeprg is make, hence the name of the command.

Vim has also the :compiler command, that sets makeprg and errorformat to predefined values, so that simple build systems can be defined this way.

The :make command is synchronous, that is, the whole editor is frozen while the external process runs.

If you ever used the :grep command, you’ll possibly know that it uses similar settings (grepprg and grepformat) to produce its results. As a matter of fact, they work very similarly, and also the :grep command is synchronous.

Starting from version 8.0 (and neovim did before it), vim supports asynchronous jobs, but all default commands still work synchronously, and implementation of asynchronous commands is left to plugins. There’s also the fact that vim and neovim implementation of jobs is different and incompatible, so plugins must handle that on their own.

This module provides both a way to start and manage jobs, and an unified interface that works in the same way for vim and neovim.

What does this module do?

This module provides:

  • a series of commands that let :make and :grep work asynchronously.

  • a job indexing system, so that you can have an overview of currently running jobs (to stop them if needed), and also finished jobs (to check their exit status).

  • functions that will start asynchronous jobs, with many possible customizations.

Out of the three, if you are a regular user you’ll be probably interested in the first one, and the second one at most. The third one requires some understanding of how vim and neovim handle jobs, so it’s not exactly for the average vim user. But it’s what the second module (tasks.vim) is for: an easy-to-use interface to define job commands and their options.

Commands

:Make commands

Command Args Bang Description
Make ? ? run :make asynchronously and populate quickfix with results
LMake ? ? same, but use the location list for the current window

Unless ! (bang) is used, you will be brought to the first error.

Any extra argument is passed as-is to the :make command.

:Compiler commands

Command Args Bang Description
Compiler 1 1 execute :compiler, then :Make
LCompiler 1 1 same, but use :LMake

Unless ! (bang) is used, you will be brought to the first error.

Differently from vim, with the :Compiler commands changes to &makeprg and &errorformat are temporary, that is, they are reset to the previous values after the command is issued.

The first argument of the command is the compiler type, any other argument will be passed to the :Make command that is run after it.

They can be used for one-shot lintings, for example on a python buffer:

:Compiler pylint %

:Grep commands

Command Args Bang Description
Grep 1 1 run :grep asynchronously and populate quickfix with results
GrepAdd 1 1 append to list instead of creating a new one
LGrep 1 1 as :Grep, but use the location list for the current window
LGrepAdd 1 1 as for :GrepAdd

Unless ! (bang) is used, you will be brought to the first match.

As for :Make, they are wrappers around :grep, that is, they expect the same kind of arguments that :grep would expect (switches, quotes, etc).

:Jobs command

The :Jobs command will list current running jobs, and prompt for a job id to terminate.

:Jobs! will show finished jobs, and prompt for a job id for which you want to see the full details (a system python installation is needed).

Other commands

Command Args Bang Description
Async 1 1 run a command asynchronously in headless mode
AsyncBuf 1 1 run a command asynchronously and print output in a buffer
AsyncCmd 1 1 echo the output of an asynchronous command to the command line

Even in headless mode, the job is managed and can be accessed with :Jobs[!].

Output and errors are not retained to spare memory (who knows how big they are), but if BANG is used, both stdout and stderr are written to log files in temporary locations. You can then get their location with the :Jobs! command (or directly from the global index). More on this where job options are discussed in detail.

Internals

The rest of the documentation for this module is of no use for the regular user, because it describes how jobs are run and indexed, and the options they use. Most options can be set with the interface offered by tasks.vim.

g:async_jobs

Running jobs are stored in a global dictionary. To support both vim and nvim, incremental ids are used: for each new job, a new id is added to the global dictionary. Parallel jobs are possible. For each entry, values are:

id the id (same as the key)
pid the process id
job the job object/handle
title the command as requested by the user, used as title
cmd the argument for the job starter
opts options for the job starter
out a list, initially empty, will be filled with the stdout
err same for stderr

This table can then be extended by user options passed to the function that starts the job.

g:async_finished_jobs

When a job terminates, it is removed from g:async_jobs and added to g:async_finished_jobs, stripped of its out and err, to make it lighter.

If it is needed to retrieve the stdout and stderr at a later stage, logging to disk should be enabled in advance, because after a job has terminated and added to this variable, they are removed from memory.

As stated before, the command :Jobs! can pretty print the selected id from this table for you.

Functions

Function: async#cmd(cmd, mode, …)

Starts an asynchronous process. The first of the two optional dictionaries contains user options, that are stored in the global dictionary, and that can be used later in custom callbacks. The second one can contain vim job options. While the user options can contain any field, the job options dictionary is restricted to supported fields (because vim doesn’t accept extra fields, contrary to nvim).

@param cmd:  the command
@param mode: quickfix, buffer, terminal, external, cmdline or headless
@param ...:  can be one or two dicts
             - first is 'useropts' (user data stored in g:async_jobs[id])
             - second is 'jobopts' (merged with [job
               options](https://vimhelp.org/channel.txt.html#job-options))
Returns: the id of the job if successful, or 0

Function: async#qfix(args, …)

Runs a job and populate quickfix with results.

@param args:  the args for the command
@param ...:   optional dicts as for async#cmd (useropts, jobopts)
Returns: the id of the job if successful, or 0

Function: async#compiler(args, …)

Sets compiler, then executes the job in quickfix mode.

@param args: the compiler name, followed by any argument
@param ...:  optional dicts as for async#cmd (useropts, jobopts)
Returns: the id of the job if successful, or 0

Function: async#stop(id, …)

Stops jobs.

@param id:   the id of the job to stop. If 0, all jobs are stopped.
@param ...:  kill rather than terminate. Vim only.
Returns: the id of the stopped job, or 0 if all jobs are stopped.

Function: async#list(finished)

Lists running or finished jobs. Prompts for id of running job to terminate/finished job to pretty print.

@param finished: list finished jobs, not running jobs.

Function: async#expand(cmd, …)

Expands filename modfiers/variables in a given string. Includes args and performs expansions for the Make/Grep commands.

@param cmd:  generally &makeprg or &grepprg
@param args: the command arguments
Returns: the full command

Function: async#remove_job(job)

Removes a job from the global dictionary, when job has exited.

@param job: the job to remove
Returns: the removed entry

Function: async#finish(exit_cb, job, status, …)

Adds finished job to the global table, and stores its exit status.

@param exit_cb: the actual exit callback
@param job:     the finished job id/object
@param status:  the exit status of the job
@param ...:     the event type (passed by nvim, unused)

User options

Most functions that can start a job accept a dictionary with user options as optional argument. This is a list of valid options with their defaults. You can see that most options have a default of 0, or an empty string.

Option Description Default
grep use grepprg, not makeprg 0
makeprg makeprg &makeprg
grepprg grepprg &grepprg
errorformat errorformat &errorformat
grepformat grepformat &grepformat
compiler run :compiler x ’’
qfautocmd quickfix autocommands read below
env environmental variables {}
locl use loclist, not qfix 0
openqf open qfix window 0
focus focus on qf window 0
nojump don’t jump to first item 0
append append to qfix, don’t add 0
nosave don’t :update before cmd !grep
wall do :wall before cmd 0
keepouts keep out/err in memory 0
writelogs write out/err to logfiles 0
outfile file where to write out ’’
errfile file where to write err ’’
noquit when quitting vim 0
noenv don’t set env variables 0

Options explained:

grep: should be set to 1 (true) when running a command like grep, rg and the like. Quickfix output and exit status are handled accordingly.

makeprg, grepprg, errorformat, grepformat: these are the settings that control the command to run, and the errorformat for the quickfix window. The grep variants are used if grep is true. Note that they only matter when calling async#qfix, because the command provided to async#cmd is executed as-is.

compiler: if not an empty string, will run the :compiler command with this value as argument, and then use the new values of makeprg and errorformat. Old values are immediately restored.

qfautocmd: a pattern for the QuickFixCmdPre/Post autocommands. Default value is grep or make, depending on the grep option. Read :help QuickFixCmdPre for more informations.

nosave, wall: by default the current buffer is updated before executing the command, nosave prevents this. Other buffers are not updated, set option wall to 1 for this. Exception: when using the grep option, nosave is true by default.

openqf, focus, nojump, append: by default, quickfix is not opened, not focused if opened, the cursor will jump to the first error/match, and lines are added, not appended.

keepouts: keep stdout and stderr in memory, stored in g:async_finished_jobs, for easier retrieval.

writelogs, outfile, errfile: if writelogs is true, logs will be written to files, whose names are obtained with tempname(), unless you provide specific paths (outfile and errfile).

noquit: vim terminates running jobs when quitting by default, setting this option prevents it and leaves them running after vim has quitted.

noenv: don’t set any environmental variable.