Skip to content

Commit 83727be

Browse files
authored
Merge pull request #1 from mhiro2/chore/minor-hardening-and-typing
chore: Minor hardening, performance, and type annotation improvements
2 parents 0b2bd1d + 41d92b3 commit 83727be

9 files changed

Lines changed: 221 additions & 7 deletions

File tree

lua/peekstack/commands.lua

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,27 @@ local COMMAND_NAMES = {
1414
"PeekstackQuickPeek",
1515
}
1616

17+
---@param session PeekstackSession|table
18+
---@return integer
19+
local function session_item_count(session)
20+
local items = session and session.items
21+
if type(items) ~= "table" then
22+
return 0
23+
end
24+
return #items
25+
end
26+
27+
---@param session PeekstackSession|table
28+
---@return string
29+
local function session_updated_at_text(session)
30+
local meta = session and session.meta
31+
local updated_at = type(meta) == "table" and meta.updated_at or nil
32+
if type(updated_at) ~= "number" then
33+
return "unknown"
34+
end
35+
return os.date("%Y-%m-%d %H:%M:%S", updated_at)
36+
end
37+
1738
---@return string[]
1839
local function list_session_names()
1940
local persist = require("peekstack.persist")
@@ -76,8 +97,8 @@ function M.setup()
7697
local info = string.format(
7798
"%s: %d items (updated: %s)",
7899
selected,
79-
#session.items,
80-
os.date("%Y-%m-%d %H:%M:%S", session.meta.updated_at)
100+
session_item_count(session),
101+
session_updated_at_text(session)
81102
)
82103
vim.ui.select({ "Restore", "Info only" }, { prompt = info }, function(action)
83104
if action == "Restore" then

lua/peekstack/core/events.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ function M.setup()
7575
group = group,
7676
callback = function()
7777
local winid = vim.api.nvim_get_current_win()
78+
if vim.w[winid].peekstack_popup_id == nil then
79+
return
80+
end
7881
if is_floating_window(winid) then
7982
stack.touch(winid)
8083
end

lua/peekstack/init.lua

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,25 @@ local function peek_by_provider(provider, opts)
194194
end)
195195
end
196196

