From 1142f46a27358c8f48023382389a8b31c9628b6b Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Thu, 20 Feb 2025 09:26:04 +0100 Subject: [PATCH] perf(explorer): only update tree when diagnostics actually changed --- lua/snacks/explorer/diagnostics.lua | 4 +++ lua/snacks/explorer/tree.lua | 40 +++++++++++++++++++++++++++ lua/snacks/picker/source/explorer.lua | 17 +++++++++--- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/lua/snacks/explorer/diagnostics.lua b/lua/snacks/explorer/diagnostics.lua index 3a1df33d..67290a19 100644 --- a/lua/snacks/explorer/diagnostics.lua +++ b/lua/snacks/explorer/diagnostics.lua @@ -6,6 +6,8 @@ function M.update(cwd) local Tree = require("snacks.explorer.tree") local node = Tree:find(cwd) + local snapshot = Tree:snapshot(node, { "severity" }) + Tree:walk(node, function(n) n.severity = nil end, { all = true }) @@ -31,6 +33,8 @@ function M.update(cwd) end end end + + return Tree:changed(node, snapshot) end return M diff --git a/lua/snacks/explorer/tree.lua b/lua/snacks/explorer/tree.lua index d31796f3..8d635c5a 100644 --- a/lua/snacks/explorer/tree.lua +++ b/lua/snacks/explorer/tree.lua @@ -21,6 +21,8 @@ ---@field exclude? string[] globs to exclude ---@field include? string[] globs to exclude +---@alias snacks.picker.explorer.Snapshot {fields: string[], state:table} + local uv = vim.uv or vim.loop local function norm(path) @@ -331,4 +333,42 @@ function Tree:next(cwd, filter, opts) return next or first end +---@param node snacks.picker.explorer.Node +---@param snapshot snacks.picker.explorer.Snapshot +function Tree:changed(node, snapshot) + local old = snapshot.state + local current = self:snapshot(node, snapshot.fields).state + if vim.tbl_count(current) ~= vim.tbl_count(old) then + return true + end + for n, data in pairs(current) do + local prev = old[n] + if not prev then + return true + end + if not vim.deep_equal(prev, data) then + return true + end + end + return false +end + +---@param node snacks.picker.explorer.Node +---@param fields string[] +function Tree:snapshot(node, fields) + ---@type snacks.picker.explorer.Snapshot + local ret = { + state = {}, + fields = fields, + } + Tree:walk(node, function(n) + local data = {} ---@type any[] + for f, field in ipairs(fields) do + data[f] = n[field] + end + ret.state[n] = data + end, { all = true }) + return ret +end + return Tree.new() diff --git a/lua/snacks/picker/source/explorer.lua b/lua/snacks/picker/source/explorer.lua index 847c1fbb..29125132 100644 --- a/lua/snacks/picker/source/explorer.lua +++ b/lua/snacks/picker/source/explorer.lua @@ -72,12 +72,21 @@ function State.new(picker) end) if opts.diagnostics then - picker.list.win:on("DiagnosticChanged", function(_, ev) + local dirty = false + local diag_update = Snacks.util.debounce(function() + dirty = false local p = ref() if p then - require("snacks.explorer.diagnostics").update(p:cwd()) - p.list:set_target() - p:find() + if require("snacks.explorer.diagnostics").update(p:cwd()) then + p.list:set_target() + p:find() + end + end + end, { ms = 200 }) + picker.list.win:on({ "InsertLeave", "DiagnosticChanged" }, function(_, ev) + dirty = dirty or ev.event == "DiagnosticChanged" + if vim.fn.mode() == "n" and dirty then + diag_update() end end) end