A library for asynchronous IO in Neovim, inspired by the asyncio library in Python. The library focuses on providing both common asynchronous primitives and asynchronous APIs for Neovim's core.
- Motivation
- Installation
- Configuration
- Usage
nio.control
: Primitives for flow control in async functionsnio.lsp
: A fully typed and documented async LSP client library, generated from the LSP specification.nio.file
: Open and operate on files asynchronouslynio.process
: Run and control subprocesses asynchronouslynio.uv
: Async versions ofvim.loop
functionsnio.ui
: Async versions of vim.ui functionsnio.tests
: Async versions of plenary.nvim's test functions- Third Party Integration
- Used By
Work has been ongoing around async libraries in Neovim for years, with a lot of discussion around a Neovim core implementation. Much of the motivation behind this library can be seen in that discussion.
nvim-nio aims to provide a simple interface to Lua coroutines that doesn't feel like it gets in the way of your actual
logic. You won't even know you're using them. An example of this is error handling. With other libraries, a custom
pcall
or some other custom handling must be used to catch errors. With nvim-nio, Lua's built-in pcall
works exactly
as you'd expect.
nvim-nio is focused on providing a great developer experience. The API is well documented with examples and full type annotations, which can all be used by the Lua LSP. It's recommended to use neodev.nvim to get LSP support.
Credit to the async library in plenary.nvim and async.nvim for inspiring nvim-nio and its implementation. If Neovim core integrates an async library, nvim-nio will aim to maintain compatibility with it if possible.
Install with your favourite package manager
{ "nvim-neotest/nvim-nio" }
dein:
call dein#add("nvim-neotest/nvim-nio")
Plug 'nvim-neotest/nvim-nio'
use { "nvim-neotest/nvim-nio" }
There are no configuration options currently available.
nvim-nio is based on the concept of tasks. These tasks represent a series of asynchronous actions that run in a single context. Under the hood, each task is running on a separate lua coroutine.
Tasks are created by providing an async function to nio.run
. All async
functions must be called from a task.
local nio = require("nio")
local task = nio.run(function()
nio.sleep(10)
print("Hello world")
end)
For simple use cases tasks won't be too important but they support features such as cancelling and retrieving stack traces.
nvim-nio comes with built-in modules to help with writing async code. See :help nvim-nio
for extensive documentation.
Primitives for flow control in async functions
local event = nio.control.event()
local worker = nio.run(function()
nio.sleep(1000)
event.set()
end)
local listeners = {
nio.run(function()
event.wait()
print("First listener notified")
end),
nio.run(function()
event.wait()
print("Second listener notified")
end),
}
A fully typed and documented async LSP client library, generated from the LSP specification.
local client = nio.lsp.get_clients({ name = "lua_ls" })[1]
local err, response = client.request.textDocument_semanticTokens_full({
textDocument = { uri = vim.uri_from_bufnr(0) },
})
assert(not err, err)
for _, token in pairs(response.data) do
print(token)
end
Open and operate on files asynchronously
local file = nio.file.open("test.txt", "w+")
file.write("Hello, World!\n")
local content = file.read(nil, 0)
print(content)
Run and control subprocesses asynchronously
local first = nio.process.run({
cmd = "printf", args = { "hello" }
})
local second = nio.process.run({
cmd = "cat", stdin = first.stdout
})
local output = second.stdout.read()
print(output)
Async versions of vim.loop
functions
local file_path = "README.md"
local open_err, file_fd = nio.uv.fs_open(file_path, "r", 438)
assert(not open_err, open_err)
local stat_err, stat = nio.uv.fs_fstat(file_fd)
assert(not stat_err, stat_err)
local read_err, data = nio.uv.fs_read(file_fd, stat.size, 0)
assert(not read_err, read_err)
local close_err = nio.uv.fs_close(file_fd)
assert(not close_err, close_err)
print(data)
Async versions of vim.ui functions
local value = nio.ui.input({ prompt = "Enter something: " })
print(("You entered: %s"):format(value))
Async versions of plenary.nvim's test functions
nio.tests.it("notifies listeners", function()
local event = nio.control.event()
local notified = 0
for _ = 1, 10 do
nio.run(function()
event.wait()
notified = notified + 1
end)
end
event.set()
nio.sleep(10)
assert.equals(10, notified)
end)
It is also easy to wrap callback style functions to make them asynchronous using nio.wrap
, which allows easily
integrating third-party APIs with nvim-nio.
local nio = require("nio")
local sleep = nio.wrap(function(ms, cb)
vim.defer_fn(cb, ms)
end, 2)
nio.run(function()
sleep(10)
print("Slept for 10ms")
end)
Here are some of the plugins using nvim-nio:
Please open an issue to add any missing entries!