2024-06-26 16:06:56 +01:00
|
|
|
local Async = require("lazy.async")
|
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-28 10:04:32 +00:00
|
|
|
|
|
|
|
---@class RunnerOpts
|
2022-11-28 21:03:44 +00:00
|
|
|
---@field pipeline (string|{[1]:string, [string]:any})[]
|
2022-11-28 10:04:32 +00:00
|
|
|
---@field plugins? LazyPlugin[]|fun(plugin:LazyPlugin):any?
|
2022-11-30 22:14:31 +00:00
|
|
|
---@field concurrency? number
|
2022-11-28 10:04:32 +00:00
|
|
|
|
2024-06-26 16:06:56 +01:00
|
|
|
---@class RunnerTask
|
|
|
|
---@field task? LazyTask
|
|
|
|
---@field step number
|
|
|
|
|
|
|
|
---@alias PipelineStep {task:string, opts?:TaskOptions }
|
2022-11-28 21:03:44 +00:00
|
|
|
|
2022-11-20 22:04:56 +00:00
|
|
|
---@class Runner
|
2024-06-24 18:55:09 +01:00
|
|
|
---@field _plugins table<string,LazyPlugin>
|
2022-11-28 21:03:44 +00:00
|
|
|
---@field _pipeline PipelineStep[]
|
2022-11-28 10:04:32 +00:00
|
|
|
---@field _on_done fun()[]
|
|
|
|
---@field _opts RunnerOpts
|
2024-06-26 16:06:56 +01:00
|
|
|
---@field _running? Async
|
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
|
2024-06-24 18:55:09 +01:00
|
|
|
---@type LazyPlugin[]
|
|
|
|
local pp = {}
|
|
|
|
if type(plugins) == "function" then
|
|
|
|
pp = vim.tbl_filter(plugins, Config.plugins)
|
|
|
|
else
|
|
|
|
pp = plugins or Config.plugins
|
|
|
|
end
|
|
|
|
self._plugins = {}
|
|
|
|
for _, plugin in ipairs(pp) do
|
|
|
|
self._plugins[plugin.name] = plugin
|
|
|
|
end
|
2022-11-28 10:04:32 +00:00
|
|
|
self._on_done = {}
|
2022-11-28 21:03:44 +00:00
|
|
|
|
|
|
|
---@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
|
|
|
|
|
2024-06-24 18:55:09 +01:00
|
|
|
function Runner:plugin(name)
|
|
|
|
return Config.plugins[name] or self._plugins[name]
|
|
|
|
end
|
|
|
|
|
2024-06-26 16:06:56 +01:00
|
|
|
function Runner:start()
|
|
|
|
---@async
|
|
|
|
self._running = Async.run(function()
|
|
|
|
self:_start()
|
|
|
|
end, {
|
|
|
|
on_done = function()
|
|
|
|
for _, cb in ipairs(self._on_done) do
|
|
|
|
cb()
|
2022-11-28 21:03:44 +00:00
|
|
|
end
|
2024-06-26 16:06:56 +01:00
|
|
|
end,
|
|
|
|
})
|
2022-11-20 22:04:56 +00:00
|
|
|
end
|
|
|
|
|
2024-06-26 16:06:56 +01:00
|
|
|
---@async
|
|
|
|
function Runner:_start()
|
2024-06-24 18:55:09 +01:00
|
|
|
---@type string[]
|
|
|
|
local names = vim.tbl_keys(self._plugins)
|
|
|
|
table.sort(names)
|
2022-11-20 22:04:56 +00:00
|
|
|
|
2024-06-26 16:06:56 +01:00
|
|
|
---@type table<string,RunnerTask>
|
|
|
|
local state = {}
|
|
|
|
|
|
|
|
local active = 1
|
|
|
|
local waiting = 0
|
|
|
|
---@type number?
|
|
|
|
local wait_step = nil
|
|
|
|
|
|
|
|
---@param resume? boolean
|
|
|
|
local function continue(resume)
|
2024-06-26 17:42:52 +01:00
|
|
|
active = 0
|
2024-06-26 16:06:56 +01:00
|
|
|
waiting = 0
|
|
|
|
wait_step = nil
|
|
|
|
for _, name in ipairs(names) do
|
|
|
|
state[name] = state[name] or { step = 0 }
|
|
|
|
local s = state[name]
|
|
|
|
local running = s.task and s.task:is_running()
|
|
|
|
local step = self._pipeline[s.step]
|
|
|
|
|
2024-06-26 17:42:52 +01:00
|
|
|
if s.task and s.task:has_errors() then
|
|
|
|
local ignore = true
|
|
|
|
elseif step and step.task == "wait" and not resume then
|
2024-06-26 16:06:56 +01:00
|
|
|
waiting = waiting + 1
|
|
|
|
wait_step = s.step
|
|
|
|
elseif not running then
|
2024-06-26 17:45:40 +01:00
|
|
|
if not self._opts.concurrency or active < self._opts.concurrency then
|
|
|
|
local plugin = self:plugin(name)
|
|
|
|
if s.step == #self._pipeline then
|
|
|
|
s.task = nil
|
2024-06-26 16:06:56 +01:00
|
|
|
plugin._.working = false
|
2024-06-26 17:45:40 +01:00
|
|
|
elseif s.step < #self._pipeline then
|
|
|
|
active = active + 1
|
|
|
|
s.step = s.step + 1
|
|
|
|
step = self._pipeline[s.step]
|
|
|
|
if step.task == "wait" then
|
|
|
|
plugin._.working = false
|
|
|
|
else
|
|
|
|
s.task = self:queue(plugin, step)
|
|
|
|
plugin._.working = not not s.task
|
|
|
|
end
|
2024-06-26 16:06:56 +01:00
|
|
|
end
|
2022-11-28 21:03:44 +00:00
|
|
|
end
|
2024-06-26 17:42:52 +01:00
|
|
|
else
|
|
|
|
active = active + 1
|
2022-11-28 21:03:44 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2024-06-26 16:06:56 +01:00
|
|
|
|
|
|
|
while active > 0 do
|
|
|
|
continue()
|
|
|
|
if active == 0 and waiting > 0 then
|
|
|
|
local sync = self._pipeline[wait_step]
|
|
|
|
if sync and sync.opts and type(sync.opts.sync) == "function" then
|
|
|
|
sync.opts.sync(self)
|
|
|
|
end
|
|
|
|
continue(true)
|
|
|
|
end
|
|
|
|
coroutine.yield()
|
|
|
|
end
|
2022-11-20 22:04:56 +00:00
|
|
|
end
|
|
|
|
|
2022-11-28 21:03:44 +00:00
|
|
|
---@param plugin LazyPlugin
|
2024-06-26 16:06:56 +01:00
|
|
|
---@param step PipelineStep
|
2022-11-28 21:03:44 +00:00
|
|
|
---@return LazyTask?
|
2024-06-26 16:06:56 +01:00
|
|
|
function Runner:queue(plugin, step)
|
|
|
|
assert(self._running and self._running:running(), "Runner is not running")
|
|
|
|
local def = vim.split(step.task, ".", { plain = true })
|
2022-11-28 21:03:44 +00:00
|
|
|
---@type LazyTaskDef
|
|
|
|
local task_def = require("lazy.manage.task." .. def[1])[def[2]]
|
2024-06-26 16:06:56 +01:00
|
|
|
assert(task_def, "Task not found: " .. step.task)
|
|
|
|
local opts = step.opts or {}
|
2022-11-29 09:29:37 +00:00
|
|
|
if not (task_def.skip and task_def.skip(plugin, opts)) then
|
2024-06-26 16:06:56 +01:00
|
|
|
return Task.new(plugin, def[2], task_def.run, opts)
|
2022-11-28 21:03:44 +00:00
|
|
|
end
|
2022-11-20 22:04:56 +00:00
|
|
|
end
|
|
|
|
|
2024-06-26 16:06:56 +01:00
|
|
|
function Runner:is_running()
|
|
|
|
return self._running and self._running:running()
|
|
|
|
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)
|
2024-06-26 16:06:56 +01:00
|
|
|
if not self:is_running() then
|
2022-12-24 10:55:42 +00:00
|
|
|
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
|
2024-06-26 16:06:56 +01:00
|
|
|
while self:is_running() do
|
2022-11-28 21:03:44 +00:00
|
|
|
vim.wait(10)
|
2022-11-20 22:04:56 +00:00
|
|
|
end
|
|
|
|
end
|
2022-12-24 10:55:42 +00:00
|
|
|
return self
|
2022-11-20 22:04:56 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return Runner
|