-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathcache.lua
146 lines (121 loc) · 4.22 KB
/
cache.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
--[[
This file is an internal file-browser addon.
It should not be imported like a normal module.
Maintains a cache of the accessed directories to improve
parsing speed. Disabled by default.
]]
local mp = require 'mp'
local msg = require 'mp.msg'
local utils = require 'mp.utils'
local fb = require 'file-browser'
---@type ParserConfig
local cacheParser = {
name = 'cache',
priority = 0,
api_version = '1.9',
}
---@class CacheEntry
---@field list List
---@field opts Opts?
---@field timeout MPTimer
---@type table<string,CacheEntry>
local cache = {}
---@type table<string,(async fun(list: List?, opts: Opts?))[]>
local pending_parses = {}
---@param directories? string[]
local function clear_cache(directories)
if directories then
msg.debug('clearing cache for', #directories, 'directorie(s)')
for _, dir in ipairs(directories) do
if cache[dir] then
msg.trace('clearing cache for', dir)
cache[dir].timeout:kill()
cache[dir] = nil
end
end
else
msg.debug('clearing cache')
for _, entry in pairs(cache) do
entry.timeout:kill()
end
cache = {}
end
end
---@type string
local prev_directory = ''
function cacheParser:can_parse(directory, parse_state)
-- allows the cache to be forcibly used or bypassed with the
-- cache/use parse property.
if parse_state.properties.cache and parse_state.properties.cache.use ~= nil then
if parse_state.source == 'browser' then prev_directory = directory end
return parse_state.properties.cache.use
end
-- the script message is guaranteed to always bypass the cache
if parse_state.source == 'script-message' then return false end
if not fb.get_opt('cache') or directory == '' then return false end
-- clear the cache if reloading the current directory in the browser
-- this means that fb.rescan() should maintain expected behaviour
if parse_state.source == 'browser' then
if prev_directory == directory then clear_cache({directory}) end
prev_directory = directory
end
return true
end
---@async
function cacheParser:parse(directory)
if cache[directory] then
msg.verbose('fetching', directory, 'contents from cache')
cache[directory].timeout:kill()
cache[directory].timeout:resume()
return cache[directory].list, cache[directory].opts
end
---@type List?, Opts?
local list, opts
-- if another parse is already running on the same directory, then wait and use the same result
if not pending_parses[directory] then
pending_parses[directory] = {}
list, opts = self:defer(directory)
else
msg.debug('parse for', directory, 'already running - waiting for other parse to finish...')
table.insert(pending_parses[directory], fb.coroutine.callback(30))
list, opts = coroutine.yield()
end
local pending = pending_parses[directory]
-- need to clear the pending parses before resuming them or they will also attempt to resume the parses
pending_parses[directory] = nil
if pending and #pending > 0 then
msg.debug('resuming', #pending, 'pending parses for', directory)
for _, cb in ipairs(pending) do
cb(list, opts)
end
end
if not list then return end
-- pending will be truthy for the original parse and falsy for any parses that were pending
if pending then
msg.debug('storing', directory, 'contents in cache')
cache[directory] = {
list = list,
opts = opts,
timeout = mp.add_timeout(120, function() cache[directory] = nil end),
}
end
return list, opts
end
cacheParser.keybinds = {
{
key = 'Ctrl+Shift+r',
name = 'clear',
command = function() clear_cache() ; fb.rescan() end,
}
}
-- provide method of clearing the cache through script messages
mp.register_script_message('cache/clear', function(dirs)
if not dirs then
return clear_cache()
end
---@type string[]?
local directories = utils.parse_json(dirs)
if not directories then msg.error('unable to parse', dirs) end
clear_cache(directories)
end)
return cacheParser