Skip to content

Commit 2cbbe3f

Browse files
committed
feat: prune tag containers based on ttl
1 parent b2b0586 commit 2cbbe3f

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

lua/grapple.lua

+14
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,20 @@ function Grapple.reset(opts)
393393
vim.notify(string.format("Scope reset: %s", opts.scope or opts.id), vim.log.levels.INFO)
394394
end
395395

396+
---@param opts { ttl: integer | string }
397+
---@return string[] | nil, string? error
398+
function Grapple.prune(opts)
399+
local App = require("grapple.app")
400+
local app = App.get()
401+
402+
local pruned_ids, err = app.tag_manager:prune(opts.ttl)
403+
if not pruned_ids then
404+
return nil, err
405+
end
406+
407+
return pruned_ids, nil
408+
end
409+
396410
---Create a user-defined scope
397411
---@param definition grapple.scope_definition
398412
---@return string? error

lua/grapple/state.lua

+47
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,53 @@ function State:remove(name)
8282
end
8383
end
8484

85+
---@return string[] | nil pruned, string? error, string? error_kind
86+
function State:prune(ttl_msec)
87+
local function current_time()
88+
local name = os.tmpname()
89+
local fd, err, err_kind = vim.loop.fs_open(name, "r", 438)
90+
if not fd then
91+
return nil, err, err_kind
92+
end
93+
94+
local stat, err, err_kind = vim.loop.fs_fstat(fd)
95+
if not stat then
96+
return nil, err, err_kind
97+
end
98+
99+
assert(vim.loop.fs_close(fd))
100+
assert(vim.loop.fs_unlink(name))
101+
102+
return stat.mtime.sec, nil, nil
103+
end
104+
105+
local now, err, err_kind = current_time()
106+
if not now then
107+
return nil, err, err_kind
108+
end
109+
110+
local pruned = {}
111+
112+
for name, _ in vim.fs.dir(self.save_dir) do
113+
local path = Path.join(self.save_dir, name)
114+
115+
---@diagnostic disable-next-line: redefined-local
116+
local stat, err, err_kind = vim.loop.fs_stat(path)
117+
if not stat then
118+
return err, err_kind
119+
end
120+
121+
local elapsed_msec = now - stat.mtime.sec
122+
if elapsed_msec > ttl_msec then
123+
name = path_decode(name)
124+
name = string.gsub(name, "%.json", "")
125+
table.insert(pruned, path_decode(name))
126+
end
127+
end
128+
129+
return pruned, nil, nil
130+
end
131+
85132
---@param name string
86133
---@return any decoded, string? error, string? error_kind
87134
function State:read(name)

lua/grapple/tag_manager.lua

+38
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,44 @@ function TagManager:reset(id)
147147
end
148148
end
149149

150+
---@param ttl integer | string
151+
---@return string[] | nil pruned, string? error
152+
function TagManager:prune(ttl)
153+
vim.validate({
154+
ttl = { ttl, { "number", "string" } },
155+
})
156+
157+
local ttl_msec
158+
if type(ttl) == "number" then
159+
ttl_msec = ttl
160+
elseif type(ttl) == "string" then
161+
local n, kind = string.match(ttl, "^(%d+)(%S)$")
162+
if not n or not kind then
163+
return nil, string.format("Could not parse time-to-live: %s", ttl)
164+
end
165+
166+
n = assert(tonumber(n))
167+
if kind == "d" then
168+
ttl_msec = n * 24 * 60 * 60 * 1000
169+
elseif kind == "h" then
170+
ttl_msec = n * 60 * 60 * 1000
171+
elseif kind == "m" then
172+
ttl_msec = n * 60 * 1000
173+
else
174+
return nil, string.format("Invalid time-to-live kind: %s", kind)
175+
end
176+
else
177+
return nil, string.format("Invalid time-to-live: %s", vim.inspect(ttl))
178+
end
179+
180+
local pruned_ids, err = self.state:prune(ttl_msec)
181+
if not pruned_ids then
182+
return nil, err
183+
end
184+
185+
return pruned_ids, nil
186+
end
187+
150188
---@param id string
151189
---@return string? error
152190
function TagManager:sync(id)

0 commit comments

Comments
 (0)