refactor: logging

This commit is contained in:
Folke Lemaitre 2024-06-26 18:31:31 +02:00
parent 765773a176
commit 6c7ef7e27a
No known key found for this signature in database
GPG Key ID: 41F8B1FBACAE2040
10 changed files with 130 additions and 80 deletions

View File

@ -403,7 +403,7 @@ end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
function M.has_errors(plugin) function M.has_errors(plugin)
for _, task in ipairs(plugin._.tasks or {}) do for _, task in ipairs(plugin._.tasks or {}) do
if task.error then if task:has_errors() then
return true return true
end end
end end

View File

@ -232,7 +232,7 @@ function M.clear(plugins)
if plugin._.tasks then if plugin._.tasks then
---@param task LazyTask ---@param task LazyTask
plugin._.tasks = vim.tbl_filter(function(task) plugin._.tasks = vim.tbl_filter(function(task)
return task:is_running() or task.error return task:is_running() or task:has_errors()
end, plugin._.tasks) end, plugin._.tasks)
end end
end end

View File

@ -96,7 +96,7 @@ function Runner:_start()
wait_step = s.step wait_step = s.step
elseif not running then elseif not running then
local plugin = self:plugin(name) local plugin = self:plugin(name)
if s.task and s.task.error then if s.task and s.task:has_errors() then
active = active - 1 active = active - 1
elseif s.step == #self._pipeline then elseif s.step == #self._pipeline then
s.task = nil s.task = nil

View File

