mirror of
https://github.com/folke/snacks.nvim
synced 2025-12-23 08:47:57 +00:00
perf(notifier): index queue by id
This commit is contained in:
parent
f59237f1dc
commit
5df4394c60
1 changed files with 55 additions and 48 deletions
|
|
@ -31,16 +31,17 @@ local M = setmetatable({}, {
|
||||||
|
|
||||||
--- Notification object
|
--- Notification object
|
||||||
---@class snacks.notifier.Notif: snacks.notifier.Notif.opts
|
---@class snacks.notifier.Notif: snacks.notifier.Notif.opts
|
||||||
---@field msg string
|
|
||||||
---@field id number|string
|
---@field id number|string
|
||||||
|
---@field msg string
|
||||||
---@field win? snacks.win
|
---@field win? snacks.win
|
||||||
---@field icon string
|
---@field icon string
|
||||||
---@field level snacks.notifier.level
|
---@field level snacks.notifier.level
|
||||||
---@field timeout number
|
---@field timeout number
|
||||||
---@field dirty? boolean
|
---@field dirty? boolean
|
||||||
---@field shown? number timestamp in ms
|
---@field added number timestamp with nano precision
|
||||||
---@field added number timestamp in ms
|
---@field updated number timestamp with nano precision
|
||||||
---@field added_hr number hrtime in ms
|
---@field shown? number timestamp with nano precision
|
||||||
|
---@field hidden? number timestamp with nano precision
|
||||||
---@field layout? { top?: number, size: { width: number, height: number }}
|
---@field layout? { top?: number, size: { width: number, height: number }}
|
||||||
|
|
||||||
--- ### Rendering
|
--- ### Rendering
|
||||||
|
|
@ -93,7 +94,8 @@ local defaults = {
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class snacks.notifier.Class
|
---@class snacks.notifier.Class
|
||||||
---@field queue snacks.notifier.Notif[]
|
---@field queue table<string|number, snacks.notifier.Notif>
|
||||||
|
---@field sorted? snacks.notifier.Notif[]
|
||||||
---@field opts snacks.notifier.Config
|
---@field opts snacks.notifier.Config
|
||||||
---@field dirty boolean
|
---@field dirty boolean
|
||||||
local N = {}
|
local N = {}
|
||||||
|
|
@ -168,6 +170,11 @@ local function hl(name, level)
|
||||||
return "SnacksNotifier" .. name .. (level and (level:sub(1, 1):upper() .. level:sub(2):lower()) or "")
|
return "SnacksNotifier" .. name .. (level and (level:sub(1, 1):upper() .. level:sub(2):lower()) or "")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function ts()
|
||||||
|
local ret = assert(vim.uv.clock_gettime("realtime"))
|
||||||
|
return ret.sec + ret.nsec / 1e9
|
||||||
|
end
|
||||||
|
|
||||||
local _id = 0
|
local _id = 0
|
||||||
|
|
||||||
local function next_id()
|
local function next_id()
|
||||||
|
|
@ -181,7 +188,6 @@ function N.new(opts)
|
||||||
local self = setmetatable({}, { __index = N })
|
local self = setmetatable({}, { __index = N })
|
||||||
self.opts = Snacks.config.get("notifier", defaults, opts)
|
self.opts = Snacks.config.get("notifier", defaults, opts)
|
||||||
self.queue = {}
|
self.queue = {}
|
||||||
self.dirty = false
|
|
||||||
self:init()
|
self:init()
|
||||||
self:start()
|
self:start()
|
||||||
return self
|
return self
|
||||||
|
|
@ -208,7 +214,7 @@ function N:start()
|
||||||
100,
|
100,
|
||||||
100,
|
100,
|
||||||
vim.schedule_wrap(function()
|
vim.schedule_wrap(function()
|
||||||
if #self.queue == 0 then
|
if not next(self.queue) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
xpcall(function()
|
xpcall(function()
|
||||||
|
|
@ -227,66 +233,66 @@ end
|
||||||
|
|
||||||
---@param opts snacks.notifier.Notif.opts
|
---@param opts snacks.notifier.Notif.opts
|
||||||
function N:add(opts)
|
function N:add(opts)
|
||||||
local now = vim.uv.hrtime() / 1e6
|
local now = ts()
|
||||||
local notif = vim.deepcopy(opts) --[[@as snacks.notifier.Notif]]
|
local notif = vim.deepcopy(opts) --[[@as snacks.notifier.Notif]]
|
||||||
|
|
||||||
notif.msg = notif.msg or ""
|
notif.msg = notif.msg or ""
|
||||||
notif.id = notif.id or next_id()
|
notif.id = notif.id or next_id()
|
||||||
notif.level = normlevel(notif.level)
|
notif.level = normlevel(notif.level)
|
||||||
notif.icon = notif.icon or self.opts.icons[notif.level]
|
notif.icon = notif.icon or self.opts.icons[notif.level]
|
||||||
notif.timeout = notif.timeout or self.opts.timeout
|
notif.timeout = notif.timeout or self.opts.timeout
|
||||||
notif.added = os.time()
|
notif.added = now
|
||||||
notif.added_hr = now
|
|
||||||
if opts.id then
|
if opts.id and self.queue[opts.id] then
|
||||||
for i, n in ipairs(self.queue) do
|
local n = self.queue[opts.id] --[[@as snacks.notifier.Notif]]
|
||||||
if n.id == notif.id then
|
notif.added = n.added
|
||||||
notif.shown = n.shown and now or nil -- reset shown time
|
notif.updated = now
|
||||||
notif.win = n.win
|
notif.shown = n.shown and now or nil -- reset shown time
|
||||||
notif.layout = n.layout
|
notif.win = n.win
|
||||||
notif.dirty = true
|
notif.layout = n.layout
|
||||||
self.queue[i] = notif
|
notif.dirty = true
|
||||||
return notif.id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
table.insert(self.queue, notif)
|
self.sorted = nil
|
||||||
self.dirty = true
|
|
||||||
|
self.queue[notif.id] = notif
|
||||||
return notif.id
|
return notif.id
|
||||||
end
|
end
|
||||||
|
|
||||||
function N:update()
|
function N:update()
|
||||||
local now = vim.uv.now()
|
local now = ts()
|
||||||
--- Cleanup queue
|
--- Cleanup queue
|
||||||
---@param notif snacks.notifier.Notif
|
for id, notif in pairs(self.queue) do
|
||||||
self.queue = vim.tbl_filter(function(notif)
|
|
||||||
local timeout = notif.timeout or self.opts.timeout
|
local timeout = notif.timeout or self.opts.timeout
|
||||||
local keep = not notif.shown -- not shown yet
|
local keep = not notif.shown -- not shown yet
|
||||||
or (notif.win and notif.win:win_valid() and vim.api.nvim_get_current_win() == notif.win.win) -- current window
|
or (notif.win and notif.win:win_valid() and vim.api.nvim_get_current_win() == notif.win.win) -- current window
|
||||||
or (notif.keep and notif.keep(notif)) -- custom keep
|
or (notif.keep and notif.keep(notif)) -- custom keep
|
||||||
or (self.opts.keep and self.opts.keep(notif)) -- global keep
|
or (self.opts.keep and self.opts.keep(notif)) -- global keep
|
||||||
or (notif.shown + timeout > now) -- not timed out
|
or (notif.shown + timeout / 1e3 > now) -- not timed out
|
||||||
if not keep and notif.win then
|
if not keep then
|
||||||
notif.win:close()
|
self:hide(id)
|
||||||
notif.win = nil
|
|
||||||
self.dirty = true
|
|
||||||
end
|
end
|
||||||
return keep
|
|
||||||
end, self.queue)
|
|
||||||
if self.dirty then
|
|
||||||
self:sort()
|
|
||||||
end
|
end
|
||||||
self.dirty = false
|
self.sorted = self.sorted or self:sort()
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param id? number|string
|
---@param id? number|string
|
||||||
function N:hide(id)
|
function N:hide(id)
|
||||||
---@param notif snacks.notifier.Notif
|
if not id then
|
||||||
self.queue = vim.tbl_filter(function(notif)
|
for i in pairs(self.queue) do
|
||||||
if notif.win and id == nil or notif.id == id then
|
self:hide(i)
|
||||||
notif.win:close()
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
return true
|
return
|
||||||
end, self.queue)
|
end
|
||||||
|
local notif = self.queue[id]
|
||||||
|
if not notif then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.queue[id], self.sorted = nil, nil
|
||||||
|
notif.hidden = ts()
|
||||||
|
if notif.win then
|
||||||
|
notif.win:hide()
|
||||||
|
notif.win = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param value number
|
---@param value number
|
||||||
|
|
@ -384,13 +390,13 @@ function N:render(notif)
|
||||||
end
|
end
|
||||||
|
|
||||||
function N:sort()
|
function N:sort()
|
||||||
table.sort(self.queue, function(a, b)
|
---@type snacks.notifier.Notif[]
|
||||||
|
local ret = vim.tbl_values(self.queue)
|
||||||
|
table.sort(ret, function(a, b)
|
||||||
for _, key in ipairs(self.opts.sort) do
|
for _, key in ipairs(self.opts.sort) do
|
||||||
local function v(n)
|
local function v(n)
|
||||||
if key == "level" then
|
if key == "level" then
|
||||||
return 10 - vim.log.levels[n[key]:upper()]
|
return 10 - vim.log.levels[n[key]:upper()]
|
||||||
elseif key == "added" then
|
|
||||||
return n.added_hr
|
|
||||||
end
|
end
|
||||||
return n[key]
|
return n[key]
|
||||||
end
|
end
|
||||||
|
|
@ -401,6 +407,7 @@ function N:sort()
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end)
|
end)
|
||||||
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
function N:layout()
|
function N:layout()
|
||||||
|
|
@ -432,7 +439,7 @@ function N:layout()
|
||||||
|
|
||||||
local shown = 0
|
local shown = 0
|
||||||
local max_visible = vim.o.lines * (self.opts.height.min + 2)
|
local max_visible = vim.o.lines * (self.opts.height.min + 2)
|
||||||
for _, notif in ipairs(self.queue) do
|
for _, notif in ipairs(assert(self.sorted)) do
|
||||||
local skip = shown >= max_visible
|
local skip = shown >= max_visible
|
||||||
if not skip then
|
if not skip then
|
||||||
if not notif.win or notif.dirty or not notif.win:buf_valid() or type(notif.opts) == "function" then
|
if not notif.win or notif.dirty or not notif.win:buf_valid() or type(notif.opts) == "function" then
|
||||||
|
|
@ -448,7 +455,7 @@ function N:layout()
|
||||||
mark(notif.layout.top, notif.layout.size.height, false)
|
mark(notif.layout.top, notif.layout.size.height, false)
|
||||||
notif.win.opts.row = notif.layout.top - 1
|
notif.win.opts.row = notif.layout.top - 1
|
||||||
notif.win.opts.col = vim.o.columns - notif.layout.size.width - self.opts.margin.right
|
notif.win.opts.col = vim.o.columns - notif.layout.size.width - self.opts.margin.right
|
||||||
notif.shown = notif.shown or vim.uv.now()
|
notif.shown = notif.shown or ts()
|
||||||
notif.win:show()
|
notif.win:show()
|
||||||
notif.win:update()
|
notif.win:update()
|
||||||
elseif notif.win then
|
elseif notif.win then
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue