Skip to content

Commit

Permalink
perf: minimize meta rebuild when loading specs
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed Jun 24, 2024
1 parent 972baa6 commit 1446f6c
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 13 deletions.
10 changes: 8 additions & 2 deletions lua/lazy/core/loader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ function M.colorscheme(name)
end

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
plugin._.rtp_loaded = true
-- don't load if:
Expand All @@ -545,8 +545,14 @@ end

---@param modname string
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]

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
M.auto_load(modname, ret.modpath)
local mod = package.loaded[modname]
Expand Down
16 changes: 15 additions & 1 deletion lua/lazy/core/meta.lua
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ end
--- Rebuild all plugins based on dirty fragments,
--- or dirty plugins. Will remove plugins that no longer have fragments.
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
local meta = self.frag_to_meta[fid]
if meta then
Expand All @@ -143,13 +151,20 @@ function M:rebuild()
for n, _ in pairs(self.dirty) do
self:_rebuild(n)
end
if Config.options.debug then
Util.track()
end
end

--- Rebuild a single plugin.
--- This will resolve the plugin based on its fragments using metatables.
--- This also resolves dependencies, dep, optional, dir, dev, and url.
---@param name string
function M:_rebuild(name)
if not self.dirty[name] then
return
end
self.dirty[name] = nil
local plugin = self.plugins[name]
if not plugin or #plugin._.frags == 0 then
self.plugins[name] = nil
Expand Down Expand Up @@ -225,7 +240,6 @@ function M:_rebuild(name)

setmetatable(plugin, { __index = super })

self.dirty[plugin.name] = nil
return plugin
end

Expand Down
11 changes: 9 additions & 2 deletions lua/lazy/core/plugin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -362,17 +362,24 @@ end

-- Finds the plugin that has this path
---@param path string
function M.find(path)
---@param opts? {fast?:boolean}
function M.find(path, opts)
if not Config.spec then
return
end
opts = opts or {}
local lua = path:find("/lua/", 1, true)
if lua then
local name = path:sub(1, lua - 1)
local slash = name:reverse():find("/", 1, true)
if slash then
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
Expand Down
43 changes: 35 additions & 8 deletions lua/lazy/core/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -238,34 +238,61 @@ function M.walkmods(root, fn, modname)
end

---@param modname string
function M.get_unloaded_rtp(modname)
modname = modname:gsub("/", ".")
local idx = modname:find(".", 1, true)
local topmod = idx and modname:sub(1, idx - 1) or modname
topmod = M.normname(topmod)
---@return string
function M.topmod(modname)
return modname:match("^[^./]+") or modname
end

---@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 Config = require("lazy.core.config")
if Config.spec then
for _, plugin in pairs(Config.spec.plugins) do
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)
else
table.insert(rtp, plugin.dir)
end
end
end
end
return rtp
M.unloaded_cache[topmod] = rtp
return rtp, false
end

function M.find_root(modname)
local paths, cached = M.get_unloaded_rtp(modname, { cache = true })

local ret = require("lazy.core.cache").find(modname, {
rtp = true,
paths = M.get_unloaded_rtp(modname),
paths = paths,
patterns = { "", ".lua" },
})[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
local root = ret.modpath:gsub("/init%.lua$", ""):gsub("%.lua$", "")
return root
Expand Down

0 comments on commit 1446f6c

Please sign in to comment.