Skip to content

Commit

Permalink
feat: lots of improvements to pipeline runner and converted all tasks…
Browse files Browse the repository at this point in the history
… to new system
  • Loading branch information
folke committed Nov 28, 2022
1 parent 4de10f9 commit fb84c08
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 197 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

## ✅ TODO

- [ ] show time taken for op in view
- [ ] package meta index (package.lua cache for all packages)
- [ ] migrate from Packer
- [ ] auto lazy-loading of lua modules
Expand Down
5 changes: 4 additions & 1 deletion lua/lazy/core/plugin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ M.dirty = false
---@field updated? {from:string, to:string}
---@field is_local? boolean
---@field is_symlink? boolean
---@field cloned? boolean

---@class LazyPluginRef
---@field branch? string
Expand Down Expand Up @@ -139,7 +140,9 @@ function M.update_state(check_clean)
for _, plugin in pairs(Config.plugins) do
plugin._ = plugin._ or {}
plugin[1] = plugin["1"] or plugin[1]
plugin.opt = plugin.opt == nil and Config.options.opt or plugin.opt
if plugin.opt == nil then
plugin.opt = Config.options.opt
end
local opt = plugin.opt and "opt" or "start"
plugin.dir = Config.options.package_path .. "/" .. opt .. "/" .. plugin.name
plugin._.is_local = plugin.uri:sub(1, 4) ~= "http" and plugin.uri:sub(1, 3) ~= "git"
Expand Down
27 changes: 22 additions & 5 deletions lua/lazy/manage/git.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ local Semver = require("lazy.manage.semver")

local M = {}

---@alias GitInfo {branch?:string, commit?:string, tag?:string, version?:Semver}

---@param details? boolean
---@return GitInfo?
function M.info(repo, details)
local line = Util.head(repo .. "/.git/HEAD")
if line then
Expand All @@ -23,7 +26,6 @@ function M.info(repo, details)
end
end)
end

return ret
end
end
Expand All @@ -48,6 +50,7 @@ function M.get_versions(repo, spec)
end

