Skip to content

Commit

Permalink
feat: new task pipeline runner
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed Nov 28, 2022
1 parent 97f44f9 commit ab1b512
Show file tree
Hide file tree
Showing 7 changed files with 423 additions and 294 deletions.
103 changes: 40 additions & 63 deletions lua/lazy/manage/init.lua
Original file line number Diff line number Diff line change
@@ -1,109 +1,86 @@
local Config = require("lazy.core.config")
local Task = require("lazy.manage.task")
local Runner = require("lazy.manage.runner")
local Plugin = require("lazy.core.plugin")

local M = {}

---@alias ManagerOpts {wait?: boolean, plugins?: LazyPlugin[], clear?: boolean, show?: boolean}
---@class ManagerOpts
---@field wait? boolean
---@field clear? boolean
---@field interactive? boolean

---@param operation TaskType
---@param ropts RunnerOpts
---@param opts? ManagerOpts
---@param filter? fun(plugin:LazyPlugin):boolean?
function M.run(operation, opts, filter)
function M.run(ropts, opts)
opts = opts or {}
local plugins = opts.plugins or Config.plugins
if opts.interactive == nil then
opts.interactive = Config.options.interactive
end
if ropts.interactive == nil then
ropts.interactive = opts.interactive
end

if opts.clear then
M.clear()
end

if opts.show then
if opts.interactive then
require("lazy.view").show()
end

---@type Runner
local runner = Runner.new()

-- install missing plugins
for _, plugin in pairs(plugins) do
if filter == nil or filter(plugin) then
runner:add(Task.new(plugin, operation))
end
end
local runner = Runner.new(ropts)
runner:start()

vim.cmd([[do User LazyRender]])

-- wait for install to finish
-- wait for post-install to finish
runner:wait(function()
-- check if we need to do any post-install hooks
for _, plugin in ipairs(runner:plugins()) do
if plugin.dirty then
runner:add(Task.new(plugin, "docs"))
if plugin.opt == false or plugin.run then
runner:add(Task.new(plugin, "run"))
end
end
plugin.dirty = false
if opts.show and operation == "update" and plugin.updated and plugin.updated.from ~= plugin.updated.to then
runner:add(Task.new(plugin, "log", {
log = {
from = plugin.updated.from,
to = plugin.updated.to,
},
}))
end
end
-- wait for post-install to finish
runner:wait(function()
vim.cmd([[do User LazyRender]])
end)
vim.cmd([[do User LazyRender]])
end)

if opts.wait then
runner:wait()
end
return runner
end

---@param opts? ManagerOpts
function M.install(opts)
---@param plugin LazyPlugin
M.run("install", opts, function(plugin)
return plugin.uri and not plugin.installed
end)
M.run({
pipeline = { "git.install", { "plugin.docs", "plugin.run" } },
plugins = function(plugin)
return plugin.uri and not plugin.installed
end,
}, opts)
end

---@param opts? ManagerOpts
function M.update(opts)
---@param plugin LazyPlugin
M.run("update", opts, function(plugin)
return plugin.uri and plugin.installed
end)
M.run({
pipeline = { "git.update", { "plugin.docs", "plugin.run" }, "git.log" },
plugins = function(plugin)
return plugin.uri and plugin.installed
end,
}, opts)
end

---@param opts? ManagerOpts
function M.log(opts)
---@param plugin LazyPlugin
M.run("log", opts, function(plugin)
return plugin.uri and plugin.installed
end)
end

---@param opts? ManagerOpts
function M.docs(opts)
---@param plugin LazyPlugin
M.run("docs", opts, function(plugin)
return plugin.installed
end)
M.run({
pipeline = { "git.log" },
plugins = function(plugin)
return plugin.uri and plugin.installed
end,
}, opts)
end

---@param opts? ManagerOpts
function M.clean(opts)
opts = opts or {}
Plugin.update_state(true)
opts.plugins = vim.tbl_values(Config.to_clean)
M.run("clean", opts)
M.run({
pipeline = { "plugin.clean" },
plugins = Config.to_clean,
}, opts)
end