@ -18,6 +18,7 @@ M.log = {
local stat = vim.uv.fs_stat(plugin.dir .. "/.git") local stat = vim.uv.fs_stat(plugin.dir .. "/.git")
return not (stat and stat.type == "directory") return not (stat and stat.type == "directory")
end, end,
---@async
---@param opts {args?: string[], updated?:boolean, check?:boolean} ---@param opts {args?: string[], updated?:boolean, check?:boolean}
run = function(self, opts) run = function(self, opts)
local args = { local args = {
@ -68,6 +69,7 @@ M.clone = {
skip = function(plugin) skip = function(plugin)
return plugin._.installed or plugin._.is_local return plugin._.installed or plugin._.is_local
end, end,
---@async
run = function(self) run = function(self)
local args = { local args = {
"clone", "clone",
@ -129,6 +131,7 @@ M.branch = {
local branch = assert(Git.get_branch(plugin)) local branch = assert(Git.get_branch(plugin))
return Git.get_commit(plugin.dir, branch, true) return Git.get_commit(plugin.dir, branch, true)
end, end,
---@async
run = function(self) run = function(self)
local args = { local args = {
"remote", "remote",
@ -154,14 +157,17 @@ M.origin = {
local origin = Git.get_origin(plugin.dir) local origin = Git.get_origin(plugin.dir)
return origin == plugin.url return origin == plugin.url
end, end,
---@async
---@param opts {check?:boolean} ---@param opts {check?:boolean}
run = function(self, opts) run = function(self, opts)
if opts.check then if opts.check then
local origin = Git.get_origin(self.plugin.dir) local origin = Git.get_origin(self.plugin.dir)
self.error = "Origin has changed:\n" self:error({
self.error = self.error .. " * old: " .. origin .. "\n" "Origin has changed:",
self.error = self.error .. " * new: " .. self.plugin.url .. "\n" " * old: " .. origin,
self.error = self.error .. "Please run update to fix" " * new: " .. self.plugin.url,
"Please run update to fix",
})
return return
end end
require("lazy.manage.task.fs").clean.run(self, opts) require("lazy.manage.task.fs").clean.run(self, opts)
@ -173,6 +179,7 @@ M.status = {
skip = function(plugin) skip = function(plugin)
return not plugin._.installed or plugin._.is_local return not plugin._.installed or plugin._.is_local
end, end,
---@async
run = function(self) run = function(self)
self:spawn("git", { self:spawn("git", {
args = { "ls-files", "-d", "-m" }, args = { "ls-files", "-d", "-m" },
@ -180,6 +187,7 @@ M.status = {
on_exit = function(ok, output) on_exit = function(ok, output)
if ok then if ok then
local lines = vim.split(output, "\n") local lines = vim.split(output, "\n")
---@type string[]
lines = vim.tbl_filter(function(line) lines = vim.tbl_filter(function(line)
-- Fix doc/tags being marked as modified -- Fix doc/tags being marked as modified
if line:gsub("[\\/]", "/") == "doc/tags" then if line:gsub("[\\/]", "/") == "doc/tags" then
@ -190,12 +198,13 @@ M.status = {
return line ~= "" return line ~= ""
end, lines) end, lines)
if #lines > 0 then if #lines > 0 then
self.error = "You have local changes in `" .. self.plugin.dir .. "`:\n" local msg = { "You have local changes in `" .. self.plugin.dir .. "`:" }
for _, line in ipairs(lines) do for _, line in ipairs(lines) do
self.error = self.error .. " * " .. line .. "\n" msg[#msg + 1] = " * " .. line
end end
self.error = self.error .. "Please remove them to update.\n" msg[#msg + 1] = "Please remove them to update."
self.error = self.error .. "You can also press `x` to remove the plugin and then `I` to install it again." msg[#msg + 1] = "You can also press `x` to remove the plugin and then `I` to install it again."
self:error(msg)
end end
end end
end, end,
@ -209,6 +218,7 @@ M.fetch = {
return not plugin._.installed or plugin._.is_local return not plugin._.installed or plugin._.is_local
end, end,
---@async
run = function(self) run = function(self)
local args = { local args = {
"fetch", "fetch",
@ -236,6 +246,7 @@ M.checkout = {
return not plugin._.installed or plugin._.is_local return not plugin._.installed or plugin._.is_local
end, end,
---@async
---@param opts {lockfile?:boolean} ---@param opts {lockfile?:boolean}
run = function(self, opts) run = function(self, opts)
local info = assert(Git.info(self.plugin.dir)) local info = assert(Git.info(self.plugin.dir))

View File

@ -3,21 +3,23 @@ local Process = require("lazy.manage.process")
---@class LazyTaskDef ---@class LazyTaskDef
---@field skip? fun(plugin:LazyPlugin, opts?:TaskOptions):any? ---@field skip? fun(plugin:LazyPlugin, opts?:TaskOptions):any?
---@field run fun(task:LazyTask, opts:TaskOptions) ---@field run async fun(task:LazyTask, opts:TaskOptions)
---@alias LazyTaskFn async fun(task:LazyTask, opts:TaskOptions) ---@alias LazyTaskFn async fun(task:LazyTask, opts:TaskOptions)
---@class LazyMsg
---@field msg string
---@field level? number
---@class LazyTask ---@class LazyTask
---@field plugin LazyPlugin ---@field plugin LazyPlugin
---@field name string ---@field name string
---@field output string ---@field private _log LazyMsg[]
---@field status string
---@field error? string
---@field warn? string
---@field private _started? number ---@field private _started? number
---@field private _ended? number ---@field private _ended? number
---@field private _opts TaskOptions ---@field private _opts TaskOptions
---@field private _running Async ---@field private _running Async
---@field private _level number
local Task = {} local Task = {}
---@class TaskOptions: {[string]:any} ---@class TaskOptions: {[string]:any}
@ -30,10 +32,10 @@ local Task = {}
function Task.new(plugin, name, task, opts) function Task.new(plugin, name, task, opts)
local self = setmetatable({}, { __index = Task }) local self = setmetatable({}, { __index = Task })
self._opts = opts or {} self._opts = opts or {}
self._log = {}
self._level = vim.log.levels.TRACE
self.plugin = plugin self.plugin = plugin
self.name = name self.name = name
self.output = ""
self.status = ""
---@param other LazyTask ---@param other LazyTask
plugin._.tasks = vim.tbl_filter(function(other) plugin._.tasks = vim.tbl_filter(function(other)
return other.name ~= name or other:is_running() return other.name ~= name or other:is_running()
@ -43,6 +45,31 @@ function Task.new(plugin, name, task, opts)
return self return self
end end
---@param level? number
---@return LazyMsg[]
function Task:get_log(level)
level = level or vim.log.levels.DEBUG
return vim.tbl_filter(function(msg)
return msg.level >= level
end, self._log)
end
---@param level? number
function Task:output(level)
return table.concat(
---@param m LazyMsg
vim.tbl_map(function(m)
return m.msg
end, self:get_log(level)),
"\n"
)
end
function Task:status()
local ret = self._log[#self._log]
return ret and ret.msg or ""
end
function Task:has_started() function Task:has_started()
return self._started ~= nil return self._started ~= nil
end end
@ -55,6 +82,14 @@ function Task:is_running()
return not self:has_ended() return not self:has_ended()
end end
function Task:has_errors()
return self._level >= vim.log.levels.ERROR
end
function Task:has_warnings()
return self._level >= vim.log.levels.WARN
end
---@private ---@private
---@param task LazyTaskFn ---@param task LazyTaskFn
function Task:_start(task) function Task:_start(task)
@ -70,36 +105,33 @@ function Task:_start(task)
self:_done() self:_done()
end, end,
on_error = function(err) on_error = function(err)
self:notify_error(err) self:error(err)
end, end,
on_yield = function(res) on_yield = function(res)
self:notify(res) self:log(res)
end, end,
}) })
end end
---@param msg string|string[] ---@param msg string|string[]
---@param severity? vim.diagnostic.Severity ---@param level? number
function Task:notify(msg, severity) function Task:log(msg, level)
local var = severity == vim.diagnostic.severity.ERROR and "error" level = level or vim.log.levels.DEBUG
or severity == vim.diagnostic.severity.WARN and "warn" self._level = math.max(self._level or 0, level or 0)
or "output"
msg = type(msg) == "table" and table.concat(msg, "\n") or msg msg = type(msg) == "table" and table.concat(msg, "\n") or msg
---@cast msg string ---@cast msg string
---@diagnostic disable-next-line: no-unknown table.insert(self._log, { msg = msg, level = level })
self[var] = self[var] and (self[var] .. "\n" .. msg) or msg
self.status = msg
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false }) vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
end end
---@param msg string|string[] ---@param msg string|string[]
function Task:notify_error(msg) function Task:error(msg)
self:notify(msg, vim.diagnostic.severity.ERROR) self:log(msg, vim.log.levels.ERROR)
end end
---@param msg string|string[] ---@param msg string|string[]
function Task:notify_warn(msg) function Task:warn(msg)
self:notify(msg, vim.diagnostic.severity.WARN) self:log(msg, vim.log.levels.WARN)
end end
---@private ---@private
@ -141,20 +173,16 @@ function Task:spawn(cmd, opts)
local on_exit = opts.on_exit local on_exit = opts.on_exit
function opts.on_line(line) function opts.on_line(line)
self.status = line self:log(line, vim.log.levels.TRACE)
if on_line then if on_line then
pcall(on_line, line) pcall(on_line, line)
end end
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
end end
local running = true local running = true
---@param output string ---@param output string
function opts.on_exit(ok, output) function opts.on_exit(ok, output)
self.output = self.output .. output self:log(output, ok and vim.log.levels.DEBUG or vim.log.levels.ERROR)
if not ok then
self.error = self.error and (self.error .. "\n" .. output) or output
end
if on_exit then if on_exit then
pcall(on_exit, ok, output) pcall(on_exit, ok, output)
end end

View File

@ -1,4 +1,3 @@
local Config = require("lazy.core.config")
local Loader = require("lazy.core.loader") local Loader = require("lazy.core.loader")
local Rocks = require("lazy.pkg.rockspec") local Rocks = require("lazy.pkg.rockspec")
local Util = require("lazy.util") local Util = require("lazy.util")
@ -21,9 +20,10 @@ local B = {}
---@param build string ---@param build string
function B.cmd(task, build) function B.cmd(task, build)
local cmd = vim.api.nvim_parse_cmd(build:sub(2), {}) --[[@as vim.api.keyset.cmd]] local cmd = vim.api.nvim_parse_cmd(build:sub(2), {}) --[[@as vim.api.keyset.cmd]]
task.output = vim.api.nvim_cmd(cmd, { output = true }) task:log(vim.api.nvim_cmd(cmd, { output = true }))
end end
---@async
---@param task LazyTask ---@param task LazyTask
---@param build string ---@param build string
function B.shell(task, build) function B.shell(task, build)
@ -44,6 +44,7 @@ M.build = {
end end
return not ((plugin._.dirty or plugin._.build) and (plugin.build or get_build_file(plugin))) return not ((plugin._.dirty or plugin._.build) and (plugin.build or get_build_file(plugin)))
end, end,
---@async
run = function(self) run = function(self)
vim.cmd([[silent! runtime plugin/rplugin.vim]]) vim.cmd([[silent! runtime plugin/rplugin.vim]])
@ -92,7 +93,7 @@ M.docs = {
run = function(self) run = function(self)
local docs = self.plugin.dir .. "/doc/" local docs = self.plugin.dir .. "/doc/"
if Util.file_exists(docs) then if Util.file_exists(docs) then
self.output = vim.api.nvim_cmd({ cmd = "helptags", args = { docs } }, { output = true }) self:log(vim.api.nvim_cmd({ cmd = "helptags", args = { docs } }, { output = true }))
end end
end, end,
} }

View File

@ -106,15 +106,15 @@ function M.build(task)
if if
not M.check({ not M.check({
error = function(msg) error = function(msg)
task:notify_error(msg:gsub("[{}]", "`")) task:error(msg:gsub("[{}]", "`"))
end, end,
warn = function(msg) warn = function(msg)
task:notify_warn(msg) task:warn(msg)
end, end,
ok = function(msg) end, ok = function(msg) end,
}) })
then then
task:notify_warn({ task:log({
"", "",
"This plugin requires `luarocks`. Try one of the following:", "This plugin requires `luarocks`. Try one of the following:",
" - fix your `luarocks` installation", " - fix your `luarocks` installation",

View File

@ -30,8 +30,9 @@ M.colors = {
Button = "CursorLine", Button = "CursorLine",
ButtonActive = "Visual", ButtonActive = "Visual",
TaskOutput = "MsgArea", -- task output TaskOutput = "MsgArea", -- task output
TaskError = "ErrorMsg", -- task errors Error = "DiagnosticError", -- task errors
TaskWarning = "WarningMsg", -- task errors Warning = "DiagnosticWarn", -- task errors
Info = "DiagnosticInfo", -- task errors
Dir = "@markup.link", -- directory Dir = "@markup.link", -- directory
Url = "@markup.link", -- url Url = "@markup.link", -- url
Bold = { bold = true }, Bold = { bold = true },

View File

@ -354,6 +354,31 @@ end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
function M:diagnostics(plugin) function M:diagnostics(plugin)
local skip = false
for _, task in ipairs(plugin._.tasks or {}) do
if task:is_running() then
self:diagnostic({
severity = vim.diagnostic.severity.WARN,
message = task.name .. (task:status() and (": " .. task:status()) or ""),
})
skip = true
elseif task:has_errors() then
self:diagnostic({
message = task.name .. " failed",
severity = vim.diagnostic.severity.ERROR,
})
skip = true
elseif task:has_warnings() then
self:diagnostic({
message = task.name .. " warning",
severity = vim.diagnostic.severity.WARN,
})
skip = true
end
end
if skip then
return
end
if plugin._.updated then if plugin._.updated then
if plugin._.updated.from == plugin._.updated.to then if plugin._.updated.from == plugin._.updated.to then
self:diagnostic({ self:diagnostic({
@ -383,24 +408,6 @@ function M:diagnostics(plugin)
}) })
end end
end end
for _, task in ipairs(plugin._.tasks or {}) do
if task:is_running() then
self:diagnostic({
severity = vim.diagnostic.severity.WARN,
message = task.name .. (task.status == "" and "" or (": " .. task.status)),
})
elseif task.error then
self:diagnostic({
message = task.name .. " failed",
severity = vim.diagnostic.severity.ERROR,
})
elseif task.warn then
self:diagnostic({
message = task.name .. " warning",
severity = vim.diagnostic.severity.WARN,
})
end
end
end end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
@ -463,24 +470,27 @@ function M:tasks(plugin)
self:append(" " .. math.floor((task:time()) * 100) / 100 .. "ms", "Bold") self:append(" " .. math.floor((task:time()) * 100) / 100 .. "ms", "Bold")
self:nl() self:nl()
end end
if task.error then
self:markdown(task.error, "LazyTaskError", { indent = 6 }) if not task:has_warnings() and task.name == "log" then
end
if task.warn then
self:markdown(task.warn, "LazyTaskWarning", { indent = 6 })
end
if not task.error and not task.warn and task.name == "log" then
self:log(task) self:log(task)
end else
if (self.view:is_selected(plugin) or (task.error or task.warn)) and task.output ~= task.error then local hls = {
self:markdown(vim.trim(task.output), "LazyTaskOutput", { indent = 6 }) [vim.log.levels.ERROR] = "LazyError",
[vim.log.levels.WARN] = "LazyWarning",
[vim.log.levels.INFO] = "LazyInfo",
}
for _, msg in ipairs(task:get_log()) do
if task:has_warnings() or self.view:is_selected(plugin) then
self:markdown(msg.msg, hls[msg.level] or "LazyTaskOutput", { indent = 6 })
end
end
end end
end end
end end
---@param task LazyTask ---@param task LazyTask
function M:log(task) function M:log(task)
local log = vim.trim(task.output) local log = vim.trim(task:output())
if log ~= "" then if log ~= "" then
local lines = vim.split(log, "\n") local lines = vim.split(log, "\n")
for _, line in ipairs(lines) do for _, line in ipairs(lines) do

View File

@ -17,7 +17,7 @@ return {
{ {
filter = function(plugin) filter = function(plugin)
return has_task(plugin, function(task) return has_task(plugin, function(task)
return task.error ~= nil return task:has_errors()
end) end)
end, end,
title = "Failed", title = "Failed",
@ -39,8 +39,7 @@ return {
if task.name ~= "log" then if task.name ~= "log" then
return return
end end
local lines = vim.split(task.output, "\n") for _, line in ipairs(vim.split(task:output(), "\n")) do
for _, line in ipairs(lines) do
if line:find("^%w+ %S+!:") then if line:find("^%w+ %S+!:") then
return true return true
end end
@ -71,7 +70,7 @@ return {
{ {
filter = function(plugin) filter = function(plugin)
return has_task(plugin, function(task) return has_task(plugin, function(task)
return task.name == "log" and vim.trim(task.output) ~= "" return task.name == "log" and vim.trim(task:output()) ~= ""
end) end)
end, end,
title = "Log", title = "Log",
@ -89,7 +88,7 @@ return {
title = "Not Installed", title = "Not Installed",
}, },
{ {
filter = function (plugin) filter = function(plugin)
return plugin._.outdated return plugin._.outdated
end, end,
title = "Outdated", title = "Outdated",