diff --git a/lua/trouble/util.lua b/lua/trouble/util.lua index de11655e..0850b61f 100644 --- a/lua/trouble/util.lua +++ b/lua/trouble/util.lua @@ -77,11 +77,9 @@ function M.camel(str, sep) ) end ----@alias ThrottleOpts {ms:number, debounce?:boolean, is_running?:fun():boolean} - ---@param opts? {ms?: number, debounce?: boolean}|number ----@param default ThrottleOpts ----@return ThrottleOpts +---@param default Throttle.opts +---@return Throttle.opts function M.throttle_opts(opts, default) opts = opts or {} if type(opts) == "number" then @@ -90,72 +88,73 @@ function M.throttle_opts(opts, default) return vim.tbl_deep_extend("force", default, opts) end +---@alias Throttle.opts {ms:number, debounce?:boolean, is_running?:fun():boolean} + -- throttle with trailing execution ---@generic T: fun() ---@param fn T ----@param opts? ThrottleOpts +---@param opts? Throttle.opts ---@return T function M.throttle(fn, opts) opts = opts or {} opts.ms = opts.ms or 20 - local timer = assert(vim.loop.new_timer()) - local check = assert(vim.loop.new_check()) local last = 0 - local args = {} ---@type any[] - local executing = false - local trailing = false - - local throttle = {} - - check:start(function() - if not throttle.is_running() and not timer:is_active() and trailing then - trailing = false - throttle.schedule() - end - end) + local args = nil ---@type {n?:number}? + local timer = assert(vim.loop.new_timer()) + local pending = false -- from run() till end of fn + local running = false -- from run() till end of fn with is_running() - function throttle.is_running() - return executing or (opts.is_running and opts.is_running()) - end + local t = {} - function throttle.run() - executing = true - last = vim.loop.now() + function t.run() + pending = true + running = true + timer:stop() + last = vim.uv.now() vim.schedule(function() xpcall(function() - local _args = vim.F.unpack_len(args) - -- FIXME: - if not trailing and not timer:is_active() then - args = {} -- clear args so they can be gc'd + if not args then + return M.debug("Empty args. This should not happen.") end - fn(_args) + fn(vim.F.unpack_len(args)) + args = nil end, function(err) vim.schedule(function() M.error(err) end) end) - executing = false + pending = false + t.check() end) end - function throttle.schedule() - local now = vim.loop.now() - local delay = opts.ms - (now - last) - if opts.debounce then - delay = opts.ms + function t.schedule() + local now = vim.uv.now() + local delay = opts.debounce and opts.ms or (opts.ms - (now - last)) + timer:stop() + timer:start(math.max(0, delay), 0, t.run) + end + + function t.check() + if running and not pending and not (opts.is_running and opts.is_running()) then + running = false + if args then -- schedule if there are pending args + t.schedule() + end end - timer:start(math.max(0, delay), 0, throttle.run) end + local check = assert(vim.uv.new_check()) + check:start(t.check) + return function(...) args = vim.F.pack_len(...) + if timer:is_active() and not opts.debounce then return - elseif throttle.is_running() then - trailing = true - return + elseif not running then + t.schedule() end - throttle.schedule() end end diff --git a/lua/trouble/view/init.lua b/lua/trouble/view/init.lua index 0abd28b6..14c61762 100644 --- a/lua/trouble/view/init.lua +++ b/lua/trouble/view/init.lua @@ -95,8 +95,14 @@ function M:on_mount() local _self = Util.weak(self) - local preview = - Util.throttle(M.preview, Util.throttle_opts(self.opts.throttle.preview, { ms = 100, debounce = true })) + local preview = Util.throttle( + M.preview, + Util.throttle_opts(self.opts.throttle.preview, { + ms = 100, + debounce = true, + }) + ) + self.win:on("CursorMoved", function() local this = _self() if not this then @@ -461,6 +467,9 @@ end -- When not in the trouble window, try to show the range function M:follow() + if not self.win:valid() then + return + end local current_win = vim.api.nvim_get_current_win() ---@type number[]|nil local cursor = nil diff --git a/lua/trouble/view/section.lua b/lua/trouble/view/section.lua index c630f7e1..9549d48f 100644 --- a/lua/trouble/view/section.lua +++ b/lua/trouble/view/section.lua @@ -45,10 +45,10 @@ function M.new(section, opts) end function M:refresh() + self.fetching = true if self.on_refresh then self:on_refresh() end - self.fetching = true local done = false local complete = function() if done then