function M.clear()
Expand All @@ -114,7 +91,7 @@ function M.clear()
if plugin.tasks then
---@param task LazyTask
plugin.tasks = vim.tbl_filter(function(task)
return task.running
return task:is_running()
end, plugin.tasks)
end
end
Expand Down
150 changes: 118 additions & 32 deletions lua/lazy/manage/runner.lua
Original file line number Diff line number Diff line change
@@ -1,24 +1,124 @@
local Task = require("lazy.manage.task")
local Config = require("lazy.core.config")

---@alias LazyPipeline (TaskType|TaskType[])[]

---@class RunnerOpts
---@field pipeline LazyPipeline
---@field interactive? boolean
---@field plugins? LazyPlugin[]|fun(plugin:LazyPlugin):any?

---@class Runner
---@field _tasks LazyTask[]
---@field _plugins LazyPlugin[]
---@field _running boolean
---@field _on_done fun()[]
---@field _waiting fun()[]
---@field _opts RunnerOpts
local Runner = {}

function Runner.new()
local self = setmetatable({}, {
__index = Runner,
})
---@param opts RunnerOpts
function Runner.new(opts)
local self = setmetatable({}, { __index = Runner })
self._opts = opts or {}
self._tasks = {}

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
self._running = false
self._on_done = {}
self._waiting = {}
return self
end

---@param task LazyTask
function Runner:add(task)
table.insert(self._tasks, task)
task:start()
---@param plugin LazyPlugin
---@param pipeline LazyPipeline
function Runner:_run(plugin, pipeline)
if #pipeline == 0 then
return
end
local ops = table.remove(pipeline, 1)
if ops == "wait" then
return table.insert(self._waiting, function()
self:_run(plugin, pipeline)
end)
end

ops = type(ops) == "string" and { ops } or ops
---@cast ops TaskType[]

---@type LazyTask[]
local tasks = {}

local function on_done()
for _, task in ipairs(tasks) do
if task.error or not task:is_done() then
return
end
end
self:_run(plugin, pipeline)
end

for _, op in ipairs(ops) do
local task = self:queue(plugin, op, { on_done = on_done })
if task then
table.insert(tasks, task)
end
end

for _, task in ipairs(tasks) do
task:start()
end
end

---@param plugin LazyPlugin
---@param task_type TaskType
---@param opts? TaskOptions
---@return LazyTask?
function Runner:queue(plugin, task_type, opts)
local def = vim.split(task_type, ".", { plain = true })
assert(#def == 2)
---@type LazyTaskDef
local task_def = require("lazy.manage.task." .. def[1])[def[2]]
assert(task_def)
if not task_def.needed or task_def.needed(plugin, self._opts) then
local task = Task.new(plugin, def[2], task_def.run, opts)
table.insert(self._tasks, task)
return task
end
end

function Runner:is_empty()
return #self._tasks == 0
function Runner:start()
for _, plugin in pairs(self._plugins) do
self:_run(plugin, vim.deepcopy(self._opts.pipeline))
end
self._running = true
local check = vim.loop.new_check()

check:start(function()
for _, task in ipairs(self._tasks) do
if task:is_running() then
return
end
end
if #self._waiting > 0 then
for _, cb in ipairs(self._waiting) do
cb()
end
self._waiting = {}
return
end
check:stop()
self._running = false
for _, cb in ipairs(self._on_done) do
vim.schedule(cb)
end
self._on_done = {}
end)
end

---@return LazyPlugin[]
Expand All @@ -33,33 +133,19 @@ function Runner:tasks()
return self._tasks
end

-- Execute the callback async when done.
-- When no callback is specified, this will wait sync
---@param cb? fun()
function Runner:wait(cb)
if #self._tasks == 0 then
if #self._tasks == 0 or not self._running then
return cb and cb()
end

local done = false
local check = vim.loop.new_check()

check:start(function()
for _, task in ipairs(self._tasks) do
if task.running then
return
end
end

check:stop()

done = true

if cb then
vim.schedule(cb)
end
end)

if not cb then
while not done do
if cb then
table.insert(self._on_done, cb)
else
-- sync wait
while self._running do
vim.wait(100)
end
end
Expand Down

0 comments on commit ab1b512

Please sign in to comment.