Skip to content

Commit 28ef3e9

Browse files
authored
Merge pull request #12 from mhiro2/feat/perf-refactor-batch
perf: Optimize stack view rendering and add LRU cache with data-driven config
2 parents f6c1fce + 1df2325 commit 28ef3e9

10 files changed

Lines changed: 970 additions & 347 deletions

File tree

lua/peekstack/config.lua

Lines changed: 330 additions & 284 deletions
Large diffs are not rendered by default.

lua/peekstack/core/location.lua

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,121 @@ local fs = require("peekstack.util.fs")
22
local str = require("peekstack.util.str")
33

44
local M = {}
5+
6+
local REALPATH_CACHE_MAX = 512
7+
8+
---@type table<string, string>
59
local realpath_cache = {}
10+
---@type table<string, { prev: string?, next: string? }>
11+
local realpath_cache_nodes = {}
12+
---@type string?
13+
local realpath_cache_head = nil
14+
---@type string?
15+
local realpath_cache_tail = nil
16+
local realpath_cache_size = 0
17+
18+
---@param key string
19+
local function cache_detach(key)
20+
local node = realpath_cache_nodes[key]
21+
if not node then
22+
return
23+
end
24+
25+
local prev = node.prev
26+
local next = node.next
27+
28+
if prev then
29+
realpath_cache_nodes[prev].next = next
30+
else
31+
realpath_cache_head = next
32+
end
33+
34+
if next then
35+
realpath_cache_nodes[next].prev = prev
36+
else
37+
realpath_cache_tail = prev
38+
end
39+
40+
node.prev = nil
41+
node.next = nil
42+
end
43+
44+
---@param key string
45+
local function cache_append_tail(key)
46+
if not realpath_cache_nodes[key] then
47+
realpath_cache_nodes[key] = {}
48+
end
49+
50+
local node = realpath_cache_nodes[key]
51+
node.prev = realpath_cache_tail
52+
node.next = nil
53+
54+
if realpath_cache_tail then
55+
realpath_cache_nodes[realpath_cache_tail].next = key
56+
else
57+
realpath_cache_head = key
58+
end
59+
realpath_cache_tail = key
60+
end
61+
62+
---@param key string
63+
---@param is_new boolean
64+
local function cache_touch(key, is_new)
65+
if not is_new and realpath_cache_tail == key then
66+
return
67+
end
68+
69+
if not is_new then
70+
cache_detach(key)
71+
else
72+
realpath_cache_size = realpath_cache_size + 1
73+
end
74+
cache_append_tail(key)
75+
end
76+
77+
local function cache_evict_if_needed()
78+
while realpath_cache_size > REALPATH_CACHE_MAX do
79+
local evict_key = realpath_cache_head
80+
if not evict_key then
81+
break
82+
end
83+
cache_detach(evict_key)
84+
realpath_cache_nodes[evict_key] = nil
85+
realpath_cache[evict_key] = nil
86+
realpath_cache_size = realpath_cache_size - 1
87+
end
88+
end
89+
90+
local function cache_clear()
91+
realpath_cache = {}
92+
realpath_cache_nodes = {}
93+
realpath_cache_head = nil
94+
realpath_cache_tail = nil
95+
realpath_cache_size = 0
96+
end
697

798
---@param fname string
899
---@param cache? table<string, string>
9100
---@return string
10101
local function resolve_realpath(fname, cache)
11-
local store = cache or realpath_cache
12-
if store[fname] then
13-
return store[fname]
102+
if cache then
103+
if cache[fname] then
104+
return cache[fname]
105+
end
106+
local resolved = vim.uv.fs_realpath(fname) or fname
107+
cache[fname] = resolved
108+
return resolved
109+
end
110+
111+
if realpath_cache[fname] then
112+
cache_touch(fname, false)
113+
return realpath_cache[fname]
14114
end
115+
15116
local resolved = vim.uv.fs_realpath(fname) or fname
16-
store[fname] = resolved
117+
realpath_cache[fname] = resolved
118+
cache_touch(fname, true)
119+
cache_evict_if_needed()
17120
return resolved
18121
end
19122

@@ -205,4 +308,14 @@ function M.is_same_position(location, uri, line, character, opts)
205308
return true
206309
end
207310

311+
---Reset internal caches (for testing).
312+
function M._reset()
313+
cache_clear()
314+
end
315+
316+
---@return integer
317+
function M._realpath_cache_limit()
318+
return REALPATH_CACHE_MAX
319+
end
320+
208321
return M

0 commit comments

Comments
 (0)