diff options
Diffstat (limited to 'lua/telescope/debounce.lua')
| -rw-r--r-- | lua/telescope/debounce.lua | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/lua/telescope/debounce.lua b/lua/telescope/debounce.lua new file mode 100644 index 0000000..329cdc5 --- /dev/null +++ b/lua/telescope/debounce.lua @@ -0,0 +1,179 @@ +-- 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(ms) + return type(ms) == 'number' and ms > 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 |
