feat(explorer): added diagnostics file/directory status

This commit is contained in:
Folke Lemaitre 2025-02-07 08:45:38 +01:00
parent cc1b6c77aa
commit 7f1b60d557
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
7 changed files with 83 additions and 8 deletions

View file

@ -0,0 +1,36 @@
---@diagnostic disable: missing-fields
local M = {}
---@param cwd string
function M.update(cwd)
local Tree = require("snacks.explorer.tree")
local node = Tree:find(cwd)
Tree:walk(node, function(n)
n.severity = nil
end, { all = true })
local diags = vim.diagnostic.get()
---@param path string
---@param diag vim.Diagnostic
local function add(path, diag)
local n = Tree:find(path)
local severity = tonumber(diag.severity) or vim.diagnostic.severity.INFO
n.severity = math.min(n.severity or severity, severity)
end
for _, diag in ipairs(diags) do
local path = diag.bufnr and vim.api.nvim_buf_get_name(diag.bufnr)
path = path and path ~= "" and vim.fs.normalize(path) or nil
if path then
add(path, diag)
add(cwd, diag)
for dir in Snacks.picker.util.parents(path, cwd) do
add(dir, diag)
end
end
end
end
return M

View file

@ -12,6 +12,7 @@
---@field last? boolean child of the parent ---@field last? boolean child of the parent
---@field utime? number ---@field utime? number
---@field children table<string, snacks.picker.explorer.Node> ---@field children table<string, snacks.picker.explorer.Node>
---@field severity? number
local uv = vim.uv or vim.loop local uv = vim.uv or vim.loop

View file

@ -60,7 +60,9 @@ function M.refresh()
if not picker.list.target then if not picker.list.target then
picker.list:set_target() picker.list:set_target()
end end
picker:find() vim.schedule(function()
picker:find()
end)
end end
end) end)
end end

View file

@ -151,6 +151,8 @@ local defaults = {
severity = { severity = {
icons = true, -- show severity icons icons = true, -- show severity icons
level = false, -- show severity level level = false, -- show severity level
---@type "left"|"right"
pos = "left", -- position of the diagnostics
}, },
}, },
---@class snacks.picker.previewers.Config ---@class snacks.picker.previewers.Config

View file

