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