lazy.nvim/lua/lazy/manage/runner.lua

175 lines
4.4 KiB
Lua
Raw Permalink Normal View History

2022-11-28 10:04:32 +00:00
local Config = require("lazy.core.config")
2023-10-09 10:25:42 +01:00
local Task = require("lazy.manage.task")
2022-11-29 09:29:37 +00:00
local Util = require("lazy.util")
2022-11-28 10:04:32 +00:00
---@class RunnerOpts
---@field pipeline (string|{[1]:string, [string]:any})[]
2022-11-28 10:04:32 +00:00
---@field plugins? LazyPlugin[]|fun(plugin:LazyPlugin):any?
---@field concurrency? number
2022-11-28 10:04:32 +00:00
---@alias PipelineStep {task:string, opts?:TaskOptions}
---@alias LazyRunnerTask {co:thread, status: {task?:LazyTask, waiting?:boolean}, plugin: LazyPlugin}
2022-11-20 22:04:56 +00:00
---@class Runner
2022-11-28 10:04:32 +00:00
---@field _plugins LazyPlugin[]
---@field _running LazyRunnerTask[]
---@field _pipeline PipelineStep[]
2022-11-28 10:04:32 +00:00
---@field _on_done fun()[]
---@field _opts RunnerOpts
2022-11-20 22:04:56 +00:00
local Runner = {}
2022-11-28 10:04:32 +00:00
---@param opts RunnerOpts
function Runner.new(opts)
local self = setmetatable({}, { __index = Runner })
self._opts = opts or {}
2022-11-20 22:04:56 +00:00
2022-11-28 10:04:32 +00:00
local plugins = self._opts.plugins
if type(plugins) == "function" then
self._plugins = vim.tbl_filter(plugins, Config.plugins)
else
self._plugins = plugins or Config.plugins
end
table.sort(self._plugins, function(a, b)
return a.name < b.name
end)
self._running = {}
2022-11-28 10:04:32 +00:00
self._on_done = {}
---@param step string|(TaskOptions|{[1]:string})
self._pipeline = vim.tbl_map(function(step)
return type(step) == "string" and { task = step } or { task = step[1], opts = step }
end, self._opts.pipeline)
2022-11-20 22:04:56 +00:00
return self
end
---@param entry LazyRunnerTask
function Runner:_resume(entry)
if entry.status.task and not entry.status.task:is_done() then
return true
2022-11-28 10:04:32 +00:00
end
local ok, status = coroutine.resume(entry.co)
2022-11-29 09:29:37 +00:00
if not ok then
Util.error("Could not resume a task\n" .. status)
end
entry.status = ok and status
return entry.status ~= nil
2022-11-28 10:04:32 +00:00
end
function Runner:resume(waiting)
if waiting then
for _, entry in ipairs(self._running) do
if entry.status then
if entry.status.waiting then
entry.status.waiting = false
entry.plugin._.working = true
end
end
end
end
local running = 0
for _, entry in ipairs(self._running) do
if entry.status then
if not entry.status.waiting and self:_resume(entry) then
running = running + 1
if self._opts.concurrency and running >= self._opts.concurrency then
break
end
end
end
2022-11-28 10:04:32 +00:00
end
return running > 0 or (not waiting and self:resume(true))
2022-11-20 22:04:56 +00:00
end
2022-11-28 10:04:32 +00:00
function Runner:start()
for _, plugin in pairs(self._plugins) do
local co = coroutine.create(self.run_pipeline)
local ok, err = coroutine.resume(co, self, plugin)
if ok then
table.insert(self._running, { co = co, status = {}, plugin = plugin })
2022-11-29 09:29:37 +00:00
else
Util.error("Could not start tasks for " .. plugin.name .. "\n" .. err)
end
2022-11-28 10:04:32 +00:00
end
local check = vim.uv.new_check()
2022-11-28 10:04:32 +00:00
check:start(function()
if self:resume() then
2022-11-28 10:04:32 +00:00
return
end
check:stop()
self._running = {}
2022-11-28 10:04:32 +00:00
for _, cb in ipairs(self._on_done) do
vim.schedule(cb)
end
self._on_done = {}
end)
2022-11-20 22:04:56 +00:00
end
---@async
---@param plugin LazyPlugin
function Runner:run_pipeline(plugin)
plugin._.working = true
coroutine.yield()
for _, step in ipairs(self._pipeline) do
if step.task == "wait" then
plugin._.working = false
coroutine.yield({ waiting = true })
plugin._.working = true
else
local task = self:queue(plugin, step.task, step.opts)
if task then
coroutine.yield({ task = task })
assert(task:is_done())
if task.error then
plugin._.working = false
return
end
end
end
end
plugin._.working = false
2022-11-20 22:04:56 +00:00
end
---@param plugin LazyPlugin
2022-11-29 09:29:37 +00:00
---@param task_name string
---@param opts? TaskOptions
---@return LazyTask?
2022-11-29 09:29:37 +00:00
function Runner:queue(plugin, task_name, opts)
assert(self._running)
2022-11-29 09:29:37 +00:00
local def = vim.split(task_name, ".", { plain = true })
---@type LazyTaskDef
local task_def = require("lazy.manage.task." .. def[1])[def[2]]
assert(task_def)
2022-11-29 09:29:37 +00:00
opts = opts or {}
if not (task_def.skip and task_def.skip(plugin, opts)) then
local task = Task.new(plugin, def[2], task_def.run, opts)
task:start()
return task
end
2022-11-20 22:04:56 +00:00
end
2022-11-28 10:04:32 +00:00
-- Execute the callback async when done.
-- When no callback is specified, this will wait sync
2022-11-20 22:04:56 +00:00
---@param cb? fun()
function Runner:wait(cb)
if #self._running == 0 then
if cb then
cb()
end
return self
2022-11-20 22:04:56 +00:00
end
2022-11-28 10:04:32 +00:00
if cb then
table.insert(self._on_done, cb)
else
-- sync wait
while #self._running > 0 do
vim.wait(10)
2022-11-20 22:04:56 +00:00
end
end
return self
2022-11-20 22:04:56 +00:00
end
return Runner