Skip to content

Commit

Permalink
feat: add optional debounce
Browse files Browse the repository at this point in the history
  • Loading branch information
willothy authored and tris203 committed Jun 2, 2024
1 parent 9985531 commit ac1213a
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 24 deletions.
83 changes: 59 additions & 24 deletions lua/precognition/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ local M = {}
---@field NextParagraph Precognition.HintOpts

---@class Precognition.Config
---@field debounce integer
---@field startVisible boolean
---@field showBlankVirtLine boolean
---@field highlightColor vim.api.keyset.highlight
Expand Down Expand Up @@ -74,6 +75,7 @@ local defaultHintConfig = {

---@type Precognition.Config
local default = {
debounce = 0,
startVisible = true,
showBlankVirtLine = true,
highlightColor = { link = "Comment" },
Expand Down Expand Up @@ -279,21 +281,6 @@ local function display_marks()
dirty = false
end

local function on_cursor_moved(ev)
local buf = ev and ev.buf or vim.api.nvim_get_current_buf()
if extmark then
local ext = vim.api.nvim_buf_get_extmark_by_id(buf, ns, extmark, {
details = true,
})
if ext and ext[1] ~= vim.api.nvim_win_get_cursor(0)[1] - 1 then
vim.api.nvim_buf_del_extmark(0, ns, extmark)
extmark = nil
end
end
dirty = true
display_marks()
end

local function on_insert_enter(ev)
if extmark then
vim.api.nvim_buf_del_extmark(ev.buf, ns, extmark)
Expand All @@ -302,6 +289,24 @@ local function on_insert_enter(ev)
dirty = true
end

---@param draw fun()
local function cursor_moved_handler(draw)
return function(ev)
local buf = ev and ev.buf or vim.api.nvim_get_current_buf()
if extmark then
local ext = vim.api.nvim_buf_get_extmark_by_id(buf, ns, extmark, {
details = true,
})
if ext and ext[1] ~= vim.api.nvim_win_get_cursor(0)[1] - 1 then
vim.api.nvim_buf_del_extmark(0, ns, extmark)
extmark = nil
end
end
dirty = true
draw()
end
end

local function on_buf_edit()
apply_gutter_hints(build_gutter_hints())
end
Expand Down Expand Up @@ -333,29 +338,44 @@ function M.show()
end
visible = true

local prev_line
local draw = display_marks
if config.debounce > 0 then
local debounced = utils.debounce_trailing(display_marks, config.debounce)
draw = function(...)
local line = vim.api.nvim_win_get_cursor(0)[1]
if line == prev_line then
display_marks()
else
prev_line = line
end
debounced(...)
end
end

-- clear and redraw the hints when the cursor moves
vim.api.nvim_create_autocmd("CursorMoved", {
group = au,
callback = cursor_moved_handler(draw),
})

-- clear the extmark entirely when leaving a buffer (hints should only show in current buffer)
vim.api.nvim_create_autocmd("BufLeave", {
group = au,
callback = on_buf_leave,
})

-- clear the extmark when the cursor moves in insert mode
vim.api.nvim_create_autocmd("CursorMovedI", {
group = au,
callback = on_buf_edit,
})
-- clear the extmark when the cursor moves, or when insert mode is entered
--
vim.api.nvim_create_autocmd("CursorMoved", {
group = au,
callback = on_cursor_moved,
})

vim.api.nvim_create_autocmd("InsertEnter", {
group = au,
callback = on_insert_enter,
})

display_marks()
draw()
end

--- Disable automatic showing of hints
Expand Down Expand Up @@ -411,7 +431,22 @@ local state = {
return build_gutter_hints
end,
on_cursor_moved = function()
return on_cursor_moved
local prev_line
local draw = display_marks
if config.debounce > 0 then
local debounced = utils.debounce_trailing(display_marks, config.debounce)
draw = function(...)
local line = vim.api.nvim_win_get_cursor(0)[1]
if line == prev_line then
display_marks()
else
prev_line = line
end
debounced(...)
end
end

return cursor_moved_handler(draw)
end,
extmark = function()
return extmark
Expand Down
38 changes: 38 additions & 0 deletions lua/precognition/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,42 @@ function M.add_multibyte_padding(cur_line, extra_padding, line_len)
end
end

---Debounces calls to a function, and ensures it only runs once per delay
---even if called repeatedly.
---@param fn fun(...: any)
---@param delay integer
function M.debounce_trailing(fn, delay)
local running = false
local timer = assert(vim.uv.new_timer())

-- Ugly hack to ensure timer is closed when the function is garbage collected
-- unfortunate but necessary to avoid creating a new timer for each call.
--
-- In LuaJIT, only userdata can have finalizers. `newproxy` creates an opaque userdata
-- which we can attach a finalizer to and use as a "canary."
local proxy = newproxy(true)
getmetatable(proxy).__gc = function()
if not timer:is_closing() then
timer:close()
end
end

return function(...)
local _ = proxy
if running then
return
end
running = true
local args = { ... }
timer:start(
delay,
0,
vim.schedule_wrap(function()
fn(unpack(args))
running = false
end)
)
end
end

return M
41 changes: 41 additions & 0 deletions tests/precognition/e2e_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,47 @@ describe("e2e tests", function()
end
end)

it("supports debounce", function()
precognition.setup({
debounce = 200,
})

local buffer = vim.api.nvim_create_buf(true, false)
vim.api.nvim_set_current_buf(buffer)
vim.api.nvim_buf_set_lines(
buffer,
0,
-1,
false,
{ "Hello World this is a test", "line 2", "", "line 4", "", "line 6" }
)

precognition.on_cursor_moved()

assert.is_nil(precognition.extmark)

eq(
{},
vim.api.nvim_buf_get_extmarks(buffer, precognition.ns, 0, -1, {
details = true,
})
)

local co = coroutine.running()
coroutine.yield(vim.defer_fn(function()
coroutine.resume(co)
end, 210))

assert.not_nil(precognition.extmark)

local extmarks = vim.api.nvim_buf_get_extmark_by_id(buffer, precognition.ns, precognition.extmark, {
details = true,
})

eq(vim.api.nvim_win_get_cursor(0)[1] - 1, extmarks[1])
eq("^ e w $", extmarks[3].virt_lines[1][1][1])
end)

it("virtual line text color can be customised", function()
precognition.setup({ highlightColor = { link = "Function" } })
local buffer = vim.api.nvim_create_buf(true, false)
Expand Down

0 comments on commit ac1213a

Please sign in to comment.