Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 34 additions & 13 deletions lua/peekstack/core/events.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ local M = {}
---@type uv.uv_timer_t?
local reflow_timer = nil
local REFLOW_DEBOUNCE_MS = 80
---@type table<integer, boolean>
local popup_cursor_buffers = {}

local function reset_reflow_timer()
local store = timer_util.get_store()
Expand All @@ -31,6 +33,27 @@ local function debounced_reflow()
end)
end

---@param group integer
---@param bufnr integer
local function ensure_popup_cursor_tracking(group, bufnr)
if popup_cursor_buffers[bufnr] then
return
end
popup_cursor_buffers[bufnr] = true

vim.api.nvim_create_autocmd("CursorMoved", {
group = group,
buffer = bufnr,
callback = function()
local winid = vim.api.nvim_get_current_win()
if vim.w[winid].peekstack_popup_id == nil then
return
end
stack.touch(winid)
end,
})
end

---Close all ephemeral popups across all stacks and reflow
local function close_ephemeral_popups()
stack.close_ephemerals()
Expand All @@ -46,6 +69,7 @@ end

function M.setup()
reset_reflow_timer()
popup_cursor_buffers = {}
local group = vim.api.nvim_create_augroup("PeekstackEvents", { clear = true })

vim.api.nvim_create_autocmd("WinClosed", {
Expand All @@ -63,6 +87,7 @@ function M.setup()
callback = function(args)
stack.handle_buf_wipeout(args.buf)
stack.handle_origin_wipeout(args.buf)
popup_cursor_buffers[args.buf] = nil
end,
})

Expand All @@ -71,23 +96,14 @@ function M.setup()
callback = debounced_reflow,
})

vim.api.nvim_create_autocmd("CursorMoved", {
group = group,
callback = function()
local winid = vim.api.nvim_get_current_win()
if vim.w[winid].peekstack_popup_id == nil then
return
end
if is_floating_window(winid) then
stack.touch(winid)
end
end,
})

vim.api.nvim_create_autocmd("WinEnter", {
group = group,
callback = function()
local winid = vim.api.nvim_get_current_win()
if vim.w[winid].peekstack_popup_id ~= nil then
local bufnr = vim.api.nvim_win_get_buf(winid)
ensure_popup_cursor_tracking(group, bufnr)
end
if is_floating_window(winid) then
stack.touch(winid)
local layout = require("peekstack.core.layout")
Expand All @@ -99,6 +115,11 @@ function M.setup()
end,
})

local current_winid = vim.api.nvim_get_current_win()
if vim.w[current_winid].peekstack_popup_id ~= nil then
ensure_popup_cursor_tracking(group, vim.api.nvim_win_get_buf(current_winid))
end

local cfg = config.get()
local close_events = cfg.ui.quick_peek and cfg.ui.quick_peek.close_events
or { "CursorMoved", "InsertEnter", "BufLeave", "WinLeave" }
Expand Down
2 changes: 2 additions & 0 deletions lua/peekstack/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ function M.setup(opts)
local config = require("peekstack.config")
local events = require("peekstack.core.events")
local commands = require("peekstack.commands")
local stack_view = require("peekstack.ui.stack_view")
local persist_auto = require("peekstack.persist.auto")

providers = {}
Expand All @@ -271,6 +272,7 @@ function M.setup(opts)
set_hl()
events.setup()
commands.setup()
stack_view.setup()

local cfg = config.get()

Expand Down
20 changes: 17 additions & 3 deletions lua/peekstack/ui/inline_preview.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ local config = require("peekstack.config")
local fs = require("peekstack.util.fs")

local NS_NAME = "PeekstackInlinePreviewNS"
---@type integer?
local namespace_id = nil

---@type PeekstackInlinePreviewState?
local state = nil
Expand All @@ -13,10 +15,14 @@ local M = {}
---Get or create the inline preview namespace
---@return integer
local function get_namespace()
if not vim.api.nvim_get_namespaces()[NS_NAME] then
vim.api.nvim_create_namespace(NS_NAME)
if namespace_id then
return namespace_id
end
return vim.api.nvim_get_namespaces()[NS_NAME]
namespace_id = vim.api.nvim_get_namespaces()[NS_NAME]
if not namespace_id then
namespace_id = vim.api.nvim_create_namespace(NS_NAME)
end
return namespace_id
end

---Check if inline preview is currently open
Expand Down Expand Up @@ -228,4 +234,12 @@ function M.setup_close_events()
end
end

---Reset inline preview internals (for testing).
function M._reset_for_test()
M.close()
state = nil
request_id = 0
namespace_id = nil
end

