From f95c7a8be12b0121bf8fef39d3951a707ced44e9 Mon Sep 17 00:00:00 2001 From: abeldekat Date: Mon, 31 Jul 2023 20:24:59 +0200 Subject: [PATCH] discard_deps: Repair active plugins Plugins that are unused currently can still contribute to active plugins by the specs added to their "dependencies" field. This commit adds functionality to the "parse" method, repairing only the active plugins that could be affected by the aforementioned specs. This can be achieved by maintaining an extra administration table containing a copy of each added plugin instance. --- lua/lazy/core/plugin.lua | 101 ++++++++++++++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/lua/lazy/core/plugin.lua b/lua/lazy/core/plugin.lua index f141d27..d2eb1cc 100644 --- a/lua/lazy/core/plugin.lua +++ b/lua/lazy/core/plugin.lua @@ -7,7 +7,9 @@ local M = {} M.loading = false ---@class LazySpecLoader +---@field repair_info table ---@field plugins table +---@field optional_only table ---@field disabled table ---@field modules string[] ---@field notifs {msg:string, level:number, file?:string}[] @@ -20,7 +22,9 @@ M.Spec = Spec ---@param opts? {optional?:boolean} function Spec.new(spec, opts) local self = setmetatable({}, { __index = Spec }) + self.repair_info = {} self.plugins = {} + self.optional_only = {} self.disabled = {} self.modules = {} self.notifs = {} @@ -32,18 +36,75 @@ function Spec.new(spec, opts) end function Spec:parse(spec) + -- spec -> self.plugins self:normalize(spec) + -- self.plugins -> self.optional_only, self.disabled + self:fix_disabled() - -- calculate handlers - for _, plugin in pairs(self.plugins) do + if self:has_unused_plugins() then + self:fix_active() + end + + self:calculate_handlers(self.plugins) + self:calculate_handlers(self.disabled) +end + +function Spec:has_unused_plugins() + return not (vim.tbl_isempty(self.optional_only) and vim.tbl_isempty(self.disabled)) +end + +function Spec:fix_active() + ---@type table + local unused_plugins = vim.tbl_extend("keep", self.optional_only, self.disabled) + + ---@type string[] + local candidates = {} + for _, plugin in pairs(unused_plugins) do + if plugin.dependencies then + vim.list_extend(candidates, plugin.dependencies) + end + end + + -- merge candidate again without instances supplied by unused plugins + for _, candidate in pairs(candidates) do + local fix = false + local instances = vim.tbl_filter(function(instance) + if instance._.parent_name and unused_plugins[instance._.parent_name] then + fix = true + return false + end + return instance + end, self.repair_info[candidate]) + if fix then + self.plugins[candidate] = self:fix_merge(instances) + end + end +end + +---@param instances_of_plugin LazyPlugin[] +---@return LazyPlugin +function Spec:fix_merge(instances_of_plugin) + local last + for index, inst in ipairs(instances_of_plugin) do + if index == 1 then + last = inst + else + self:merge(last, inst) + last = inst + end + end + return last +end + +---@param plugins table +function Spec:calculate_handlers(plugins) + for _, plugin in pairs(plugins) do for _, handler in pairs(Handler.types) do if plugin[handler] then plugin[handler] = M.values(plugin, handler, true) end end end - - self:fix_disabled() end -- PERF: optimized code to get package name without using lua patterns @@ -56,8 +117,8 @@ end ---@param plugin LazyPlugin ---@param results? string[] ----@param is_dep? boolean -function Spec:add(plugin, results, is_dep) +---@param parent_name? string +function Spec:add(plugin, results, parent_name) -- check if we already processed this spec. Can happen when a user uses the same instance of a spec in multiple specs -- see https://github.com/folke/lazy.nvim/issues/45 if rawget(plugin, "_") then @@ -125,9 +186,10 @@ function Spec:add(plugin, results, is_dep) end plugin._ = {} - plugin._.dep = is_dep + plugin._.dep = (parent_name ~= nil) or nil - plugin.dependencies = plugin.dependencies and self:normalize(plugin.dependencies, {}, true) or nil + plugin.dependencies = plugin.dependencies and self:normalize(plugin.dependencies, {}, plugin.name) or nil + self:add_to_repair_info(plugin, parent_name) if self.plugins[plugin.name] then plugin = self:merge(self.plugins[plugin.name], plugin) end @@ -138,6 +200,15 @@ function Spec:add(plugin, results, is_dep) return plugin end +---@param plugin LazyPlugin +---@param parent_name? string +function Spec:add_to_repair_info(plugin, parent_name) + local copy = vim.deepcopy(plugin) -- copy the instance of the plugin + copy._.parent_name = parent_name + self.repair_info[copy.name] = self.repair_info[copy.name] or {} + table.insert(self.repair_info[copy.name], copy) +end + function Spec:error(msg) self:log(msg, vim.log.levels.ERROR) end @@ -197,6 +268,7 @@ function Spec:fix_optional() for _, plugin in pairs(self.plugins) do if plugin.optional and all_optional(plugin) then self.plugins[plugin.name] = nil + self.optional_only[plugin.name] = plugin if plugin.dependencies then vim.list_extend(all_optional_deps, plugin.dependencies) end @@ -243,7 +315,9 @@ function Spec:fix_disabled() -- fix deps of plugins that are completely optional self:fix_dependencies(all_optional_deps, dep_of, function(dep_name) + local plugin = self.plugins[dep_name] self.plugins[dep_name] = nil + self.optional_only[dep_name] = plugin end) -- fix deps of disabled plugins self:fix_dependencies(disabled_deps, dep_of, function(dep_name) @@ -271,8 +345,9 @@ end ---@param spec LazySpec|LazySpecImport ---@param results? string[] ----@param is_dep? boolean -function Spec:normalize(spec, results, is_dep) +---@param parent_name? string +function Spec:normalize(spec, results, parent_name) + local is_dep = parent_name ~= nil if type(spec) == "string" then if is_dep and not spec:find("/", 1, true) then -- spec is a plugin name @@ -280,16 +355,16 @@ function Spec:normalize(spec, results, is_dep) table.insert(results, spec) end else - self:add({ spec }, results, is_dep) + self:add({ spec }, results, parent_name) end elseif #spec > 1 or Util.is_list(spec) then ---@cast spec LazySpec[] for _, s in ipairs(spec) do - self:normalize(s, results, is_dep) + self:normalize(s, results, parent_name) end elseif spec[1] or spec.dir or spec.url then ---@cast spec LazyPlugin - local plugin = self:add(spec, results, is_dep) + local plugin = self:add(spec, results, parent_name) ---@diagnostic disable-next-line: cast-type-mismatch ---@cast plugin LazySpecImport if plugin and plugin.import then