mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-23 13:00:04 +08:00
180 lines
4.7 KiB
Lua
180 lines
4.7 KiB
Lua
-- Credit: https://gist.github.com/runiq/31aa5c4bf00f8e0843cd267880117201
|
|
--
|
|
|
|
local M = {}
|
|
|
|
---Validates args for `throttle()` and `debounce()`.
|
|
local function td_validate(fn, ms)
|
|
vim.validate {
|
|
fn = { fn, "f" },
|
|
ms = {
|
|
ms,
|
|
function(v)
|
|
return type(v) == "number" and v > 0
|
|
end,
|
|
"number > 0",
|
|
},
|
|
}
|
|
end
|
|
|
|
--- Throttles a function on the leading edge. Automatically `schedule_wrap()`s.
|
|
---
|
|
--@param fn (function) Function to throttle
|
|
--@param timeout (number) Timeout in ms
|
|
--@returns (function, timer) throttled function and timer. Remember to call
|
|
---`timer:close()` at the end or you will leak memory!
|
|
function M.throttle_leading(fn, ms)
|
|
td_validate(fn, ms)
|
|
local timer = vim.loop.new_timer()
|
|
local running = false
|
|
|
|
local function wrapped_fn(...)
|
|
if not running then
|
|
timer:start(ms, 0, function()
|
|
running = false
|
|
end)
|
|
running = true
|
|
pcall(vim.schedule_wrap(fn), select(1, ...))
|
|
end
|
|
end
|
|
return wrapped_fn, timer
|
|
end
|
|
|
|
--- Throttles a function on the trailing edge. Automatically
|
|
--- `schedule_wrap()`s.
|
|
---
|
|
--@param fn (function) Function to throttle
|
|
--@param timeout (number) Timeout in ms
|
|
--@param last (boolean, optional) Whether to use the arguments of the last
|
|
---call to `fn` within the timeframe. Default: Use arguments of the first call.
|
|
--@returns (function, timer) Throttled function and timer. Remember to call
|
|
---`timer:close()` at the end or you will leak memory!
|
|
function M.throttle_trailing(fn, ms, last)
|
|
td_validate(fn, ms)
|
|
local timer = vim.loop.new_timer()
|
|
local running = false
|
|
|
|
local wrapped_fn
|
|
if not last then
|
|
function wrapped_fn(...)
|
|
if not running then
|
|
local argv = { ... }
|
|
local argc = select("#", ...)
|
|
|
|
timer:start(ms, 0, function()
|
|
running = false
|
|
pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc))
|
|
end)
|
|
running = true
|
|
end
|
|
end
|
|
else
|
|
local argv, argc
|
|
function wrapped_fn(...)
|
|
argv = { ... }
|
|
argc = select("#", ...)
|
|
|
|
if not running then
|
|
timer:start(ms, 0, function()
|
|
running = false
|
|
pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc))
|
|
end)
|
|
running = true
|
|
end
|
|
end
|
|
end
|
|
return wrapped_fn, timer
|
|
end
|
|
|
|
--- Debounces a function on the leading edge. Automatically `schedule_wrap()`s.
|
|
---
|
|
--@param fn (function) Function to debounce
|
|
--@param timeout (number) Timeout in ms
|
|
--@returns (function, timer) Debounced function and timer. Remember to call
|
|
---`timer:close()` at the end or you will leak memory!
|
|
function M.debounce_leading(fn, ms)
|
|
td_validate(fn, ms)
|
|
local timer = vim.loop.new_timer()
|
|
local running = false
|
|
|
|
local function wrapped_fn(...)
|
|
timer:start(ms, 0, function()
|
|
running = false
|
|
end)
|
|
|
|
if not running then
|
|
running = true
|
|
pcall(vim.schedule_wrap(fn), select(1, ...))
|
|
end
|
|
end
|
|
return wrapped_fn, timer
|
|
end
|
|
|
|
--- Debounces a function on the trailing edge. Automatically
|
|
--- `schedule_wrap()`s.
|
|
---
|
|
--@param fn (function) Function to debounce
|
|
--@param timeout (number) Timeout in ms
|
|
--@param first (boolean, optional) Whether to use the arguments of the first
|
|
---call to `fn` within the timeframe. Default: Use arguments of the last call.
|
|
--@returns (function, timer) Debounced function and timer. Remember to call
|
|
---`timer:close()` at the end or you will leak memory!
|
|
function M.debounce_trailing(fn, ms, first)
|
|
td_validate(fn, ms)
|
|
local timer = vim.loop.new_timer()
|
|
local wrapped_fn
|
|
|
|
if not first then
|
|
function wrapped_fn(...)
|
|
local argv = { ... }
|
|
local argc = select("#", ...)
|
|
|
|
timer:start(ms, 0, function()
|
|
pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc))
|
|
end)
|
|
end
|
|
else
|
|
local argv, argc
|
|
function wrapped_fn(...)
|
|
argv = argv or { ... }
|
|
argc = argc or select("#", ...)
|
|
|
|
timer:start(ms, 0, function()
|
|
pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc))
|
|
end)
|
|
end
|
|
end
|
|
return wrapped_fn, timer
|
|
end
|
|
|
|
--- Test deferment methods (`{throttle,debounce}_{leading,trailing}()`).
|
|
---
|
|
--@param bouncer (string) Bouncer function to test
|
|
--@param ms (number, optional) Timeout in ms, default 2000.
|
|
--@param firstlast (bool, optional) Whether to use the 'other' fn call
|
|
---strategy.
|
|
function M.test_defer(bouncer, ms, firstlast)
|
|
local bouncers = {
|
|
tl = M.throttle_leading,
|
|
tt = M.throttle_trailing,
|
|
dl = M.debounce_leading,
|
|
dt = M.debounce_trailing,
|
|
}
|
|
|
|
local timeout = ms or 2000
|
|
|
|
local bounced = bouncers[bouncer](function(i)
|
|
vim.cmd('echom "' .. bouncer .. ": " .. i .. '"')
|
|
end, timeout, firstlast)
|
|
|
|
for i, _ in ipairs { 1, 2, 3, 4, 5 } do
|
|
bounced(i)
|
|
vim.schedule(function()
|
|
vim.cmd("echom " .. i)
|
|
end)
|
|
vim.fn.call("wait", { 1000, "v:false" })
|
|
end
|
|
end
|
|
|
|
return M
|