refactor: address emmylua diagnostics
This commit is contained in:
+108
-145
@@ -293,167 +293,130 @@ function M.is_list_or_nil(val, t)
|
||||
end
|
||||
end
|
||||
|
||||
---@class ow.Util.Debouncer
|
||||
---@field package _fn fun(...)
|
||||
---@field package _delay integer
|
||||
---@field package _timer uv.uv_timer_t
|
||||
---@field package _gen integer
|
||||
---@field package _fired_gen integer
|
||||
---@field package _args? table
|
||||
---@field package _cb_main fun()
|
||||
---@field package _cb_uv fun()
|
||||
local Debouncer = {}
|
||||
Debouncer.__index = Debouncer
|
||||
|
||||
---@param fn fun(...)
|
||||
---@param delay integer
|
||||
---@return ow.Util.Debouncer
|
||||
function Debouncer.new(fn, delay)
|
||||
local self = setmetatable({
|
||||
_fn = fn,
|
||||
_delay = delay,
|
||||
_timer = assert(vim.uv.new_timer()),
|
||||
_gen = 0,
|
||||
_fired_gen = 0,
|
||||
_args = nil,
|
||||
}, Debouncer)
|
||||
self._cb_main = vim.schedule_wrap(function()
|
||||
-- Identity check: the libuv fire may have been superseded by a
|
||||
-- re-arm or a cancel between the timer firing and this scheduled
|
||||
-- callback running.
|
||||
if self._fired_gen ~= self._gen or self._args == nil then
|
||||
return
|
||||
end
|
||||
local args = self._args
|
||||
self._args = nil
|
||||
self._fn(vim.F.unpack_len(args))
|
||||
end)
|
||||
self._cb_uv = function()
|
||||
self._fired_gen = self._gen
|
||||
self._cb_main()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Debouncer:__call(...)
|
||||
self._args = vim.F.pack_len(...)
|
||||
self._gen = self._gen + 1
|
||||
self._timer:start(self._delay, 0, self._cb_uv)
|
||||
end
|
||||
|
||||
function Debouncer:cancel()
|
||||
self._timer:stop()
|
||||
self._args = nil
|
||||
end
|
||||
|
||||
function Debouncer:flush()
|
||||
if self._args == nil then
|
||||
return
|
||||
end
|
||||
self._timer:stop()
|
||||
local args = self._args
|
||||
self._args = nil
|
||||
self._fn(vim.F.unpack_len(args))
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
function Debouncer:pending()
|
||||
return self._args ~= nil
|
||||
end
|
||||
|
||||
function Debouncer:close()
|
||||
self._timer:stop()
|
||||
if not self._timer:is_closing() then
|
||||
self._timer:close()
|
||||
end
|
||||
self._args = nil
|
||||
end
|
||||
---@class ow.Util.DebounceHandle
|
||||
---@field cancel fun()
|
||||
---@field flush fun()
|
||||
---@field pending fun(): boolean
|
||||
---@field close fun()
|
||||
|
||||
---@generic F: fun(...)
|
||||
---@param fn F
|
||||
---@param delay integer
|
||||
---@return F | ow.Util.Debouncer
|
||||
---@return F, ow.Util.DebounceHandle
|
||||
function M.debounce(fn, delay)
|
||||
return Debouncer.new(fn, delay)
|
||||
end
|
||||
local timer = assert(vim.uv.new_timer())
|
||||
local args ---@type table?
|
||||
local gen = 0
|
||||
local fired_gen = 0
|
||||
|
||||
---@class ow.Util.KeyedDebouncer<T>
|
||||
---@field package _fn fun(key: T, ...)
|
||||
---@field package _delay integer
|
||||
---@field package _slots table<T, ow.Util.Debouncer>
|
||||
local KeyedDebouncer = {}
|
||||
KeyedDebouncer.__index = KeyedDebouncer
|
||||
local cb_main = vim.schedule_wrap(function()
|
||||
-- Identity check: the libuv fire may have been superseded by a
|
||||
-- re-arm or a cancel between the timer firing and this scheduled
|
||||
-- callback running.
|
||||
if fired_gen ~= gen or args == nil then
|
||||
return
|
||||
end
|
||||
local a = args
|
||||
args = nil
|
||||
fn(vim.F.unpack_len(a))
|
||||
end)
|
||||
|
||||
---@generic T
|
||||
---@param fn fun(key: T, ...)
|
||||
---@param delay integer
|
||||
---@return ow.Util.KeyedDebouncer<T>
|
||||
function KeyedDebouncer.new(fn, delay)
|
||||
return setmetatable({
|
||||
_fn = fn,
|
||||
_delay = delay,
|
||||
_slots = {},
|
||||
}, KeyedDebouncer)
|
||||
end
|
||||
|
||||
---@generic T
|
||||
---@param self ow.Util.KeyedDebouncer<T>
|
||||
---@param key T
|
||||
function KeyedDebouncer:__call(key, ...)
|
||||
local slot = self._slots[key]
|
||||
if not slot then
|
||||
slot = Debouncer.new(function(...)
|
||||
self._fn(key, ...)
|
||||
end, self._delay)
|
||||
self._slots[key] = slot
|
||||
local cb_uv = function()
|
||||
fired_gen = gen
|
||||
cb_main()
|
||||
end
|
||||
slot(...)
|
||||
end
|
||||
|
||||
---@generic T
|
||||
---@param self ow.Util.KeyedDebouncer<T>
|
||||
---@param key T
|
||||
function KeyedDebouncer:cancel(key)
|
||||
local slot = self._slots[key]
|
||||
if slot then
|
||||
slot:close()
|
||||
self._slots[key] = nil
|
||||
local function call(...)
|
||||
args = vim.F.pack_len(...)
|
||||
gen = gen + 1
|
||||
timer:start(delay, 0, cb_uv)
|
||||
end
|
||||
|
||||
return call,
|
||||
{
|
||||
cancel = function()
|
||||
timer:stop()
|
||||
args = nil
|
||||
end,
|
||||
flush = function()
|
||||
if args == nil then
|
||||
return
|
||||
end
|
||||
timer:stop()
|
||||
local a = args
|
||||
args = nil
|
||||
fn(vim.F.unpack_len(a))
|
||||
end,
|
||||
pending = function()
|
||||
return args ~= nil
|
||||
end,
|
||||
close = function()
|
||||
timer:stop()
|
||||
if not timer:is_closing() then
|
||||
timer:close()
|
||||
end
|
||||
args = nil
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
---@generic T
|
||||
---@param self ow.Util.KeyedDebouncer<T>
|
||||
---@param key T
|
||||
function KeyedDebouncer:flush(key)
|
||||
local slot = self._slots[key]
|
||||
if slot then
|
||||
slot:flush()
|
||||
end
|
||||
end
|
||||
---@class ow.Util.KeyedDebounceHandle<K>
|
||||
---@field cancel fun(key: K)
|
||||
---@field flush fun(key: K)
|
||||
---@field pending fun(key: K): boolean
|
||||
---@field close fun()
|
||||
|
||||
---@generic T
|
||||
---@param self ow.Util.KeyedDebouncer<T>
|
||||
---@param key T
|
||||
---@return boolean
|
||||
function KeyedDebouncer:pending(key)
|
||||
local slot = self._slots[key]
|
||||
return slot ~= nil and slot:pending()
|
||||
end
|
||||
|
||||
function KeyedDebouncer:close()
|
||||
for _, slot in pairs(self._slots) do
|
||||
slot:close()
|
||||
end
|
||||
self._slots = {}
|
||||
end
|
||||
|
||||
---@diagnostic disable-next-line: undefined-doc-name
|
||||
---@generic T, F: fun(key: T, ...)
|
||||
---@generic K, F: fun(key: K, ...)
|
||||
---@param fn F
|
||||
---@param delay integer
|
||||
---@return F | ow.Util.KeyedDebouncer<T>
|
||||
---@return F, ow.Util.KeyedDebounceHandle<K>
|
||||
function M.keyed_debounce(fn, delay)
|
||||
return KeyedDebouncer.new(fn, delay)
|
||||
---@type table<K, { call: fun(...), handle: ow.Util.DebounceHandle }>
|
||||
local slots = {}
|
||||
|
||||
local function call(key, ...)
|
||||
local t = type(key)
|
||||
assert(
|
||||
t == "string" or t == "number" or t == "boolean",
|
||||
"key must be a primitive (string, number, boolean)"
|
||||
)
|
||||
local slot = slots[key]
|
||||
if not slot then
|
||||
local c, h = M.debounce(function(...)
|
||||
fn(key, ...)
|
||||
end, delay)
|
||||
slot = { call = c, handle = h }
|
||||
slots[key] = slot
|
||||
end
|
||||
slot.call(...)
|
||||
end
|
||||
|
||||
return call,
|
||||
{
|
||||
cancel = function(key)
|
||||
local slot = slots[key]
|
||||
if slot then
|
||||
slot.handle.close()
|
||||
slots[key] = nil
|
||||
end
|
||||
end,
|
||||
flush = function(key)
|
||||
local slot = slots[key]
|
||||
if slot then
|
||||
slot.handle.flush()
|
||||
end
|
||||
end,
|
||||
pending = function(key)
|
||||
local slot = slots[key]
|
||||
return slot ~= nil and slot.handle.pending()
|
||||
end,
|
||||
close = function()
|
||||
for _, slot in pairs(slots) do
|
||||
slot.handle.close()
|
||||
end
|
||||
slots = {}
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
function M.get_hl_source(name)
|
||||
|
||||
Reference in New Issue
Block a user