mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +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
 |