feat: a gazilion rendering improvements

This commit is contained in:
Folke Lemaitre 2022-11-23 16:12:02 +01:00
parent 00ff59f385
commit a11fc5a0e0
No known key found for this signature in database
GPG Key ID: 41F8B1FBACAE2040
5 changed files with 292 additions and 51 deletions

View File

@ -1,11 +1,14 @@
local M = {} local M = {}
M.colors = { M.colors = {
Error = "Error", Error = "ErrorMsg",
H1 = "Title", H1 = "Title",
H2 = "Title", H2 = "Bold",
Muted = "Comment", Muted = "Comment",
Normal = "NormalFloat", Normal = "NormalFloat",
Commit = "@variable.builtin",
Key = "@comment",
Value = "@string",
ProgressDone = { ProgressDone = {
bold = true, bold = true,
default = true, default = true,

View File

@ -1,5 +1,6 @@
local View = require("lazy.view") local View = require("lazy.view")
local Manager = require("lazy.manager") local Manager = require("lazy.manager")
local Util = require("lazy.core.util")
local M = {} local M = {}
@ -18,6 +19,10 @@ M.commands = {
clean = function() clean = function()
Manager.clean({ clear = true, show = true }) Manager.clean({ clear = true, show = true })
end, end,
clear = function()
Manager.clear()
View.show()
end,
install = function() install = function()
Manager.install({ clear = true, show = true }) Manager.install({ clear = true, show = true })
end, end,
@ -27,6 +32,9 @@ M.commands = {
show = function() show = function()
View.show() View.show()
end, end,
docs = function()
Manager.docs({ clear = true, show = true })
end,
sync = function() sync = function()
Manager.update({ clear = true, show = true }) Manager.update({ clear = true, show = true })
Manager.install({ show = true }) Manager.install({ show = true })

View File

@ -12,6 +12,7 @@ function M.show()
require("lazy.view.colors").setup() require("lazy.view.colors").setup()
if M._buf and vim.api.nvim_buf_is_valid(M._buf) then if M._buf and vim.api.nvim_buf_is_valid(M._buf) then
vim.api.nvim_win_set_cursor(M._win, { 1, 0 })
return return
end end
@ -28,6 +29,7 @@ function M.show()
opts.row = (vim.o.lines - opts.height) / 2 opts.row = (vim.o.lines - opts.height) / 2
opts.col = (vim.o.columns - opts.width) / 2 opts.col = (vim.o.columns - opts.width) / 2
local win = vim.api.nvim_open_win(buf, true, opts) local win = vim.api.nvim_open_win(buf, true, opts)
M._win = win
vim.api.nvim_set_current_win(win) vim.api.nvim_set_current_win(win)
@ -68,24 +70,104 @@ function M.show()
callback = close, callback = close,
}) })
local render = Util.throttle(30, function() local render = Render.new(buf, win, 2)
local update = Util.throttle(30, function()
vim.bo[buf].modifiable = true vim.bo[buf].modifiable = true
Render.render_plugins(buf, win, 2) render:update()
vim.bo[buf].modifiable = false vim.bo[buf].modifiable = false
vim.cmd.redraw() vim.cmd.redraw()
end) end)
local function get_plugin()
local pos = vim.api.nvim_win_get_cursor(win)
return render:get_plugin(pos[1])
end
vim.keymap.set("n", "<cr>", function()
local plugin = get_plugin()
if plugin then
if render._details == plugin.name then
render._details = nil
else
render._details = plugin.name
end
update()
end
end, {
nowait = true,
buffer = buf,
})
local function open(path)
local plugin = get_plugin()
if plugin then
local url = plugin.uri:gsub("%.git$", "")
if Util.file_exists(url) then
url = "https://github.com/" .. plugin[1]
end
Util.open(url .. path)
end
end
M.keys(buf, {
["%s(" .. string.rep("[a-z0-9]", 7) .. ")%s"] = function(hash)
open("/commit/" .. hash)
end,
["%s(" .. string.rep("[a-z0-9]", 7) .. ")$"] = function(hash)
open("/commit/" .. hash)
end,
["^(" .. string.rep("[a-z0-9]", 7) .. ")%s"] = function(hash)
open("/commit/" .. hash)
end,
["#(%d+)"] = function(issue)
open("/issues/" .. issue)
end,
["README.md"] = function()
local plugin = get_plugin()
Util.open(plugin.dir .. "/README.md")
end,
["(https?://%S+)"] = function(url)
Util.open(url)
end,
})
vim.api.nvim_create_autocmd("User", { vim.api.nvim_create_autocmd("User", {
pattern = "LazyRender", pattern = "LazyRender",
callback = function() callback = function()
if not vim.api.nvim_buf_is_valid(buf) then if not vim.api.nvim_buf_is_valid(buf) then
return true return true
end end
update()
render()
end, end,
}) })
render() update()
end
---@param handlers table<string, fun(str:string)>
function M.keys(buf, handlers)
local function map(lhs)
vim.keymap.set("n", lhs, function()
local line = vim.api.nvim_get_current_line()
local pos = vim.api.nvim_win_get_cursor(0)
local col = pos[2] + 1
for pattern, handler in pairs(handlers) do
local from = 1
local to, url
while from do
from, to, url = line:find(pattern, from)
if from and col >= from and col <= to then
return handler(url)
end
if from then
from = to + 1
end
end
end
end, { buffer = buf, silent = true })
end
map("K")
end end
return M return M

View File

@ -5,20 +5,32 @@ local Sections = require("lazy.view.sections")
local Text = require("lazy.view.text") local Text = require("lazy.view.text")
---@alias LazyDiagnostic {row: number, severity: number, message:string}
---@class Render:Text ---@class Render:Text
---@field buf buffer ---@field buf buffer
---@field win window ---@field win window
---@field padding number
---@field plugins LazyPlugin[] ---@field plugins LazyPlugin[]
---@field progress {total:number, done:number} ---@field progress {total:number, done:number}
local M = setmetatable({}, { __index = Text }) ---@field _diagnostics LazyDiagnostic[]
---@field plugin_range table<string, {from: number, to: number}>
---@field _details? string
local M = setmetatable({}, {
__index = Text,
})
function M.render_plugins(buf, win, padding) function M.new(buf, win, padding)
local self = setmetatable({}, { __index = M }) local self = setmetatable({}, { __index = M })
self._lines = {}
self.buf = buf self.buf = buf
self.win = win self.win = win
self.padding = padding self.padding = padding or 0
return self
end
function M:update()
self._lines = {}
self._diagnostics = {}
self.plugin_range = {}
Manager.check_clean() Manager.check_clean()
@ -51,8 +63,28 @@ function M.render_plugins(buf, win, padding)
end end
self:trim() self:trim()
self:render(buf, padding) self:render(self.buf)
return self vim.diagnostic.set(
Config.ns,
self.buf,
---@param diag LazyDiagnostic
vim.tbl_map(function(diag)
diag.col = 0
diag.lnum = diag.row - 1
return diag
end, self._diagnostics),
{ signs = false }
)
end
---@param row number
---@return LazyPlugin?
function M:get_plugin(row)
for name, range in pairs(self.plugin_range) do
if row >= range.from and row <= range.to then
return Config.plugins[name]
end
end
end end
function M:title() function M:title()
@ -102,54 +134,160 @@ function M:section(section)
end end
end end
---@param diag LazyDiagnostic
function M:diagnostic(diag)
diag.row = diag.row or self:row()
diag.severity = diag.severity or vim.diagnostic.severity.INFO
table.insert(self._diagnostics, diag)
end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
function M:plugin(plugin) function M:reason(plugin)
self:append(" - ", "LazySpecial"):append(plugin.name) local reason = vim.deepcopy(plugin.loaded or {})
if plugin.tasks then ---@type string?
for _, task in ipairs(plugin.tasks) do local source = reason.source
if task.running then if source then
self:append(" [" .. task.type .. "] ", "Identifier") ---@type string?
self:append(task.status, "LazyMuted") local modname = source:match("/lua/(.*)%.lua$")
elseif task.error then if modname then
local lines = vim.split(vim.trim(task.error), "\n") modname = modname:gsub("/", ".")
self:append(" [" .. task.type .. "] ", "Identifier") end
for l, line in ipairs(lines) do local pack = source:match("/([^/]-)/lua")
self:append(line, "LazyError") for _, other in pairs(Config.plugins) do
if l ~= #lines then if (modname and other.modname == modname) or (pack and other.pack == pack) then
self:nl() reason.plugin = other.name
end reason.source = nil
end break
elseif task.type == "log" then end
local log = vim.trim(task.output) end
if log ~= "" then if reason.source then
local lines = vim.split(log, "\n") reason.source = modname or reason.source
for l, line in ipairs(lines) do if reason.source == "lua" then
if l == 1 then reason.source = Config.options.plugins
self:nl()
end
local ref, msg, time = line:match("^(%w+) (.*) (%(.*%))$")
self:append(" " .. ref .. " ", "@variable.builtin")
local col = self:col()
self:append(msg)
-- string.gsub
self:append(" " .. time, "Comment")
if l ~= #lines then
self:nl()
end
end
end
end end
end end
end end
self:append(" " .. math.floor((reason.time or 0) / 1e6 * 100) / 100 .. "ms ", "Bold")
for key, value in pairs(reason) do
if key == "require" then
self:append("require", "@function.builtin")
self:append("(", "@punctuation.bracket")
self:append('"' .. value .. '"', "@string")
self:append(")", "@punctuation.bracket")
elseif key ~= "time" then
self:append(key .. " ", "@field")
self:append(value .. " ", "@string")
end
end
end
---@param plugin LazyPlugin
function M:diagnostics(plugin)
if plugin.updated then
if plugin.updated.from == plugin.updated.to then
self:diagnostic({
message = "already up to date",
})
else
self:diagnostic({
message = "updated from " .. plugin.updated.from:sub(1, 7) .. " to " .. plugin.updated.to:sub(1, 7),
})
end
end
for _, task in ipairs(plugin.tasks or {}) do
if task.running then
self:diagnostic({
severity = vim.diagnostic.severity.WARN,
message = task.type .. (task.status == "" and "" or (": " .. task.status)),
})
elseif task.error then
self:diagnostic({
message = task.type .. " failed",
severity = vim.diagnostic.severity.ERROR,
})
end
end
end
---@param plugin LazyPlugin
function M:plugin(plugin)
self:append(" - ", "LazySpecial"):append(plugin.name)
local plugin_start = self:row()
if plugin.loaded then
self:reason(plugin)
end
self:diagnostics(plugin)
self:nl() self:nl()
-- self:details(plugin)
if self._details == plugin.name then
self:details(plugin)
end
self:tasks(plugin)
self.plugin_range[plugin.name] = { from = plugin_start, to = self:row() - 1 }
end
---@param plugin LazyPlugin
function M:tasks(plugin)
for _, task in ipairs(plugin.tasks or {}) do
if task.type == "log" and not task.error then
self:log(task)
elseif task.error or self._details == plugin.name then
if task.error then
self:append(vim.trim(task.error), "LazyError", { indent = 4, prefix = "" })
self:nl()
end
if task.output ~= "" and task.output ~= task.error then
self:append(vim.trim(task.output), "MsgArea", { indent = 4, prefix = "" })
self:nl()
end
end
end
end
---@param task LazyTask
function M:log(task)
local log = vim.trim(task.output)
if log ~= "" then
local lines = vim.split(log, "\n")
for l, line in ipairs(lines) do
local ref, msg, time = line:match("^(%w+) (.*) (%(.*%))$")
self:append(ref .. " ", "LazyCommit", { indent = 6 })
self:append(vim.trim(msg)):highlight({
["#%d+"] = "Number",
["^%S+:"] = "Title",
["^%S+(%(.*%)):"] = "Italic",
["`.-`"] = "@text.literal.markdown_inline",
})
-- string.gsub
self:append(" " .. time, "Comment")
self:nl()
end
self:nl()
end
end end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
function M:details(plugin) function M:details(plugin)
---@type string[][]
local props = {}
table.insert(props, { "uri", (plugin.uri:gsub("%.git$", "")), "@text.reference" })
local git = Util.git_info(plugin.dir) local git = Util.git_info(plugin.dir)
if git then if git then
self:append(git.branch) table.insert(props, { "commit ", git.hash:sub(1, 7), "LazyCommit" })
table.insert(props, { "branch ", git.branch })
end
if Util.file_exists(plugin.dir .. "/README.md") then
table.insert(props, { "readme ", "README.md" })
end
local width = 0
for _, prop in ipairs(props) do
width = math.max(width, #prop[1])
end
for _, prop in ipairs(props) do
self:append(prop[1] .. string.rep(" ", width - #prop[1]), "LazyKey", { indent = 6 })
self:append(prop[2], prop[3] or "LazyValue")
self:nl()
end end
self:nl() self:nl()
end end

View File

@ -10,6 +10,9 @@ local function has_task(plugin, filter)
end end
end end
---@alias LazySection {title:string, filter:fun(plugin:LazyPlugin):boolean?}
---@type LazySection[]
return { return {
{ {
filter = function(plugin) filter = function(plugin)
@ -51,6 +54,13 @@ return {
end, end,
title = "Running", title = "Running",
}, },
{
---@param plugin LazyPlugin
filter = function(plugin)
return plugin.updated and plugin.updated.from ~= plugin.updated.to
end,
title = "Updated",
},
{ {
filter = function(plugin) filter = function(plugin)
return has_task(plugin, function(task) return has_task(plugin, function(task)