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

189 lines
4.6 KiB
Lua
Raw Normal View History

local Async = require("lazy.async")
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-28 18:04:32 +08:00
---@class RunnerOpts
---@field pipeline (string|{[1]:string, [string]:any})[]
2022-11-28 18:04:32 +08:00
---@field plugins? LazyPlugin[]|fun(plugin:LazyPlugin):any?
---@field concurrency? number
2022-11-28 18:04:32 +08:00
---@class RunnerTask
---@field task? LazyTask
---@field step number
---@alias PipelineStep {task:string, opts?:TaskOptions }
2022-11-21 06:04:56 +08:00
---@class Runner
2024-06-25 01:55:09 +08:00
---@field _plugins table<string,LazyPlugin>
---@field _pipeline PipelineStep[]
2022-11-28 18:04:32 +08:00
---@field _opts RunnerOpts
---@field _running? Async
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
---@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-21 06:04:56 +08:00
return self
end
2024-06-25 01:55:09 +08:00
function Runner:plugin(name)
return self._plugins[name]
end
--- Update plugins
function Runner:update()
for name in pairs(self._plugins) do
self._plugins[name] = Config.plugins[name] or self._plugins[name]
end
2024-06-25 01:55:09 +08:00
end
function Runner:start()
---@async
2024-06-28 22:08:26 +08:00
self._running = Async.new(function()
self:_start()
2024-06-28 22:08:26 +08:00
end)
2022-11-21 06:04:56 +08:00
end
---@async
function Runner:_start()
2024-06-25 01:55:09 +08:00
---@type string[]
local names = vim.tbl_keys(self._plugins)
table.sort(names)
2022-11-21 06:04:56 +08: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-27 00:42:52 +08:00
active = 0
waiting = 0
wait_step = nil
2024-06-27 01:58:45 +08:00
local next = {} ---@type string[]
-- check running tasks
for _, name in ipairs(names) do
state[name] = state[name] or { step = 0 }
local s = state[name]
2024-06-28 22:08:26 +08:00
local is_running = s.task and s.task:running()
local step = self._pipeline[s.step]
if is_running then
-- still running
active = active + 1
2024-06-27 01:58:45 +08:00
-- selene:allow(empty_if)
elseif s.task and s.task:has_errors() then
2024-06-27 01:58:45 +08:00
-- don't continue tasks if there are errors
2024-06-27 00:42:52 +08:00
elseif step and step.task == "wait" and not resume then
2024-06-27 01:58:45 +08:00
-- waiting for sync
waiting = waiting + 1
wait_step = s.step
2024-06-27 01:58:45 +08:00
else
next[#next + 1] = name
end
end
-- schedule next tasks
for _, name in ipairs(next) do
if self._opts.concurrency and active >= self._opts.concurrency then
break
end
local s = state[name]
local plugin = self:plugin(name)
if s.step == #self._pipeline then
-- done
s.task = nil
plugin._.working = false
elseif s.step < #self._pipeline then
-- next
s.step = s.step + 1
local step = self._pipeline[s.step]
if step.task == "wait" then
plugin._.working = false
waiting = waiting + 1
2024-06-27 02:17:32 +08:00
wait_step = s.step
2024-06-27 01:58:45 +08:00
else
s.task = self:queue(plugin, step)
plugin._.working = true
2024-06-27 01:58:45 +08:00
active = active + 1
end
end
end
end
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-21 06:04:56 +08:00
end
---@param plugin LazyPlugin
---@param step PipelineStep
---@return LazyTask?
function Runner:queue(plugin, step)
assert(self._running and self._running:running(), "Runner is not running")
local def = vim.split(step.task, ".", { plain = true })
---@type LazyTaskDef
local task_def = require("lazy.manage.task." .. def[1])[def[2]]
assert(task_def, "Task not found: " .. step.task)
local opts = step.opts or {}
2022-11-29 17:29:37 +08:00
if not (task_def.skip and task_def.skip(plugin, opts)) then
return Task.new(plugin, def[2], task_def.run, opts)
end
2022-11-21 06:04:56 +08:00
end
function Runner:is_running()
return self._running and self._running:running()
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)
if not self:is_running() then
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
2024-06-28 22:08:26 +08:00
self._running:on("done", cb)
2022-11-28 18:04:32 +08:00
else
2024-06-28 22:08:26 +08:00
self._running:wait()
2022-11-21 06:04:56 +08:00
end
return self
2022-11-21 06:04:56 +08:00
end
return Runner