197+
---@class PeekstackPeekMethods
198+
---@field definition fun(opts?: table)
199+
---@field implementation fun(opts?: table)
200+
---@field references fun(opts?: table)
201+
---@field type_definition fun(opts?: table)
202+
---@field declaration fun(opts?: table)
203+
---@field diagnostics_cursor fun(opts?: table)
204+
---@field diagnostics_buffer fun(opts?: table)
205+
---@field file_under_cursor fun(opts?: table)
206+
---@field grep fun(opts?: table)
207+
---@field marks_buffer fun(opts?: table)
208+
---@field marks_global fun(opts?: table)
209+
---@field marks_all fun(opts?: table)
210+
211+
---@alias PeekstackPeekCallable fun(provider: string, opts?: table)
212+
197213
--- `M.peek` is a callable table: call `M.peek("provider", opts)` directly,
198214
--- or use convenience shortcuts like `M.peek.definition(opts)`.
199-
---@type table
215+
---@type PeekstackPeekMethods|PeekstackPeekCallable
200216
M.peek = setmetatable({
201217
definition = function(opts)
202218
return peek_by_provider("lsp.definition", opts)

lua/peekstack/types.lua

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,14 @@
4646
---@field origin_bufnr integer
4747
---@field origin_is_popup boolean
4848
---@field title string
49+
---@field title_chunks? PeekstackTitleChunk[]
4950
---@field pinned boolean
5051
---@field buffer_mode "copy"|"source"
5152
---@field line_offset integer
5253
---@field created_at integer
5354
---@field last_active_at integer
5455
---@field ephemeral boolean
55-
---@field win_opts table
56+
---@field win_opts PeekstackRenderWinOpts
5657

5758
---@class PeekstackStackModel
5859
---@field root_winid integer
@@ -88,6 +89,19 @@
8889
---@field [1] string
8990
---@field [2]? string
9091

92+
---@class PeekstackRenderWinOpts
93+
---@field relative string
94+
---@field row number
95+
---@field col number
96+
---@field width integer
97+
---@field height integer
98+
---@field style string
99+
---@field border string|string[]
100+
---@field focusable boolean
101+
---@field zindex integer
102+
---@field title? string|PeekstackTitleChunk[]
103+
---@field title_pos? string
104+
91105
---@class PeekstackStoreData
92106
---@field version integer
93107
---@field sessions table<string, PeekstackSession>

lua/peekstack/ui/render.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,10 @@ end
287287
---@param location PeekstackLocation
288288
---@param opts? { buffer_mode?: "copy"|"source" }
289289
---@return integer winid
290-
---@return table win_opts
290+
---@return PeekstackRenderWinOpts win_opts
291291
function M.open(bufnr, location, opts)
292292
local layout_opts = layout.compute(1)
293+
---@type PeekstackRenderWinOpts
293294
local win_opts = {
294295
relative = "editor",
295296
row = layout_opts.row,

lua/peekstack/util/fs.lua

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,34 @@ local M = {}
44
local cached_repo_root = nil
55
---@type string?
66
local cached_repo_cwd = nil
7+
---@type string?
8+
local cached_scope_base = nil
9+
local scope_base_ready = false
710

811
local function reset_repo_root_cache()
912
cached_repo_root = nil
1013
cached_repo_cwd = nil
1114
end
1215

16+
local function reset_scope_base_cache()
17+
cached_scope_base = nil
18+
scope_base_ready = false
19+
end
20+
21+
---@return string
22+
local function scope_base_dir()
23+
local base = vim.fn.stdpath("state") .. "/peekstack"
24+
if cached_scope_base ~= base then
25+
cached_scope_base = base
26+
scope_base_ready = false
27+
end
28+
if not scope_base_ready then
29+
M.ensure_dir(base)
30+
scope_base_ready = true
31+
end
32+
return base
33+
end
34+
1335
vim.api.nvim_create_autocmd("DirChanged", {
1436
group = vim.api.nvim_create_augroup("PeekstackRepoRootCache", { clear = true }),
1537
callback = reset_repo_root_cache,
@@ -54,6 +76,11 @@ function M._reset_repo_root_cache()
5476
reset_repo_root_cache()
5577
end
5678

79+
---Reset scope-path cache (for testing).
80+
function M._reset_scope_path_cache()
81+
reset_scope_base_cache()
82+
end
83+
5784
---@param path string
5885
---@return string
5986
function M.ensure_dir(path)
@@ -80,8 +107,7 @@ end
80107
---@param scope string
81108
---@return string
82109
function M.scope_path(scope)
83-
local base = vim.fn.stdpath("state") .. "/peekstack"
84-
M.ensure_dir(base)
110+
local base = scope_base_dir()
85111
if scope == "global" then
86112
return base .. "/global.json"
87113
end

tests/commands_spec.lua

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@ describe("peekstack.commands", function()
22
local commands = require("peekstack.commands")
33
local persist = require("peekstack.persist")
44
local original_list_sessions = nil
5+
local original_select = nil
56

67
before_each(function()
78
commands._reset()
89
original_list_sessions = persist.list_sessions
10+
original_select = vim.ui.select
911
end)
1012

1113
after_each(function()
1214
if original_list_sessions then
1315
persist.list_sessions = original_list_sessions
1416
end
17+
if original_select then
18+
vim.ui.select = original_select
19+
end
1520
commands._reset()
1621
end)
1722

@@ -34,4 +39,33 @@ describe("peekstack.commands", function()
3439
assert.same({ "alpha", "beta" }, names)
3540
assert.is_false(called_with_opts)
3641
end)
42+
43+
it("handles missing session meta in list command", function()
44+
local prompts = {}
45+
persist.list_sessions = function(opts)
46+
assert.is_truthy(opts)
47+
assert.is_truthy(opts.on_done)
48+
opts.on_done({
49+
broken = {
50+
items = { { uri = "file:///tmp/a.lua" } },
51+
},
52+
})
53+
return {}
54+
end
55+
56+
vim.ui.select = function(_items, opts, on_choice)
57+
table.insert(prompts, opts.prompt)
58+
if opts.prompt == "Select a session" then
59+
on_choice("broken")
60+
return
61+
end
62+
on_choice("Info only")
63+
end
64+
65+
commands.setup()
66+
vim.api.nvim_cmd({ cmd = "PeekstackListSessions" }, {})
67+
68+
assert.equals("Select a session", prompts[1])
69+
assert.equals("broken: 1 items (updated: unknown)", prompts[2])
70+
end)
3771
end)

tests/events_spec.lua

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
describe("peekstack.core.events", function()
2+
local events = require("peekstack.core.events")
3+
local stack = require("peekstack.core.stack")
4+
local config = require("peekstack.config")
5+
6+
local original_touch
7+
local original_close_ephemerals
8+
local original_win_get_config
9+
10+
before_each(function()
11+
config.setup({
12+
ui = {
13+
quick_peek = { close_events = {} },
14+
popup = { auto_close = { enabled = false } },
15+
},
16+
})
17+
original_touch = stack.touch
18+
original_close_ephemerals = stack.close_ephemerals
19+
original_win_get_config = vim.api.nvim_win_get_config
20+
end)
21+
22+
after_each(function()
23+
stack.touch = original_touch
24+
stack.close_ephemerals = original_close_ephemerals
25+
vim.api.nvim_win_get_config = original_win_get_config
26+
local winid = vim.api.nvim_get_current_win()
27+
vim.w[winid].peekstack_popup_id = nil
28+
end)
29+
30+
it("skips non-peekstack windows on CursorMoved", function()
31+
local touch_calls = 0
32+
local config_calls = 0
33+
stack.touch = function()
34+
touch_calls = touch_calls + 1
35+
end
36+
stack.close_ephemerals = function() end
37+
vim.api.nvim_win_get_config = function(winid)
38+
config_calls = config_calls + 1
39+
return original_win_get_config(winid)
40+
end
41+
42+
local winid = vim.api.nvim_get_current_win()
43+
vim.w[winid].peekstack_popup_id = nil
44+
45+
events.setup()
46+
vim.api.nvim_exec_autocmds("CursorMoved", { modeline = false })
47+
48+
assert.equals(0, touch_calls)
49+
assert.equals(0, config_calls)
50+
end)
51+
52+
it("touches peekstack popup windows on CursorMoved", function()
53+
local touch_calls = 0
54+
local config_calls = 0
55+
stack.touch = function()
56+
touch_calls = touch_calls + 1
57+
end
58+
stack.close_ephemerals = function() end
59+
vim.api.nvim_win_get_config = function()
60+
config_calls = config_calls + 1
61+
return { relative = "editor" }
62+
end
63+
64+
local winid = vim.api.nvim_get_current_win()
65+
vim.w[winid].peekstack_popup_id = 999
66+
67+
events.setup()
68+
vim.api.nvim_exec_autocmds("CursorMoved", { modeline = false })
69+
70+
assert.equals(1, touch_calls)
71+
assert.equals(1, config_calls)
72+
end)
73+
end)

tests/fs_spec.lua

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ describe("fs", function()
3131
end)
3232

3333
describe("scope_path", function()
34+
local original_ensure_dir
35+
36+
before_each(function()
37+
fs._reset_scope_path_cache()
38+
original_ensure_dir = fs.ensure_dir
39+
end)
40+
41+
after_each(function()
42+
fs.ensure_dir = original_ensure_dir
43+
fs._reset_scope_path_cache()
44+
end)
45+
3446
it("returns a global path for 'global' scope", function()
3547
local path = fs.scope_path("global")
3648
assert.is_true(path:find("peekstack/global.json") ~= nil)
@@ -53,6 +65,20 @@ describe("fs", function()
5365
local path = fs.scope_path("global")
5466
assert.is_true(path:find(state_dir, 1, true) ~= nil)
5567
end)
68+
69+
it("runs ensure_dir only once after cache warmup", function()
70+
local calls = 0
71+
fs.ensure_dir = function(path)
72+
calls = calls + 1
73+
return path
74+
end
75+
76+
fs.scope_path("global")
77+
fs.scope_path("cwd")
78+
fs.scope_path("global")
79+
80+
assert.equals(1, calls)
81+
end)
5682
end)
5783

5884
describe("uri_to_fname", function()

0 commit comments

Comments
 (0)