mirror of https://github.com/folke/lazy.nvim.git
200 lines
4.7 KiB
Lua
200 lines
4.7 KiB
Lua
local Async = require("lazy.async")
|
|
local Process = require("lazy.manage.process")
|
|
|
|
---@class LazyTaskDef
|
|
---@field skip? fun(plugin:LazyPlugin, opts?:TaskOptions):any?
|
|
---@field run fun(task:LazyTask, opts:TaskOptions)
|
|
|
|
---@alias LazyTaskState {task:LazyTask, thread:thread}
|
|
|
|
---@class LazyTask
|
|
---@field plugin LazyPlugin
|
|
---@field name string
|
|
---@field output string
|
|
---@field status string
|
|
---@field error? string
|
|
---@field warn? string
|
|
---@field private _task fun(task:LazyTask, opts:TaskOptions)
|
|
---@field private _started? number
|
|
---@field private _ended? number
|
|
---@field private _opts TaskOptions
|
|
---@field private _running Async[]
|
|
local Task = {}
|
|
|
|
---@class TaskOptions: {[string]:any}
|
|
---@field on_done? fun(task:LazyTask)
|
|
|
|
---@param plugin LazyPlugin
|
|
---@param name string
|
|
---@param opts? TaskOptions
|
|
---@param task fun(task:LazyTask)
|
|
function Task.new(plugin, name, task, opts)
|
|
local self = setmetatable({}, {
|
|
__index = Task,
|
|
})
|
|
self._opts = opts or {}
|
|
self._running = {}
|
|
self._task = task
|
|
self._started = nil
|
|
self.plugin = plugin
|
|
self.name = name
|
|
self.output = ""
|
|
self.status = ""
|
|
---@param other LazyTask
|
|
plugin._.tasks = vim.tbl_filter(function(other)
|
|
return other.name ~= name or other:is_running()
|
|
end, plugin._.tasks or {})
|
|
table.insert(plugin._.tasks, self)
|
|
return self
|
|
end
|
|
|
|
function Task:has_started()
|
|
return self._started ~= nil
|
|
end
|
|
|
|
function Task:has_ended()
|
|
return self._ended ~= nil
|
|
end
|
|
|
|
function Task:is_done()
|
|
return self:has_started() and self:has_ended()
|
|
end
|
|
|
|
function Task:is_running()
|
|
return self:has_started() and not self:has_ended()
|
|
end
|
|
|
|
function Task:start()
|
|
assert(not self:has_started(), "task already started")
|
|
assert(not self:has_ended(), "task already done")
|
|
|
|
self._started = vim.uv.hrtime()
|
|
self:async(function()
|
|
self._task(self, self._opts)
|
|
end)
|
|
end
|
|
|
|
---@param msg string|string[]
|
|
---@param severity? vim.diagnostic.Severity
|
|
function Task:notify(msg, severity)
|
|
local var = severity == vim.diagnostic.severity.ERROR and "error"
|
|
or severity == vim.diagnostic.severity.WARN and "warn"
|
|
or "output"
|
|
msg = type(msg) == "table" and table.concat(msg, "\n") or msg
|
|
---@cast msg string
|
|
---@diagnostic disable-next-line: no-unknown
|
|
self[var] = self[var] and (self[var] .. "\n" .. msg) or msg
|
|
self.status = msg
|
|
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
|
|
end
|
|
|
|
---@param msg string|string[]
|
|
function Task:notify_error(msg)
|
|
self:notify(msg, vim.diagnostic.severity.ERROR)
|
|
end
|
|
|
|
---@param msg string|string[]
|
|
function Task:notify_warn(msg)
|
|
self:notify(msg, vim.diagnostic.severity.WARN)
|
|
end
|
|
|
|
---@param fn async fun()
|
|
function Task:async(fn)
|
|
local async = Async.run(fn, {
|
|
on_done = function()
|
|
self:_done()
|
|
end,
|
|
on_error = function(err)
|
|
self:notify_error(err)
|
|
end,
|
|
on_yield = function(res)
|
|
self:notify(res)
|
|
end,
|
|
})
|
|
table.insert(self._running, async)
|
|
end
|
|
|
|
---@private
|
|
function Task:_done()
|
|
assert(self:has_started(), "task not started")
|
|
assert(not self:has_ended(), "task already done")
|
|
|
|
for _, t in ipairs(self._running) do
|
|
if t:running() then
|
|
return
|
|
end
|
|
end
|
|
|
|
self._ended = vim.uv.hrtime()
|
|
if self._opts.on_done then
|
|
self._opts.on_done(self)
|
|
end
|
|
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
|
|
vim.api.nvim_exec_autocmds("User", {
|
|
pattern = "LazyPlugin" .. self.name:sub(1, 1):upper() .. self.name:sub(2),
|
|
data = { plugin = self.plugin.name },
|
|
})
|
|
end
|
|
|
|
function Task:time()
|
|
if not self:has_started() then
|
|
return 0
|
|
end
|
|
if not self:has_ended() then
|
|
return (vim.uv.hrtime() - self._started) / 1e6
|
|
end
|
|
return (self._ended - self._started) / 1e6
|
|
end
|
|
|
|
---@async
|
|
---@param cmd string
|
|
---@param opts? ProcessOpts
|
|
function Task:spawn(cmd, opts)
|
|
opts = opts or {}
|
|
local on_line = opts.on_line
|
|
local on_exit = opts.on_exit
|
|
|
|
function opts.on_line(line)
|
|
self.status = line
|
|
if on_line then
|
|
pcall(on_line, line)
|
|
end
|
|
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
|
|
end
|
|
|
|
local running = true
|
|
---@param output string
|
|
function opts.on_exit(ok, output)
|
|
self.output = self.output .. output
|
|
if not ok then
|
|
self.error = self.error and (self.error .. "\n" .. output) or output
|
|
end
|
|
if on_exit then
|
|
pcall(on_exit, ok, output)
|
|
end
|
|
running = false
|
|
end
|
|
Process.spawn(cmd, opts)
|
|
while running do
|
|
coroutine.yield()
|
|
end
|
|
end
|
|
|
|
---@param tasks (LazyTask?)[]
|
|
function Task.all_done(tasks)
|
|
for _, task in ipairs(tasks) do
|
|
if task and not task:is_done() then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function Task:wait()
|
|
while self:is_running() do
|
|
vim.wait(10)
|
|
end
|
|
end
|
|
|
|
return Task
|