---@param plugin LazyPlugin
---@return {branch:string, commit?:string}?
function M.get_branch(plugin)
if plugin.branch then
return {
Expand All @@ -69,22 +72,36 @@ function M.get_branch(plugin)
end

---@param plugin LazyPlugin
---@return GitInfo?
function M.get_target(plugin)
local branch = M.get_branch(plugin)

if plugin.commit then
return { branch = branch, commit = plugin.commit }
return {
branch = branch and branch.branch,
commit = plugin.commit,
}
end
if plugin.tag then
return { branch = branch, tag = plugin.tag, commit = M.ref(plugin.dir, "tags/" .. plugin.tag) }
return {
branch = branch and branch.branch,
tag = plugin.tag,
commit = M.ref(plugin.dir, "tags/" .. plugin.tag),
}
end
if plugin.version then
local last = Semver.last(M.get_versions(plugin.dir, plugin.version))
if last then
return { branch = branch, version = last, tag = last.tag, commit = M.ref(plugin.dir, "tags/" .. last.tag) }
return {
branch = branch and branch.branch,
version = last,
tag = last.tag,
commit = M.ref(plugin.dir, "tags/" .. last.tag),
}
end
end
return { branch = branch, commit = branch.commit }
---@diagnostic disable-next-line: return-type-mismatch
return branch
end

function M.ref(repo, ref)
Expand Down
25 changes: 19 additions & 6 deletions lua/lazy/manage/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ function M.run(ropts, opts)
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()
Expand Down Expand Up @@ -47,7 +44,14 @@ end
---@param opts? ManagerOpts
function M.install(opts)
M.run({
pipeline = { "git.install", "plugin.docs", "plugin.run" },
pipeline = {
"fs.symlink",
"git.clone",
"git.checkout",
"plugin.docs",
"wait",
"plugin.run",
},
plugins = function(plugin)
return plugin.uri and not plugin._.installed
end,
Expand All @@ -57,7 +61,16 @@ end
---@param opts? ManagerOpts
function M.update(opts)
M.run({
pipeline = { "git.update", "plugin.docs", "plugin.run", "wait", "git.log" },
pipeline = {
"fs.symlink",
"git.branch",
"git.fetch",
"git.checkout",
"plugin.docs",
"plugin.run",
"wait",
{ "git.log", updated = true },
},
plugins = function(plugin)
return plugin.uri and plugin._.installed
end,
Expand All @@ -78,7 +91,7 @@ end
function M.clean(opts)
Plugin.update_state(true)
M.run({
pipeline = { "plugin.clean" },
pipeline = { "fs.clean" },
plugins = Config.to_clean,
}, opts)
end
Expand Down
141 changes: 75 additions & 66 deletions lua/lazy/manage/runner.lua
Original file line number Diff line number Diff line change
@@ -1,132 +1,141 @@
local Task = require("lazy.manage.task")
local Config = require("lazy.core.config")

---@alias LazyPipeline TaskType[]

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

---@alias PipelineStep {task:string, opts?:TaskOptions}
---@alias LazyRunnerTask {co:thread, status: {task?:LazyTask, waiting?:boolean}}

---@class Runner
---@field _tasks LazyTask[]
---@field _plugins LazyPlugin[]
---@field _running boolean
---@field _running LazyRunnerTask[]
---@field _pipeline PipelineStep[]
---@field _on_done fun()[]
---@field _waiting fun()[]
---@field _opts RunnerOpts
local 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._running = {}
self._on_done = {}
self._waiting = {}

---@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)

return self
end

---@param plugin LazyPlugin
---@param pipeline LazyPipeline
function Runner:_run(plugin, pipeline)
---@type TaskType
local op = table.remove(pipeline, 1)
if op == "wait" then
return table.insert(self._waiting, function()
self:_run(plugin, pipeline)
end)
---@param entry LazyRunnerTask
function Runner:_resume(entry)
if entry.status.task and not entry.status.task:is_done() then
return true
end
self:queue(plugin, op, function(task)
if not (task and task.error) and #pipeline > 0 then
self:_run(plugin, pipeline)
end
end)
local ok, status = coroutine.resume(entry.co)
entry.status = ok and status
return entry.status ~= nil
end

---@param plugin LazyPlugin
---@param task_type TaskType
---@param on_done fun(task?:LazyTask)
---@return LazyTask?
function Runner:queue(plugin, task_type, on_done)
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.skip and task_def.skip(plugin, self._opts)) then
local task = Task.new(plugin, def[2], task_def.run, { on_done = on_done })
table.insert(self._tasks, task)
task:start()
else
on_done()
function Runner:resume(waiting)
local running = false
for _, entry in ipairs(self._running) do
if entry.status then
if waiting and entry.status.waiting then
entry.status.waiting = false
end
if not entry.status.waiting and self:_resume(entry) then
running = true
end
end
end
return running or (not waiting and self:resume(true))
end

function Runner:start()
for _, plugin in pairs(self._plugins) do
self:_run(plugin, vim.deepcopy(self._opts.pipeline))
local co = coroutine.create(self.run_pipeline)
local ok, status = coroutine.resume(co, self, plugin)
if ok then
table.insert(self._running, { co = co, status = status })
end
end
self._running = true
local check = vim.loop.new_check()

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
local waiting = self._waiting
self._waiting = {}
for _, cb in ipairs(waiting) do
cb()
end
if self:resume() then
return
end
check:stop()
self._running = false
self._running = {}
for _, cb in ipairs(self._on_done) do
vim.schedule(cb)
end
self._on_done = {}
end)
end

---@return LazyPlugin[]
function Runner:plugins()
---@param task LazyTask
return vim.tbl_map(function(task)
return task.plugin
end, self._tasks)
---@async
---@param plugin LazyPlugin
function Runner:run_pipeline(plugin)
for _, step in ipairs(self._pipeline) do
if step.task == "wait" then
coroutine.yield({ waiting = 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
return
end
end
end
end
end

function Runner:tasks()
return self._tasks
---@param plugin LazyPlugin
---@param task_type string
---@param task_opts? TaskOptions
---@return LazyTask?
function Runner:queue(plugin, task_type, task_opts)
assert(self._running)
local def = vim.split(task_type, ".", { plain = true })
---@type LazyTaskDef
local task_def = require("lazy.manage.task." .. def[1])[def[2]]
assert(task_def)
if not (task_def.skip and task_def.skip(plugin, task_opts)) then
local task = Task.new(plugin, def[2], task_def.run, task_opts)
task:start()
return task
end
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 or not self._running then
if #self._running == 0 then
return cb and cb()
end

if cb then
table.insert(self._on_done, cb)
else
-- sync wait
while self._running do
vim.wait(100)
while #self._running > 0 do
vim.wait(10)
end
end
end
Expand Down

0 comments on commit fb84c08

Please sign in to comment.