perf(picker.list): only re-render when visible items changed

This commit is contained in:
Folke Lemaitre 2025-02-06 14:29:16 +01:00
parent 22da4bd511
commit c72e62ef90
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040

View file

@ -16,6 +16,7 @@
---@field matcher snacks.picker.Matcher matcher for formatting list items ---@field matcher snacks.picker.Matcher matcher for formatting list items
---@field matcher_regex snacks.picker.Matcher matcher for formatting list items ---@field matcher_regex snacks.picker.Matcher matcher for formatting list items
---@field target? {cursor: number, top?: number} ---@field target? {cursor: number, top?: number}
---@field visible snacks.picker.Item[]
local M = {} local M = {}
M.__index = M M.__index = M
@ -37,6 +38,8 @@ local SCROLL_WHEEL_DOWN = Snacks.util.keycode("<ScrollWheelDown>")
---@type table<number, snacks.picker.list> ---@type table<number, snacks.picker.list>
local lists = setmetatable({}, { __mode = "v" }) local lists = setmetatable({}, { __mode = "v" })
local stats = { render = 0, render_full = 0 }
-- track mouse scrolling -- track mouse scrolling
vim.on_key(function(key, typed) vim.on_key(function(key, typed)
key = typed or key key = typed or key
@ -85,6 +88,8 @@ function M.new(picker)
breakindent = true, breakindent = true,
}, },
}) })
self.visible = {}
self.visible_count = 0
self.win = Snacks.win(win_opts) self.win = Snacks.win(win_opts)
self.top, self.cursor = 1, 1 self.top, self.cursor = 1, 1
self.items = {} self.items = {}
@ -300,20 +305,22 @@ end
function M:add(item, sort) function M:add(item, sort)
local idx = #self.items + 1 local idx = #self.items + 1
self.items[idx] = item self.items[idx] = item
-- if the visible items are less than the height, then we need to render
self.dirty = self.dirty or #self.visible < (self.state.height or 50)
if sort ~= false then if sort ~= false then
local added, prev = self.topk:add(item) local added, prev = self.topk:add(item)
if added then if added then
-- check if item is before the last visible item
if not self.dirty and #self.visible > 0 then
self.dirty = self.topk.cmp(item, self.visible[#self.visible])
end
item.match_topk = item.match_tick item.match_topk = item.match_tick
self.dirty = true if prev then
-- replace with previous item, since new item is now in topk
self.items[idx] = prev
prev.match_topk = nil
end
end end
if prev then
-- replace with previous item, since new item is now in topk
self.items[idx] = prev
prev.match_topk = nil
end
end
if not self.dirty then
self.dirty = idx >= self.top and idx <= self.top + (self.state.height or 50)
end end
end end
@ -544,6 +551,7 @@ function M:update_cursorline()
end end
function M:render() function M:render()
stats.render = stats.render + 1
if self.target then if self.target then
self:view(self.target.cursor, self.target.top, false) self:view(self.target.cursor, self.target.top, false)
if not self.picker:is_active() then if not self.picker:is_active() then
@ -555,6 +563,7 @@ function M:render()
local redraw = false local redraw = false
if self.dirty then if self.dirty then
stats.render_full = stats.render_full + 1
local height = self:height() local height = self:height()
self.dirty = false self.dirty = false
vim.api.nvim_win_call(self.win.win, function() vim.api.nvim_win_call(self.win.win, function()
@ -577,9 +586,11 @@ function M:render()
self.matcher_regex:init(search) self.matcher_regex:init(search)
end end
self.visible = {}
-- render items -- render items
for i = self.top, math.min(self:count(), self.top + height - 1) do for i = self.top, math.min(self:count(), self.top + height - 1) do
local item = assert(self:get(i), "item not found") local item = assert(self:get(i), "item not found")
self.visible[i - self.top + 1] = item
local row = self:idx2row(i) local row = self:idx2row(i)
self:_render(item, row) self:_render(item, row)
end end
@ -618,4 +629,12 @@ function M:render()
end end
end end
-- vim.uv.new_timer():start(
-- 500,
-- 500,
-- vim.schedule_wrap(function()
-- Snacks.notify(vim.inspect(stats), { ft = "lua", id = "list_stats" })
-- end)
-- )
return M return M