@@ -2,18 +2,121 @@ local fs = require("peekstack.util.fs")
22local str = require (" peekstack.util.str" )
33
44local M = {}
5+
6+ local REALPATH_CACHE_MAX = 512
7+
8+ --- @type table<string , string>
59local 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
10101local 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
18121end
19122
@@ -205,4 +308,14 @@ function M.is_same_position(location, uri, line, character, opts)
205308 return true
206309end
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+
208321return M
0 commit comments