fix(fragments): prevent adding the same spec instance more than once

This commit is contained in:
Folke Lemaitre 2024-06-24 16:42:57 +02:00
parent fd04bc62f9
commit dbffad6f44
5 changed files with 65 additions and 37 deletions

View File

@ -1,13 +1,5 @@
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local Util = require("lazy.core.util")
local M = {}
M._fid = 0
local function next_id()
M._fid = M._fid + 1
return M._fid
end
--- This class is used to manage the fragments of a plugin spec. --- This class is used to manage the fragments of a plugin spec.
--- It keeps track of the fragments and their relations to other fragments. --- It keeps track of the fragments and their relations to other fragments.
@ -17,30 +9,39 @@ end
---@field frag_stack number[] ---@field frag_stack number[]
---@field dep_stack number[] ---@field dep_stack number[]
---@field dirty table<number, boolean> ---@field dirty table<number, boolean>
---@field plugins table<LazyPlugin, number>
---@field spec LazySpecLoader ---@field spec LazySpecLoader
local F = {} local M = {}
M._fid = 0
local function next_id()
M._fid = M._fid + 1
return M._fid
end
---@param spec LazySpecLoader ---@param spec LazySpecLoader
---@return LazyFragments ---@return LazyFragments
function M.new(spec) function M.new(spec)
local self = setmetatable({}, { __index = F }) local self = setmetatable({}, { __index = M })
self.fragments = {} self.fragments = {}
self.frag_stack = {} self.frag_stack = {}
self.dep_stack = {} self.dep_stack = {}
self.spec = spec self.spec = spec
self.dirty = {} self.dirty = {}
self.plugins = {}
return self return self
end end
---@param id number ---@param id number
function F:get(id) function M:get(id)
return self.fragments[id] return self.fragments[id]
end end
--- Remove a fragment and all its children. --- Remove a fragment and all its children.
--- This will also remove the fragment from its parent's children list. --- This will also remove the fragment from its parent's children list.
---@param id number ---@param id number
function F:del(id) function M:del(id)
-- del fragment -- del fragment
local fragment = self.fragments[id] local fragment = self.fragments[id]
if not fragment then if not fragment then
@ -55,13 +56,13 @@ function F:del(id)
local parent = self.fragments[pid] local parent = self.fragments[pid]
if parent.frags then if parent.frags then
---@param fid number ---@param fid number
parent.frags = vim.tbl_filter(function(fid) parent.frags = Util.filter(function(fid)
return fid ~= id return fid ~= id
end, parent.frags) end, parent.frags)
end end
if parent.deps then if parent.deps then
---@param fid number ---@param fid number
parent.deps = vim.tbl_filter(function(fid) parent.deps = Util.filter(function(fid)
return fid ~= id return fid ~= id
end, parent.deps) end, parent.deps)
end end
@ -81,8 +82,15 @@ end
--- Add a fragment to the fragments list. --- Add a fragment to the fragments list.
--- This also resolves its name, url, dir, dependencies and child specs. --- This also resolves its name, url, dir, dependencies and child specs.
---@param plugin LazyPluginSpec ---@param plugin LazyPluginSpec
function F:add(plugin) function M:add(plugin)
if self.plugins[plugin] then
return self.fragments[self.plugins[plugin]]
end
local id = next_id() local id = next_id()
setmetatable(plugin, nil)
self.plugins[plugin] = id
local pid = self.frag_stack[#self.frag_stack] local pid = self.frag_stack[#self.frag_stack]

View File

@ -33,8 +33,7 @@ function M:load_pkgs()
if not Config.options.pkg.enabled then if not Config.options.pkg.enabled then
return return
end end
local specs = Pkg.get() for _, pkg in ipairs(Pkg.get()) do
for dir, pkg in pairs(specs) do
local meta, fragment = self:add(pkg.spec) local meta, fragment = self:add(pkg.spec)
if meta and fragment then if meta and fragment then
meta._.pkg = pkg meta._.pkg = pkg
@ -44,7 +43,7 @@ function M:load_pkgs()
frag.spec.optional = true frag.spec.optional = true
end end
-- keep track of the top-level package fragment -- keep track of the top-level package fragment
self.pkgs[dir] = fragment.id self.pkgs[pkg.dir] = fragment.id
end end
end end
end end
@ -128,7 +127,7 @@ function M:rebuild()
-- fragment was deleted, so remove it from plugin -- fragment was deleted, so remove it from plugin
self.frag_to_meta[fid] = nil self.frag_to_meta[fid] = nil
---@param f number ---@param f number
meta._.frags = vim.tbl_filter(function(f) meta._.frags = Util.filter(function(f)
return f ~= fid return f ~= fid
end, meta._.frags) end, meta._.frags)
-- if no fragments left, delete plugin -- if no fragments left, delete plugin
@ -167,10 +166,10 @@ function M:_rebuild(name)
assert(#plugin._.frags > 0, "no fragments found for plugin " .. name) assert(#plugin._.frags > 0, "no fragments found for plugin " .. name)
---@type table<number, boolean> ---@type table<number, boolean>
local done = {} local added = {}
for _, fid in ipairs(plugin._.frags) do for _, fid in ipairs(plugin._.frags) do
if not done[fid] then if not added[fid] then
done[fid] = true added[fid] = true
local fragment = self.fragments:get(fid) local fragment = self.fragments:get(fid)
assert(fragment, "fragment " .. fid .. " not found, for plugin " .. name) assert(fragment, "fragment " .. fid .. " not found, for plugin " .. name)
---@diagnostic disable-next-line: no-unknown ---@diagnostic disable-next-line: no-unknown

View File

@ -170,10 +170,10 @@ function Spec:import(spec)
self.importing = nil self.importing = nil
return self:error( return self:error(
"Invalid spec module: `" "Invalid spec module: `"
.. modname .. modname
.. "`\nExpected a `table` of specs, but a `" .. "`\nExpected a `table` of specs, but a `"
.. type(mod) .. type(mod)
.. "` was returned instead" .. "` was returned instead"
) )
end end
self:normalize(mod) self:normalize(mod)
@ -216,11 +216,11 @@ function M.update_state()
plugin._ = plugin._ or {} plugin._ = plugin._ or {}
if plugin.lazy == nil then if plugin.lazy == nil then
local lazy = plugin._.dep local lazy = plugin._.dep
or Config.options.defaults.lazy or Config.options.defaults.lazy
or plugin.event or plugin.event
or plugin.keys or plugin.keys
or plugin.ft or plugin.ft
or plugin.cmd or plugin.cmd
plugin.lazy = lazy and true or false plugin.lazy = lazy and true or false
end end
if plugin.dir:find(Config.options.root, 1, true) == 1 then if plugin.dir:find(Config.options.root, 1, true) == 1 then
@ -342,7 +342,6 @@ function M.load()
Config.plugins[name]._ = plugin._ Config.plugins[name]._ = plugin._
Config.plugins[name]._.dep = new_state.dep Config.plugins[name]._.dep = new_state.dep
Config.plugins[name]._.frags = new_state.frags Config.plugins[name]._.frags = new_state.frags
-- Config.plugins[name]._.tasks = new_state.tasks
end end
end end
Util.track() Util.track()

View File

@ -28,6 +28,20 @@ function M.track(data, time)
end end
end end
---@generic T
---@param list T[]
---@param fn fun(v: T):boolean?
---@return T[]
function M.filter(fn, list)
local ret = {}
for _, v in ipairs(list) do
if fn(v) then
table.insert(ret, v)
end
end
return ret
end
---@generic F: fun() ---@generic F: fun()
---@param data (string|{[string]:string})? ---@param data (string|{[string]:string})?
---@param fn F ---@param fn F

View File

@ -25,7 +25,7 @@ M.dirty = false
---@field pkgs LazyPkg[] ---@field pkgs LazyPkg[]
---@field version number ---@field version number
---@type table<string, LazyPkg>? ---@type LazyPkg[]?
M.cache = nil M.cache = nil
function M.update() function M.update()
@ -65,6 +65,9 @@ function M.update()
end end
end end
end end
table.sort(ret.pkgs, function(a, b)
return a.name < b.name
end)
local code = "return " .. Util.dump(ret) local code = "return " .. Util.dump(ret)
vim.fn.mkdir(vim.fn.fnamemodify(Config.options.pkg.cache, ":h"), "p") vim.fn.mkdir(vim.fn.fnamemodify(Config.options.pkg.cache, ":h"), "p")
Util.write_file(Config.options.pkg.cache, code) Util.write_file(Config.options.pkg.cache, code)
@ -91,8 +94,8 @@ local function _load()
end end
-- wrap in the scope of the plugin -- wrap in the scope of the plugin
pkg.spec = { pkg.name, specs = pkg.spec } pkg.spec = { pkg.name, specs = pkg.spec }
M.cache[pkg.dir] = pkg
end end
M.cache = ret.pkgs
end end
end, "Error loading pkg:") end, "Error loading pkg:")
end end
@ -107,10 +110,15 @@ end
---@param dir string ---@param dir string
---@return LazyPkg? ---@return LazyPkg?
---@overload fun():table<string, LazyPkg> ---@overload fun():LazyPkg[]
function M.get(dir) function M.get(dir)
if dir then if dir then
return M.cache[dir] for _, pkg in ipairs(M.cache) do
if pkg.dir == dir then
return pkg
end
end
return
end end
return M.cache return M.cache
end end