return M
13 changes: 10 additions & 3 deletions lua/peekstack/ui/stack_view.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ local TITLE_HL_TO_SV = {

---@type table<integer, PeekstackStackViewState>
local states = {}
---@type integer?
local tab_cleanup_group = nil

---@param s PeekstackStackViewState
local function cleanup_state(s)
Expand Down Expand Up @@ -79,10 +81,14 @@ local function cleanup_invalid_states()
end
end

do
local group = vim.api.nvim_create_augroup("PeekstackStackViewTabCleanup", { clear = true })
---Setup stack view autocmds.
function M.setup()
if tab_cleanup_group then
return
end
tab_cleanup_group = vim.api.nvim_create_augroup("PeekstackStackViewTabCleanup", { clear = true })
vim.api.nvim_create_autocmd("TabClosed", {
group = group,
group = tab_cleanup_group,
callback = function()
cleanup_invalid_states()
end,
Expand Down Expand Up @@ -1202,6 +1208,7 @@ end

---Open the stack view
function M.open()
M.setup()
local s = get_state()
if is_open(s) then
vim.api.nvim_set_current_win(s.winid)
Expand Down
39 changes: 24 additions & 15 deletions tests/events_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ describe("peekstack.core.events", function()

local original_touch
local original_close_ephemerals
local original_win_get_config

before_each(function()
config.setup({
Expand All @@ -16,28 +15,21 @@ describe("peekstack.core.events", function()
})
original_touch = stack.touch
original_close_ephemerals = stack.close_ephemerals
original_win_get_config = vim.api.nvim_win_get_config
end)

after_each(function()
stack.touch = original_touch
stack.close_ephemerals = original_close_ephemerals
vim.api.nvim_win_get_config = original_win_get_config
local winid = vim.api.nvim_get_current_win()
vim.w[winid].peekstack_popup_id = nil
end)

it("skips non-peekstack windows on CursorMoved", function()
local touch_calls = 0
local config_calls = 0
stack.touch = function()
touch_calls = touch_calls + 1
end
stack.close_ephemerals = function() end
vim.api.nvim_win_get_config = function(winid)
config_calls = config_calls + 1
return original_win_get_config(winid)
end

local winid = vim.api.nvim_get_current_win()
vim.w[winid].peekstack_popup_id = nil
Expand All @@ -46,28 +38,45 @@ describe("peekstack.core.events", function()
vim.api.nvim_exec_autocmds("CursorMoved", { modeline = false })

assert.equals(0, touch_calls)
assert.equals(0, config_calls)
end)

it("touches peekstack popup windows on CursorMoved", function()
local touch_calls = 0
local config_calls = 0
stack.touch = function()
touch_calls = touch_calls + 1
end
stack.close_ephemerals = function() end
vim.api.nvim_win_get_config = function()
config_calls = config_calls + 1
return { relative = "editor" }
end

local winid = vim.api.nvim_get_current_win()
vim.w[winid].peekstack_popup_id = 999

events.setup()
vim.api.nvim_exec_autocmds("WinEnter", { modeline = false })
vim.api.nvim_exec_autocmds("CursorMoved", { modeline = false })

assert.equals(1, touch_calls)
assert.equals(1, config_calls)
end)

it("registers CursorMoved autocmd only for popup buffers", function()
local winid = vim.api.nvim_get_current_win()
local bufnr = vim.api.nvim_get_current_buf()

events.setup()
local before = vim.api.nvim_get_autocmds({
group = "PeekstackEvents",
event = "CursorMoved",
buffer = bufnr,
})
assert.equals(0, #before)

vim.w[winid].peekstack_popup_id = 999
vim.api.nvim_exec_autocmds("WinEnter", { modeline = false })

local after = vim.api.nvim_get_autocmds({
group = "PeekstackEvents",
event = "CursorMoved",
buffer = bufnr,
})
assert.equals(1, #after)
end)
end)
29 changes: 29 additions & 0 deletions tests/inline_preview_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ describe("peekstack.ui.inline_preview", function()
local temp_file = nil

before_each(function()
inline_preview._reset_for_test()
-- Setup config with inline preview enabled
config.setup({
ui = {
Expand All @@ -20,6 +21,7 @@ describe("peekstack.ui.inline_preview", function()
end)

after_each(function()
inline_preview._reset_for_test()
if temp_file then
vim.fn.delete(temp_file)
end
Expand Down Expand Up @@ -119,4 +121,31 @@ describe("peekstack.ui.inline_preview", function()
-- Should not error when disabled
inline_preview.open(location)
end)

it("should cache namespace id", function()
local location = {
uri = vim.uri_from_fname(temp_file),
range = { start = { line = 0, character = 0 }, ["end"] = { line = 0, character = 10 } },
}

local original_get_namespaces = vim.api.nvim_get_namespaces
local get_namespaces_calls = 0
local ok, err = pcall(function()
vim.api.nvim_get_namespaces = function(...)
get_namespaces_calls = get_namespaces_calls + 1
return original_get_namespaces(...)
end

inline_preview.open(location)
inline_preview.close()
inline_preview.open(location)
inline_preview.close()
end)
vim.api.nvim_get_namespaces = original_get_namespaces
if not ok then
error(err)
end

assert.equals(1, get_namespaces_calls)
end)
end)