fix(pkg): correctly pre-load package specs and remove them when needed during resolve

This commit is contained in:
Folke Lemaitre 2024-06-23 17:49:19 +02:00
parent d7bc9ce11a
commit 93552cd18e
No known key found for this signature in database
GPG Key ID: 41F8B1FBACAE2040
6 changed files with 87 additions and 38 deletions

View File

@ -1,4 +1,5 @@
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local Pkg = require("lazy.pkg")
local Util = require("lazy.core.util") local Util = require("lazy.core.util")
--- This class is used to manage the plugins. --- This class is used to manage the plugins.
@ -10,6 +11,7 @@ local Util = require("lazy.core.util")
---@field dirty table<string, boolean> ---@field dirty table<string, boolean>
---@field spec LazySpecLoader ---@field spec LazySpecLoader
---@field fragments LazyFragments ---@field fragments LazyFragments
---@field pkgs table<string, number>
local M = {} local M = {}
---@param spec LazySpecLoader ---@param spec LazySpecLoader
@ -22,9 +24,30 @@ function M.new(spec)
self.frag_to_meta = {} self.frag_to_meta = {}
self.str_to_meta = {} self.str_to_meta = {}
self.dirty = {} self.dirty = {}
self.pkgs = {}
return self return self
end end
-- import package specs
function M:load_pkgs()
if not Config.options.pkg.enabled then
return
end
local specs = Pkg.spec()
for dir, spec in pairs(specs) do
local meta, fragment = self:add(spec)
if meta and fragment then
-- tag all package fragments as optional
for _, fid in ipairs(meta._.frags) do
local frag = self.fragments:get(fid)
frag.spec.optional = true
end
-- keep track of the top-level package fragment
self.pkgs[dir] = fragment.id
end
end
end
--- Remove a plugin and all its fragments. --- Remove a plugin and all its fragments.
---@param name string ---@param name string
function M:del(name) function M:del(name)
@ -88,6 +111,7 @@ function M:add(plugin)
self.plugins[meta.name] = meta self.plugins[meta.name] = meta
self.frag_to_meta[fragment.id] = meta self.frag_to_meta[fragment.id] = meta
self.dirty[meta.name] = true self.dirty[meta.name] = true
return meta, fragment
end end
--- Rebuild all plugins based on dirty fragments, --- Rebuild all plugins based on dirty fragments,
@ -101,6 +125,7 @@ function M:rebuild()
self.dirty[meta.name] = true self.dirty[meta.name] = true
else else
-- fragment was deleted, so remove it from plugin -- fragment was deleted, so remove it from plugin
self.frag_to_meta[fid] = nil
---@param f number ---@param f number
meta._.frags = vim.tbl_filter(function(f) meta._.frags = vim.tbl_filter(function(f)
return f ~= fid return f ~= fid
@ -260,11 +285,28 @@ function M:fix_disabled()
return changes return changes
end end
--- Removes package fragments for plugins that no longer use the same directory.
function M:fix_pkgs()
for dir, fid in pairs(self.pkgs) do
local plugin = self.frag_to_meta[fid]
plugin = plugin and self.plugins[plugin.name]
if plugin then
-- check if plugin is still in the same directory
if plugin.dir ~= dir then
self.fragments:del(fid)
end
end
end
self:rebuild()
end
--- Resolve all plugins, based on cond, enabled and optional. --- Resolve all plugins, based on cond, enabled and optional.
function M:resolve() function M:resolve()
Util.track("resolve plugins") Util.track("resolve plugins")
self:rebuild() self:rebuild()
self:fix_pkgs()
self:fix_cond() self:fix_cond()
-- selene: allow(empty_loop) -- selene: allow(empty_loop)

View File

@ -1,6 +1,5 @@
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local Meta = require("lazy.core.meta") local Meta = require("lazy.core.meta")
local Pkg = require("lazy.pkg")
local Util = require("lazy.core.util") local Util = require("lazy.core.util")
---@class LazyCorePlugin ---@class LazyCorePlugin
@ -16,7 +15,6 @@ M.loading = false
---@field notifs {msg:string, level:number, file?:string}[] ---@field notifs {msg:string, level:number, file?:string}[]
---@field importing? string ---@field importing? string
---@field optional? boolean ---@field optional? boolean
---@field pkgs table<string, boolean>
local Spec = {} local Spec = {}
M.Spec = Spec M.Spec = Spec
M.LOCAL_SPEC = ".lazy.lua" M.LOCAL_SPEC = ".lazy.lua"
@ -30,8 +28,8 @@ function Spec.new(spec, opts)
self.modules = {} self.modules = {}
self.notifs = {} self.notifs = {}
self.ignore_installed = {} self.ignore_installed = {}
self.pkgs = {}
self.optional = opts and opts.optional self.optional = opts and opts.optional
self.meta:load_pkgs()
if spec then if spec then
self:parse(spec) self:parse(spec)
end end
@ -62,25 +60,6 @@ function Spec.get_name(pkg)
return slash and name:sub(#name - slash + 2) or pkg:gsub("%W+", "_") return slash and name:sub(#name - slash + 2) or pkg:gsub("%W+", "_")
end end
---@param plugin LazyPluginSpec
function Spec:add(plugin)
self.meta:add(plugin)
---@diagnostic disable-next-line: cast-type-mismatch
---@cast plugin LazyPlugin
-- import the plugin's spec
if Config.options.pkg.enabled and plugin.dir and not self.pkgs[plugin.dir] then
self.pkgs[plugin.dir] = true
local pkg = Pkg.get_spec(plugin)
if pkg then
self:normalize(pkg)
end
end
return plugin
end
function Spec:error(msg) function Spec:error(msg)
self:log(msg, vim.log.levels.ERROR) self:log(msg, vim.log.levels.ERROR)
end end
@ -110,7 +89,7 @@ end
---@param spec LazySpec|LazySpecImport ---@param spec LazySpec|LazySpecImport
function Spec:normalize(spec) function Spec:normalize(spec)
if type(spec) == "string" then if type(spec) == "string" then
self:add({ spec }) self.meta:add({ spec })
elseif #spec > 1 or Util.is_list(spec) then elseif #spec > 1 or Util.is_list(spec) then
---@cast spec LazySpec[] ---@cast spec LazySpec[]
for _, s in ipairs(spec) do for _, s in ipairs(spec) do
@ -118,11 +97,11 @@ function Spec:normalize(spec)
end end
elseif spec[1] or spec.dir or spec.url then elseif spec[1] or spec.dir or spec.url then
---@cast spec LazyPluginSpec ---@cast spec LazyPluginSpec
local plugin = self:add(spec) self.meta:add(spec)
---@diagnostic disable-next-line: cast-type-mismatch ---@diagnostic disable-next-line: cast-type-mismatch
---@cast plugin LazySpecImport ---@cast spec LazySpecImport
if plugin and plugin.import then if spec and spec.import then
self:import(plugin) self:import(spec)
end end
elseif spec.import then elseif spec.import then
---@cast spec LazySpecImport ---@cast spec LazySpecImport

View File

@ -7,6 +7,7 @@ local M = {}
---@class LazyPkg ---@class LazyPkg
---@field source string ---@field source string
---@field name string
---@field file string ---@field file string
---@field spec? LazySpec ---@field spec? LazySpec
---@field chunk? string|fun():LazySpec ---@field chunk? string|fun():LazySpec
@ -30,6 +31,7 @@ function M.update()
for _, source in ipairs(sources) do for _, source in ipairs(sources) do
local spec = source.get(plugin) local spec = source.get(plugin)
if spec then if spec then
spec.name = plugin.name
if type(spec.chunk) == "function" then if type(spec.chunk) == "function" then
spec.chunk = string.dump(spec.chunk) spec.chunk = string.dump(spec.chunk)
end end
@ -81,6 +83,25 @@ function M.get(plugin)
return ret return ret
end end
function M.spec()
if not M.cache then
_load()
end
---@type table<string,LazyPluginSpec>
local ret = {}
for dir in pairs(M.cache) do
local pkg = M.get({ dir = dir })
local spec = pkg and pkg.spec
if pkg and spec then
spec = type(spec) == "table" and vim.deepcopy(spec) or spec
ret[dir] = { pkg.name, specs = spec }
end
end
return ret
end
---@param plugin LazyPlugin ---@param plugin LazyPlugin
---@return LazySpec? ---@return LazySpec?
function M.get_spec(plugin) function M.get_spec(plugin)

View File

@ -29,24 +29,27 @@ function M.get(plugin)
end end
---@type RockSpec? ---@type RockSpec?
---@diagnostic disable-next-line: missing-fields
local rockspec = {} local rockspec = {}
local ret, err = loadfile(rockspec_file, "t", rockspec) local load, err = loadfile(rockspec_file, "t", rockspec)
if not ret then if not load then
error(err) error(err)
end end
ret() load()
return rockspec
and rockspec.package ---@param dep string
local rocks = vim.tbl_filter(function(dep)
local name = dep:gsub("%s.*", "")
return not vim.tbl_contains(M.skip, name)
end, rockspec and rockspec.dependencies or {})
return #rocks > 0
and { and {
source = "rockspec", source = "rockspec",
file = rockspec_file, file = rockspec_file,
spec = { spec = {
dir = plugin.dir, plugin.name,
url = plugin.url, rocks = rocks,
rocks = vim.tbl_filter(function(dep)
local name = dep:gsub("%s.*", "")
return not vim.tbl_contains(M.skip, name)
end, rockspec.dependencies),
}, },
} }
or nil or nil

View File

@ -86,6 +86,7 @@
---@class LazyFragment ---@class LazyFragment
---@field id number ---@field id number
---@field pkg? boolean
---@field pid? number ---@field pid? number
---@field deps? number[] ---@field deps? number[]
---@field frags? number[] ---@field frags? number[]

View File

@ -1 +1,4 @@
std="vim" std="vim"
[lints]
mixed_table="allow"