perf: minimize meta rebuild when loading specs

This commit is contained in:
Folke Lemaitre 2024-06-24 21:56:43 +02:00
parent 972baa615b
commit 1446f6cfbb
No known key found for this signature in database
GPG Key ID: 41F8B1FBACAE2040
4 changed files with 67 additions and 13 deletions

View File

@ -524,7 +524,7 @@ function M.colorscheme(name)
end end
function M.auto_load(modname, modpath) function M.auto_load(modname, modpath)
local plugin = Plugin.find(modpath) local plugin = Plugin.find(modpath, { fast = not M.did_handlers })
if plugin and modpath:find(plugin.dir, 1, true) == 1 then if plugin and modpath:find(plugin.dir, 1, true) == 1 then
plugin._.rtp_loaded = true plugin._.rtp_loaded = true
-- don't load if: -- don't load if:
@ -545,8 +545,14 @@ end
---@param modname string ---@param modname string
function M.loader(modname) function M.loader(modname)
local paths = Util.get_unloaded_rtp(modname) local paths, cached = Util.get_unloaded_rtp(modname, { cache = true })
local ret = Cache.find(modname, { rtp = false, paths = paths })[1] local ret = Cache.find(modname, { rtp = false, paths = paths })[1]
if not ret and cached then
paths = Util.get_unloaded_rtp(modname)
ret = Cache.find(modname, { rtp = false, paths = paths })[1]
end
if ret then if ret then
M.auto_load(modname, ret.modpath) M.auto_load(modname, ret.modpath)
local mod = package.loaded[modname] local mod = package.loaded[modname]

View File

@ -117,6 +117,14 @@ end
--- Rebuild all plugins based on dirty fragments, --- Rebuild all plugins based on dirty fragments,
--- or dirty plugins. Will remove plugins that no longer have fragments. --- or dirty plugins. Will remove plugins that no longer have fragments.
function M:rebuild() function M:rebuild()
local frag_count = vim.tbl_count(self.fragments.dirty)
local plugin_count = vim.tbl_count(self.dirty)
if frag_count == 0 and plugin_count == 0 then
return
end
if Config.options.debug then
Util.track("rebuild plugins frags=" .. frag_count .. " plugins=" .. plugin_count)
end
for fid in pairs(self.fragments.dirty) do for fid in pairs(self.fragments.dirty) do
local meta = self.frag_to_meta[fid] local meta = self.frag_to_meta[fid]
if meta then if meta then
@ -143,6 +151,9 @@ function M:rebuild()
for n, _ in pairs(self.dirty) do for n, _ in pairs(self.dirty) do
self:_rebuild(n) self:_rebuild(n)
end end
if Config.options.debug then
Util.track()
end
end end
--- Rebuild a single plugin. --- Rebuild a single plugin.
@ -150,6 +161,10 @@ end
--- This also resolves dependencies, dep, optional, dir, dev, and url. --- This also resolves dependencies, dep, optional, dir, dev, and url.
---@param name string ---@param name string
function M:_rebuild(name) function M:_rebuild(name)
if not self.dirty[name] then
return
end
self.dirty[name] = nil
local plugin = self.plugins[name] local plugin = self.plugins[name]
if not plugin or #plugin._.frags == 0 then if not plugin or #plugin._.frags == 0 then
self.plugins[name] = nil self.plugins[name] = nil
@ -225,7 +240,6 @@ function M:_rebuild(name)
setmetatable(plugin, { __index = super }) setmetatable(plugin, { __index = super })
self.dirty[plugin.name] = nil
return plugin return plugin
end end

View File

@ -362,17 +362,24 @@ end
-- Finds the plugin that has this path -- Finds the plugin that has this path
---@param path string ---@param path string
function M.find(path) ---@param opts? {fast?:boolean}
function M.find(path, opts)
if not Config.spec then if not Config.spec then
return return
end end
opts = opts or {}
local lua = path:find("/lua/", 1, true) local lua = path:find("/lua/", 1, true)
if lua then if lua then
local name = path:sub(1, lua - 1) local name = path:sub(1, lua - 1)
local slash = name:reverse():find("/", 1, true) local slash = name:reverse():find("/", 1, true)
if slash then if slash then
name = name:sub(#name - slash + 2) name = name:sub(#name - slash + 2)
return name and Config.plugins[name] or Config.spec.plugins[name] or nil if name then
if opts.fast then
return Config.spec.meta.plugins[name]
end
return Config.spec.plugins[name]
end
end end
end end
end end

View File

@ -238,18 +238,33 @@ function M.walkmods(root, fn, modname)
end end
---@param modname string ---@param modname string
function M.get_unloaded_rtp(modname) ---@return string
modname = modname:gsub("/", ".") function M.topmod(modname)
local idx = modname:find(".", 1, true) return modname:match("^[^./]+") or modname
local topmod = idx and modname:sub(1, idx - 1) or modname end
topmod = M.normname(topmod)
---@type table<string, string[]>
M.unloaded_cache = {}
---@param modname string
---@param opts? {cache?:boolean}
function M.get_unloaded_rtp(modname, opts)
opts = opts or {}
local topmod = M.topmod(modname)
if opts.cache and M.unloaded_cache[topmod] then
return M.unloaded_cache[topmod], true
end
local norm = M.normname(topmod)
---@type string[]
local rtp = {} local rtp = {}
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
if Config.spec then if Config.spec then
for _, plugin in pairs(Config.spec.plugins) do for _, plugin in pairs(Config.spec.plugins) do
if not (plugin._.loaded or plugin.module == false) then if not (plugin._.loaded or plugin.module == false) then
if topmod == M.normname(plugin.name) then if norm == M.normname(plugin.name) then
table.insert(rtp, 1, plugin.dir) table.insert(rtp, 1, plugin.dir)
else else
table.insert(rtp, plugin.dir) table.insert(rtp, plugin.dir)
@ -257,15 +272,27 @@ function M.get_unloaded_rtp(modname)
end end
end end
end end
return rtp M.unloaded_cache[topmod] = rtp
return rtp, false
end end
function M.find_root(modname) function M.find_root(modname)
local paths, cached = M.get_unloaded_rtp(modname, { cache = true })
local ret = require("lazy.core.cache").find(modname, { local ret = require("lazy.core.cache").find(modname, {
rtp = true, rtp = true,
paths = M.get_unloaded_rtp(modname), paths = paths,
patterns = { "", ".lua" }, patterns = { "", ".lua" },
})[1] })[1]
if not ret and cached then
paths = M.get_unloaded_rtp(modname)
ret = require("lazy.core.cache").find(modname, {
rtp = false,
paths = paths,
patterns = { "", ".lua" },
})[1]
end
if ret then if ret then
local root = ret.modpath:gsub("/init%.lua$", ""):gsub("%.lua$", "") local root = ret.modpath:gsub("/init%.lua$", ""):gsub("%.lua$", "")
return root return root