local Config = require("lazy.core.config") 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. --- It keeps track of the fragments and their relations to other fragments. --- A fragment can be a dependency (dependencies) or a child (specs) of another fragment. ---@class LazyFragments ---@field fragments table ---@field frag_stack number[] ---@field dep_stack number[] ---@field dirty table ---@field spec LazySpecLoader local F = {} ---@param spec LazySpecLoader ---@return LazyFragments function M.new(spec) local self = setmetatable({}, { __index = F }) self.fragments = {} self.frag_stack = {} self.dep_stack = {} self.spec = spec self.dirty = {} return self end ---@param id number function F:get(id) return self.fragments[id] end --- Remove a fragment and all its children. --- This will also remove the fragment from its parent's children list. ---@param id number function F:del(id) -- del fragment local fragment = self.fragments[id] if not fragment then return end self.dirty[id] = true -- remove from parent local pid = fragment.pid if pid then local parent = self.fragments[pid] if parent.frags then ---@param fid number parent.frags = vim.tbl_filter(function(fid) return fid ~= id end, parent.frags) end if parent.deps then ---@param fid number parent.deps = vim.tbl_filter(function(fid) return fid ~= id end, parent.deps) end self.dirty[pid] = true end -- remove children if fragment.frags then for _, fid in ipairs(fragment.frags) do self:del(fid) end end self.fragments[id] = nil end --- Add a fragment to the fragments list. --- This also resolves its name, url, dir, dependencies and child specs. ---@param plugin LazyPluginSpec function F:add(plugin) local id = next_id() local pid = self.frag_stack[#self.frag_stack] ---@type LazyFragment local fragment = { id = id, pid = pid, name = plugin.name, url = plugin.url, dir = plugin.dir, spec = plugin --[[@as LazyPlugin]], } -- short url / ref if plugin[1] then local slash = plugin[1]:find("/", 1, true) if slash then local prefix = plugin[1]:sub(1, 4) if prefix == "http" or prefix == "git@" then fragment.url = fragment.url or plugin[1] else fragment.name = fragment.name or plugin[1]:sub(slash + 1) fragment.url = fragment.url or Config.options.git.url_format:format(plugin[1]) end else fragment.name = fragment.name or plugin[1] end end -- name fragment.name = fragment.name or fragment.url and self.spec.get_name(fragment.url) or fragment.dir and self.spec.get_name(fragment.dir) if not fragment.name then return self.spec:error("Invalid plugin spec " .. vim.inspect(plugin)) end if type(plugin.config) == "table" then self.spec:warn( "{" .. fragment.name .. "}: setting a table to `Plugin.config` is deprecated. Please use `Plugin.opts` instead" ) ---@diagnostic disable-next-line: assign-type-mismatch plugin.opts = plugin.config plugin.config = nil end self.fragments[id] = fragment -- add to parent if pid then local parent = self.fragments[pid] parent.frags = parent.frags or {} table.insert(parent.frags, id) end -- add to parent's deps local did = self.dep_stack[#self.dep_stack] if did and did == pid then fragment.dep = true local parent = self.fragments[did] parent.deps = parent.deps or {} table.insert(parent.deps, id) end table.insert(self.frag_stack, id) -- dependencies if plugin.dependencies then table.insert(self.dep_stack, id) self.spec:normalize(plugin.dependencies) table.remove(self.dep_stack) end -- child specs if plugin.specs then self.spec:normalize(plugin.specs) end table.remove(self.frag_stack) return fragment end return M