Skip to content

Commit c34ae3c

Browse files
refactor: Update memoization to clean up unused cache
1 parent 772c7c6 commit c34ae3c

File tree

4 files changed

+105
-58
lines changed

4 files changed

+105
-58
lines changed

lua/orgmode/files/file.lua

+10-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ local config = require('orgmode.config')
77
local Block = require('orgmode.files.elements.block')
88
local Link = require('orgmode.org.hyperlinks.link')
99
local Range = require('orgmode.files.elements.range')
10+
local Memoize = require('orgmode.utils.memoize')
1011

1112
---@class OrgFileMetadata
1213
---@field mtime number
@@ -26,12 +27,11 @@ local Range = require('orgmode.files.elements.range')
2627
---@field root TSNode
2728
local OrgFile = {}
2829

29-
local memoize = utils.memoize(OrgFile, function(self)
30-
return table.concat({
31-
self.filename,
32-
self.root and self.root:id() or '',
33-
self.metadata.mtime,
34-
}, '_')
30+
local memoize = Memoize:new(OrgFile, function(self)
31+
return {
32+
file = self,
33+
id = table.concat({ 'file', self.root and self.root:id() or '' }, '_'),
34+
}
3535
end)
3636

3737
---Constructor function, should not be used directly
@@ -740,8 +740,10 @@ function OrgFile:_update_lines(lines, bufnr)
740740
self:parse()
741741
if bufnr then
742742
self.metadata.changedtick = vim.api.nvim_buf_get_changedtick(bufnr)
743-
else
744-
self.metadata.mtime = vim.loop.fs_stat(self.filename).mtime.nsec
743+
end
744+
local stat = vim.loop.fs_stat(self.filename)
745+
if stat then
746+
self.metadata.mtime = stat.mtime.nsec
745747
end
746748
return self
747749
end

lua/orgmode/files/headline.lua

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ local PriorityState = require('orgmode.objects.priority_state')
77
local indent = require('orgmode.org.indent')
88
local Logbook = require('orgmode.files.elements.logbook')
99
local OrgId = require('orgmode.org.id')
10+
local Memoize = require('orgmode.utils.memoize')
1011

1112
---@alias OrgPlanDateTypes 'DEADLINE' | 'SCHEDULED' | 'CLOSED'
1213

@@ -15,13 +16,12 @@ local OrgId = require('orgmode.org.id')
1516
---@field file OrgFile
1617
local Headline = {}
1718

18-
local memoize = utils.memoize(Headline, function(self)
19+
local memoize = Memoize:new(Headline, function(self)
1920
---@cast self OrgHeadline
20-
return table.concat({
21-
self.file.filename,
22-
self.headline:id(),
23-
self.file.metadata.mtime,
24-
}, '_')
21+
return {
22+
file = self.file,
23+
id = table.concat({ 'headline', self.headline:id() }, '_'),
24+
}
2525
end)
2626

2727
---@param headline_node TSNode tree sitter headline node

lua/orgmode/utils/init.lua

-44
Original file line numberDiff line numberDiff line change
@@ -547,50 +547,6 @@ function utils.edit_file(filename)
547547
}
548548
end
549549

550-
function utils.memoize(class, key_getter)
551-
local memoizedMethods = {}
552-
local methodsToMemoize = {}
553-
local cache = setmetatable({}, { __mode = 'k' })
554-
555-
local function memoizedIndex(_, key)
556-
local method = class[key]
557-
558-
if type(method) == 'function' and methodsToMemoize[key] and not memoizedMethods[key] then
559-
memoizedMethods[key] = function(self, ...)
560-
local top_key = key_getter(self)
561-
local arg_key = key .. '_' .. table.concat({ ... }, '_')
562-
563-
if not cache[top_key] then
564-
cache[top_key] = {}
565-
end
566-
567-
if not cache[top_key][arg_key] then
568-
local value = vim.F.pack_len(method(self, ...))
569-
cache[top_key][arg_key] = value
570-
end
571-
572-
local cached_value = cache[top_key][arg_key]
573-
574-
if cached_value then
575-
local result = { pcall(vim.F.unpack_len, cached_value) }
576-
if result[1] then
577-
return unpack(result, 2)
578-
end
579-
end
580-
end
581-
end
582-
583-
return memoizedMethods[key] or method
584-
end
585-
586-
class.__index = memoizedIndex
587-
588-
return function(method)
589-
methodsToMemoize[method] = true
590-
return true
591-
end
592-
end
593-
594550
function utils.has_version_10()
595551
local v = vim.version()
596552
return not vim.version.lt({ v.major, v.minor, v.patch }, { 0, 10, 0 })

lua/orgmode/utils/memoize.lua

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---@alias MemoizeKey { file: OrgFile, id: string }
2+
3+
---@class OrgMemoize
4+
---@field class table
5+
---@field key_getter fun(self: table): MemoizeKey
6+
---@field memoized_methods table<string, fun(self: table, ...): any>
7+
---@field methods_to_memoize table<string, boolean>
8+
local Memoize = {
9+
cache = setmetatable({}, { __mode = 'k' }),
10+
}
11+
Memoize.__index = Memoize
12+
13+
---@return fun(method: string): boolean
14+
function Memoize:new(class, key_getter)
15+
local this = setmetatable({
16+
class = class,
17+
key_getter = key_getter,
18+
memoized_methods = {},
19+
methods_to_memoize = {},
20+
}, Memoize)
21+
22+
this:setup()
23+
24+
return function(method)
25+
this.methods_to_memoize[method] = true
26+
return true
27+
end
28+
end
29+
30+
function Memoize:setup()
31+
self.class.__index = function(_, key)
32+
local method = self.class[key]
33+
34+
-- Not memoizable or not required to be memoized
35+
if type(method) ~= 'function' or not self.methods_to_memoize[key] then
36+
return method
37+
end
38+
39+
-- Already memoized
40+
if self.memoized_methods[key] then
41+
return self.memoized_methods[key]
42+
end
43+
44+
self.memoized_methods[key] = function(method_self, ...)
45+
local memoize_key = self.key_getter(method_self)
46+
local cache = self:_get_cache_for_key(memoize_key)
47+
local arg_key = key .. '_' .. table.concat({ ... }, '_')
48+
49+
if not cache[arg_key] then
50+
local value = vim.F.pack_len(method(method_self, ...))
51+
cache[arg_key] = value
52+
end
53+
54+
local cached_value = cache[arg_key]
55+
56+
if cached_value then
57+
local result = { pcall(vim.F.unpack_len, cached_value) }
58+
if result[1] then
59+
return unpack(result, 2)
60+
end
61+
end
62+
end
63+
64+
return self.memoized_methods[key]
65+
end
66+
end
67+
68+
---@private
69+
---@param memoize_key MemoizeKey
70+
---@return string
71+
function Memoize:_get_cache_for_key(memoize_key)
72+
local id = memoize_key.id
73+
local filename = memoize_key.file.filename
74+
local version_key = memoize_key.file.metadata.mtime
75+
76+
if not self.cache[filename] or self.cache[filename].__version ~= version_key then
77+
self.cache[filename] = {
78+
__version = version_key,
79+
}
80+
end
81+
82+
if not self.cache[filename][id] then
83+
self.cache[filename][id] = {}
84+
end
85+
86+
return self.cache[filename][id]
87+
end
88+
89+
return Memoize

0 commit comments

Comments
 (0)