@ -42,6 +42,8 @@ M.buffers = {
---@field tree? boolean show the file tree (default: true) ---@field tree? boolean show the file tree (default: true)
---@field git_status? boolean show git status (default: true) ---@field git_status? boolean show git status (default: true)
---@field git_status_open? boolean show recursive git status for open directories ---@field git_status_open? boolean show recursive git status for open directories
---@field diagnostics? boolean show diagnostics
---@field diagnostics_open? boolean show recursive diagnostics for open directories
---@field watch? boolean watch for file changes ---@field watch? boolean watch for file changes
M.explorer = { M.explorer = {
finder = "explorer", finder = "explorer",
@ -49,6 +51,8 @@ M.explorer = {
supports_live = true, supports_live = true,
tree = true, tree = true,
watch = true, watch = true,
diagnostics = true,
diagnostics_open = false,
git_status = true, git_status = true,
git_status_open = false, git_status_open = false,
follow_file = true, follow_file = true,
@ -59,7 +63,10 @@ M.explorer = {
-- to show the explorer to the right, add the below to -- to show the explorer to the right, add the below to
-- your config under `opts.picker.sources.explorer` -- your config under `opts.picker.sources.explorer`
-- layout = { layout = { position = "right" } }, -- layout = { layout = { position = "right" } },
formatters = { file = { filename_only = true } }, formatters = {
file = { filename_only = true },
severity = { pos = "right" },
},
matcher = { sort_empty = false, fuzzy = false }, matcher = { sort_empty = false, fuzzy = false },
config = function(opts) config = function(opts)
return require("snacks.picker.source.explorer").setup(opts) return require("snacks.picker.source.explorer").setup(opts)

View file

@ -15,14 +15,27 @@ function M.severity(item, picker)
local lower = severity:lower() local lower = severity:lower()
local cap = severity:sub(1, 1):upper() .. lower:sub(2) local cap = severity:sub(1, 1):upper() .. lower:sub(2)
if picker.opts.formatters.severity.pos == "right" then
return {
{
col = 0,
virt_text = { { picker.opts.icons.diagnostics[cap], "Diagnostic" .. cap } },
virt_text_pos = "right_align",
hl_mode = "combine",
},
}
end
if picker.opts.formatters.severity.icons then if picker.opts.formatters.severity.icons then
ret[#ret + 1] = { picker.opts.icons.diagnostics[cap], "Diagnostic" .. cap, virtual = true } ret[#ret + 1] = { picker.opts.icons.diagnostics[cap], "Diagnostic" .. cap, virtual = true }
ret[#ret + 1] = { " ", virtual = true } ret[#ret + 1] = { " ", virtual = true }
end end
if picker.opts.formatters.severity.level then if picker.opts.formatters.severity.level then
ret[#ret + 1] = { lower:upper(), "Diagnostic" .. cap, virtual = true } ret[#ret + 1] = { lower:upper(), "Diagnostic" .. cap, virtual = true }
ret[#ret + 1] = { " ", virtual = true } ret[#ret + 1] = { " ", virtual = true }
end end
return ret return ret
end end
@ -120,20 +133,20 @@ function M.file(item, picker)
ret[#ret + 1] = { " ", virtual = true } ret[#ret + 1] = { " ", virtual = true }
end end
if item.severity then
vim.list_extend(ret, M.severity(item, picker))
end
if item.parent then if item.parent then
vim.list_extend(ret, M.tree(item, picker)) vim.list_extend(ret, M.tree(item, picker))
end end
vim.list_extend(ret, M.filename(item, picker))
if item.status then if item.status then
vim.list_extend(ret, M.file_git_status(item, picker)) vim.list_extend(ret, M.file_git_status(item, picker))
end end
if item.severity then
vim.list_extend(ret, M.severity(item, picker))
end
vim.list_extend(ret, M.filename(item, picker))
if item.comment then if item.comment then
table.insert(ret, { item.comment, "SnacksPickerComment" }) table.insert(ret, { item.comment, "SnacksPickerComment" })
table.insert(ret, { " " }) table.insert(ret, { " " })

View file

@ -71,6 +71,15 @@ function State.new(picker)
end end
end) end)
picker.list.win:on("DiagnosticChanged", function(_, ev)
local p = ref()
if p then
require("snacks.explorer.diagnostics").update(p:cwd())
p.list:set_target()
p:find()
end
end)
-- schedule initial follow -- schedule initial follow
if opts.follow_file then if opts.follow_file then
picker.list.win:on({ "WinEnter", "BufEnter" }, function(_, ev) picker.list.win:on({ "WinEnter", "BufEnter" }, function(_, ev)
@ -212,6 +221,10 @@ function M.explorer(opts, ctx)
}) })
end end
if opts.diagnostics then
require("snacks.explorer.diagnostics").update(ctx.filter.cwd)
end
return function(cb) return function(cb)
if state.on_find then if state.on_find then
ctx.picker.matcher.task:on("done", vim.schedule_wrap(state.on_find)) ctx.picker.matcher.task:on("done", vim.schedule_wrap(state.on_find))
@ -232,6 +245,7 @@ function M.explorer(opts, ctx)
status = (not node.dir or not node.open or opts.git_status_open) and node.status or nil, status = (not node.dir or not node.open or opts.git_status_open) and node.status or nil,
last = true, last = true,
type = node.type, type = node.type,
severity = (not node.dir or not node.open or opts.diagnostics_open) and node.severity or nil,
} }
if last[node.parent] then if last[node.parent] then
last[node.parent].last = false last[node.parent].last = false