feat(pack)!: make del() only remove non-active plugins by default

Problem: Using `vim.pack.del()` to delete active plugin can lead to
  a situation when this plugin is reinstalled after restart. Removing
  plugin from 'init.lua' is documented, but can be missed.

Solution: Make `del()` only remove non-active plugins by default and
  throw an informative error if there is an active plugin.

  Add a way to force delete any plugin by adding `opts.force`. This also
  makes `del()` signature be the same as other functions, which is nice.
This commit is contained in:
Evgeni Chasnovski
2025-12-28 16:00:40 +02:00
parent c53fb58c15
commit f5707a9c42
4 changed files with 88 additions and 34 deletions

View File

@@ -126,8 +126,10 @@
---
---Remove plugins from disk ~
---
---- Use |vim.pack.del()| with a list of plugin names to remove. Make sure their specs
---are not included in |vim.pack.add()| call in 'init.lua' or they will be reinstalled.
---- Remove plugin specs from |vim.pack.add()| calls in 'init.lua' or they will be
--- reinstalled later.
---- |:restart|.
---- Use |vim.pack.del()| with a list of plugin names to remove.
---
---[vim.pack-events]()
---
@@ -1256,12 +1258,18 @@ function M.update(names, opts)
end)
end
--- @class vim.pack.keyset.del
--- @inlinedoc
--- @field force? boolean Whether to allow deleting an active plugin. Default `false`.
--- Remove plugins from disk
---
--- @param names string[] List of plugin names to remove from disk. Must be managed
--- by |vim.pack|, not necessarily already added to current session.
function M.del(names)
--- @param opts? vim.pack.keyset.del
function M.del(names, opts)
vim.validate('names', names, vim.islist, false, 'list')
opts = vim.tbl_extend('force', { force = false }, opts or {})
local plug_list = plug_list_from_names(names)
if #plug_list == 0 then
@@ -1271,19 +1279,31 @@ function M.del(names)
lock_read()
local fail_to_delete = {} --- @type string[]
for _, p in ipairs(plug_list) do
trigger_event(p, 'PackChangedPre', 'delete')
if not active_plugins[p.path] or opts.force then
trigger_event(p, 'PackChangedPre', 'delete')
vim.fs.rm(p.path, { recursive = true, force = true })
active_plugins[p.path] = nil
notify(("Removed plugin '%s'"):format(p.spec.name), 'INFO')
vim.fs.rm(p.path, { recursive = true, force = true })
active_plugins[p.path] = nil
notify(("Removed plugin '%s'"):format(p.spec.name), 'INFO')
plugin_lock.plugins[p.spec.name] = nil
plugin_lock.plugins[p.spec.name] = nil
trigger_event(p, 'PackChanged', 'delete')
trigger_event(p, 'PackChanged', 'delete')
else
fail_to_delete[#fail_to_delete + 1] = p.spec.name
end
end
lock_write()
if #fail_to_delete > 0 then
local plugs = table.concat(fail_to_delete, ', ')
local msg = ('Some plugins are active and were not deleted: %s.'):format(plugs)
.. ' Remove them from init.lua, restart, and try again.'
error(msg)
end
end
--- @inlinedoc