mirror of
https://github.com/neovim/neovim
synced 2025-07-16 09:11:51 +00:00
110 lines
2.5 KiB
Lua
110 lines
2.5 KiB
Lua
local M = {}
|
|
|
|
local max_timeout = 30000
|
|
|
|
--- @param thread thread
|
|
--- @param on_finish fun(err: string?, ...:any)
|
|
--- @param ... any
|
|
local function resume(thread, on_finish, ...)
|
|
--- @type {n: integer, [1]:boolean, [2]:string|function}
|
|
local ret = vim.F.pack_len(coroutine.resume(thread, ...))
|
|
local stat = ret[1]
|
|
|
|
if not stat then
|
|
-- Coroutine had error
|
|
on_finish(ret[2] --[[@as string]])
|
|
elseif coroutine.status(thread) == 'dead' then
|
|
-- Coroutine finished
|
|
on_finish(nil, unpack(ret, 2, ret.n))
|
|
else
|
|
local fn = ret[2]
|
|
--- @cast fn -string
|
|
|
|
--- @type boolean, string?
|
|
local ok, err = pcall(fn, function(...)
|
|
resume(thread, on_finish, ...)
|
|
end)
|
|
|
|
if not ok then
|
|
on_finish(err)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- @param func async fun(): ...:any
|
|
--- @param on_finish? fun(err: string?, ...:any)
|
|
function M.run(func, on_finish)
|
|
local res --- @type {n:integer, [integer]:any}?
|
|
resume(coroutine.create(func), function(err, ...)
|
|
res = vim.F.pack_len(err, ...)
|
|
if on_finish then
|
|
on_finish(err, ...)
|
|
end
|
|
end)
|
|
|
|
return {
|
|
--- @param timeout? integer
|
|
--- @return any ... return values of `func`
|
|
wait = function(_self, timeout)
|
|
vim.wait(timeout or max_timeout, function()
|
|
return res ~= nil
|
|
end)
|
|
assert(res, 'timeout')
|
|
if res[1] then
|
|
error(res[1])
|
|
end
|
|
return unpack(res, 2, res.n)
|
|
end,
|
|
}
|
|
end
|
|
|
|
--- Asynchronous blocking wait
|
|
--- @async
|
|
--- @param argc integer
|
|
--- @param fun function
|
|
--- @param ... any func arguments
|
|
--- @return any ...
|
|
function M.await(argc, fun, ...)
|
|
assert(coroutine.running(), 'Async.await() must be called from an async function')
|
|
local args = vim.F.pack_len(...) --- @type {n:integer, [integer]:any}
|
|
|
|
--- @param callback fun(...:any)
|
|
return coroutine.yield(function(callback)
|
|
args[argc] = assert(callback)
|
|
fun(unpack(args, 1, math.max(argc, args.n)))
|
|
end)
|
|
end
|
|
|
|
--- @async
|
|
--- @param max_jobs integer
|
|
--- @param funs (async fun())[]
|
|
function M.join(max_jobs, funs)
|
|
if #funs == 0 then
|
|
return
|
|
end
|
|
|
|
max_jobs = math.min(max_jobs, #funs)
|
|
|
|
--- @type (async fun())[]
|
|
local remaining = { select(max_jobs + 1, unpack(funs)) }
|
|
local to_go = #funs
|
|
|
|
M.await(1, function(on_finish)
|
|
local function run_next()
|
|
to_go = to_go - 1
|
|
if to_go == 0 then
|
|
on_finish()
|
|
elseif #remaining > 0 then
|
|
local next_fun = table.remove(remaining)
|
|
M.run(next_fun, run_next)
|
|
end
|
|
end
|
|
|
|
for i = 1, max_jobs do
|
|
M.run(funs[i], run_next)
|
|
end
|
|
end)
|
|
end
|
|
|
|
return M
|