mirror of
https://github.com/neovim/neovim.git
synced 2026-05-04 13:05:05 +00:00
Merge #37142 from echasnovski/pack-safer-del
This commit is contained in:
@@ -321,9 +321,10 @@ Revert plugin after an update ~
|
||||
• When ready to deal with updating plugin, unfreeze it.
|
||||
|
||||
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*
|
||||
• *PackChangedPre* - before trying to change plugin's state.
|
||||
@@ -416,13 +417,16 @@ add({specs}, {opts}) *vim.pack.add()*
|
||||
• {confirm}? (`boolean`) Whether to ask user to confirm
|
||||
initial install. Default `true`.
|
||||
|
||||
del({names}) *vim.pack.del()*
|
||||
del({names}, {opts}) *vim.pack.del()*
|
||||
Remove plugins from disk
|
||||
|
||||
Parameters: ~
|
||||
• {names} (`string[]`) List of plugin names to remove from disk. Must
|
||||
be managed by |vim.pack|, not necessarily already added to
|
||||
current session.
|
||||
• {opts} (`table?`) A table with the following fields:
|
||||
• {force}? (`boolean`) Whether to allow deleting an active
|
||||
plugin. Default `false`.
|
||||
|
||||
get({names}, {opts}) *vim.pack.get()*
|
||||
Gets |vim.pack| plugin info, optionally filtered by `names`.
|
||||
@@ -464,7 +468,8 @@ update({names}, {opts}) *vim.pack.update()*
|
||||
details of particular pending change or newer tag.
|
||||
• 'textDocument/codeAction' (`gra` via |lsp-defaults| or
|
||||
|vim.lsp.buf.code_action()|) - show code actions available for
|
||||
"plugin at cursor". Like "delete", "update", or "skip updating".
|
||||
"plugin at cursor". Like "delete" (if plugin is not active),
|
||||
"update" or "skip updating" (if there are pending updates).
|
||||
Execute |:write| to confirm update, execute |:quit| to discard the
|
||||
update.
|
||||
• If `true`, make updates right away.
|
||||
|
||||
@@ -25,8 +25,10 @@ for i, l in ipairs(lines) do
|
||||
cur_header_hl_group = header_hl_groups[cur_group]
|
||||
hi_range(i, 0, l:len(), cur_header_hl_group)
|
||||
elseif l:find('^## (.+)$') ~= nil then
|
||||
-- Header 2
|
||||
-- Header 2 with possibly "(not active)" suffix
|
||||
hi_range(i, 0, l:len(), cur_header_hl_group)
|
||||
local col = l:match('() %(not active%)$') or l:len()
|
||||
hi_range(i, col, l:len(), 'DiagnosticError', priority + 1)
|
||||
elseif cur_info ~= nil then
|
||||
-- Plugin info
|
||||
local end_col = l:match('(). +%b()$') or l:len()
|
||||
|
||||
@@ -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]()
|
||||
---
|
||||
@@ -988,11 +990,12 @@ end
|
||||
--- @param p vim.pack.Plug
|
||||
--- @return string
|
||||
local function compute_feedback_lines_single(p)
|
||||
local active_suffix = active_plugins[p.path] ~= nil and '' or ' (not active)'
|
||||
if p.info.err ~= '' then
|
||||
return ('## %s\n\n %s'):format(p.spec.name, p.info.err:gsub('\n', '\n '))
|
||||
return ('## %s%s\n\n %s'):format(p.spec.name, active_suffix, p.info.err:gsub('\n', '\n '))
|
||||
end
|
||||
|
||||
local parts = { '## ' .. p.spec.name .. '\n' }
|
||||
local parts = { ('## %s%s\n'):format(p.spec.name, active_suffix) }
|
||||
local version_suffix = p.info.version_str == '' and '' or (' (%s)'):format(p.info.version_str)
|
||||
|
||||
if p.info.sha_head == p.info.sha_target then
|
||||
@@ -1125,7 +1128,7 @@ local function get_update_map(bufnr)
|
||||
for _, l in ipairs(lines) do
|
||||
local name = l:match('^## (.+)$')
|
||||
if name and is_in_update then
|
||||
res[name] = true
|
||||
res[name:gsub(' %(not active%)$', '')] = true
|
||||
end
|
||||
|
||||
local group = l:match('^# (%S+)')
|
||||
@@ -1158,8 +1161,9 @@ end
|
||||
--- show more information at cursor. Like details of particular pending
|
||||
--- change or newer tag.
|
||||
--- - 'textDocument/codeAction' (`gra` via |lsp-defaults| or |vim.lsp.buf.code_action()|) -
|
||||
--- show code actions available for "plugin at cursor". Like "delete", "update",
|
||||
--- or "skip updating".
|
||||
--- show code actions available for "plugin at cursor".
|
||||
--- Like "delete" (if plugin is not active), "update" or "skip updating"
|
||||
--- (if there are pending updates).
|
||||
---
|
||||
--- Execute |:write| to confirm update, execute |:quit| to discard the update.
|
||||
--- - If `true`, make updates right away.
|
||||
@@ -1256,12 +1260,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 +1281,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
|
||||
|
||||
@@ -59,7 +59,7 @@ local get_plug_data_at_lnum = function(bufnr, lnum)
|
||||
if not (from <= lnum and lnum <= to) then
|
||||
return {}
|
||||
end
|
||||
return { group = group, name = name, from = from, to = to }
|
||||
return { group = group, name = name:gsub(' %(not active%)$', ''), from = from, to = to }
|
||||
end
|
||||
|
||||
--- @alias vim.pack.lsp.Position { line: integer, character: integer }
|
||||
@@ -151,7 +151,9 @@ methods['textDocument/codeAction'] = function(params, callback)
|
||||
new_action('Skip updating', 'skip_update_plugin'),
|
||||
}, 0)
|
||||
end
|
||||
vim.list_extend(res, { new_action('Delete', 'delete_plugin') })
|
||||
if not vim.pack.get({ plug_data.name })[1].active then
|
||||
vim.list_extend(res, { new_action('Delete', 'delete_plugin') })
|
||||
end
|
||||
callback(nil, res)
|
||||
end
|
||||
|
||||
|
||||
@@ -1384,7 +1384,7 @@ describe('vim.pack', function()
|
||||
exec_lua(function()
|
||||
vim.pack.add({
|
||||
repos_src.fetch,
|
||||
{ src = repos_src.semver, version = 'v0.3.0' },
|
||||
-- No `semver` to test with non-active plugins
|
||||
{ src = repos_src.defbranch, version = 'does-not-exist' },
|
||||
})
|
||||
vim.pack.update()
|
||||
@@ -1409,7 +1409,7 @@ describe('vim.pack', function()
|
||||
{ lnum = 9, col = 1, end_lnum = 22, end_col = 1, text = '[Namespace] Update' },
|
||||
{ lnum = 11, col = 1, end_lnum = 22, end_col = 1, text = '[Module] fetch' },
|
||||
{ lnum = 22, col = 1, end_lnum = 32, end_col = 1, text = '[Namespace] Same' },
|
||||
{ lnum = 24, col = 1, end_lnum = 32, end_col = 1, text = '[Module] semver' },
|
||||
{ lnum = 24, col = 1, end_lnum = 32, end_col = 1, text = '[Module] semver (not active)' },
|
||||
}
|
||||
eq(ref_loclist, loclist)
|
||||
|
||||
@@ -1491,22 +1491,22 @@ describe('vim.pack', function()
|
||||
-- - Should not include "namespace" header as "plugin at cursor"
|
||||
assert_action({ 1, 1 }, {}, 0)
|
||||
assert_action({ 2, 0 }, {}, 0)
|
||||
-- - Only deletion should be available on errored plugin
|
||||
assert_action({ 3, 1 }, { 'Delete `defbranch`' }, 0)
|
||||
assert_action({ 7, 0 }, { 'Delete `defbranch`' }, 0)
|
||||
-- - No actions for `defbranch` since it is active and has no updates
|
||||
assert_action({ 3, 1 }, {}, 0)
|
||||
assert_action({ 7, 0 }, {}, 0)
|
||||
-- - Should not include separator blank line as "plugin at cursor"
|
||||
assert_action({ 8, 0 }, {}, 0)
|
||||
assert_action({ 9, 0 }, {}, 0)
|
||||
assert_action({ 10, 0 }, {}, 0)
|
||||
-- - Should also suggest updating related actions if updates available
|
||||
local fetch_actions = { 'Update `fetch`', 'Skip updating `fetch`', 'Delete `fetch`' }
|
||||
-- - Should suggest updating related actions if updates available
|
||||
local fetch_actions = { 'Update `fetch`', 'Skip updating `fetch`' }
|
||||
assert_action({ 11, 0 }, fetch_actions, 0)
|
||||
assert_action({ 14, 0 }, fetch_actions, 0)
|
||||
assert_action({ 20, 0 }, fetch_actions, 0)
|
||||
assert_action({ 21, 0 }, {}, 0)
|
||||
assert_action({ 22, 0 }, {}, 0)
|
||||
assert_action({ 23, 0 }, {}, 0)
|
||||
-- - Only deletion should be available on plugins without update
|
||||
-- - Only deletion should be available for not active plugins
|
||||
assert_action({ 24, 0 }, { 'Delete `semver`' }, 0)
|
||||
assert_action({ 28, 0 }, { 'Delete `semver`' }, 0)
|
||||
assert_action({ 32, 0 }, { 'Delete `semver`' }, 0)
|
||||
@@ -1516,27 +1516,26 @@ describe('vim.pack', function()
|
||||
matches(pattern, api.nvim_buf_get_lines(0, lnum - 1, lnum, false)[1])
|
||||
end
|
||||
|
||||
-- - Delete. Should remove from disk and update lockfile.
|
||||
assert_action({ 3, 0 }, { 'Delete `defbranch`' }, 1)
|
||||
eq(false, pack_exists('defbranch'))
|
||||
line_match(1, '^# Error')
|
||||
line_match(2, '^$')
|
||||
line_match(3, '^# Update')
|
||||
-- - Delete not active plugin. Should remove from disk and update lockfile.
|
||||
assert_action({ 24, 0 }, { 'Delete `semver`' }, 1)
|
||||
eq(false, pack_exists('semver'))
|
||||
line_match(22, '^# Same')
|
||||
eq(22, api.nvim_buf_line_count(0))
|
||||
|
||||
ref_lockfile.plugins.defbranch = nil
|
||||
ref_lockfile.plugins.semver = nil
|
||||
eq(ref_lockfile, get_lock_tbl())
|
||||
|
||||
-- - Skip udating
|
||||
assert_action({ 5, 0 }, fetch_actions, 2)
|
||||
assert_action({ 11, 0 }, fetch_actions, 2)
|
||||
eq('return "fetch main"', fn.readblob(fetch_lua_file))
|
||||
line_match(3, '^# Update')
|
||||
line_match(4, '^$')
|
||||
line_match(5, '^# Same')
|
||||
line_match(9, '^# Update')
|
||||
line_match(10, '^$')
|
||||
line_match(11, '^# Same')
|
||||
|
||||
-- - Update plugin. Should not re-fetch new data and update lockfile.
|
||||
n.exec('quit')
|
||||
n.exec_lua(function()
|
||||
vim.pack.update({ 'fetch', 'semver' })
|
||||
vim.pack.update({ 'fetch' })
|
||||
end)
|
||||
exec_lua('_G.echo_log = {}')
|
||||
|
||||
@@ -1549,8 +1548,7 @@ describe('vim.pack', function()
|
||||
eq('return "fetch new 2"', fn.readblob(fetch_lua_file))
|
||||
assert_progress_report('Applying updates', { 'fetch' })
|
||||
line_match(1, '^# Update')
|
||||
line_match(2, '^$')
|
||||
line_match(3, '^# Same')
|
||||
eq(1, api.nvim_buf_line_count(0))
|
||||
|
||||
eq(ref_lockfile, get_lock_tbl())
|
||||
|
||||
@@ -1622,6 +1620,29 @@ describe('vim.pack', function()
|
||||
ref_fetch_lock.rev = git_get_hash('main', 'fetch')
|
||||
eq(ref_fetch_lock, get_lock_tbl().plugins.fetch)
|
||||
end)
|
||||
|
||||
it('hints about not active plugins', function()
|
||||
exec_lua(function()
|
||||
vim.pack.update()
|
||||
end)
|
||||
|
||||
for _, l in ipairs(api.nvim_buf_get_lines(0, 0, -1, false)) do
|
||||
if l:match('^## ') then
|
||||
matches(' %(not active%)$', l)
|
||||
end
|
||||
end
|
||||
|
||||
-- Should also hint in `textDocument/documentSymbol` of in-process LSP,
|
||||
-- yet still work for navigation
|
||||
exec_lua('vim.lsp.buf.document_symbol()')
|
||||
local loclist = fn.getloclist(0)
|
||||
matches(' %(not active%)$', loclist[2].text)
|
||||
matches(' %(not active%)$', loclist[4].text)
|
||||
matches(' %(not active%)$', loclist[5].text)
|
||||
|
||||
n.exec('llast')
|
||||
eq(21, api.nvim_win_get_cursor(0)[1])
|
||||
end)
|
||||
end)
|
||||
|
||||
it('works with not active plugins', function()
|
||||
@@ -1980,7 +2001,7 @@ describe('vim.pack', function()
|
||||
|
||||
-- Should not include removed plugins immediately after they are removed,
|
||||
-- while still returning list without holes
|
||||
exec_lua('vim.pack.del({ "defbranch" })')
|
||||
exec_lua('vim.pack.del({ "defbranch" }, { force = true })')
|
||||
local defbranch_data = make_defbranch_data(true, true)
|
||||
local basic_data = make_basic_data(true, true)
|
||||
eq({ { defbranch_data, basic_data }, { basic_data } }, exec_lua('return _G.get_log'))
|
||||
@@ -2013,40 +2034,70 @@ describe('vim.pack', function()
|
||||
describe('del()', function()
|
||||
it('works', function()
|
||||
exec_lua(function()
|
||||
vim.pack.add({ repos_src.plugindirs, { src = repos_src.basic, version = 'feat-branch' } })
|
||||
local basic_spec = { src = repos_src.basic, version = 'feat-branch' }
|
||||
vim.pack.add({ repos_src.plugindirs, repos_src.defbranch, basic_spec })
|
||||
end)
|
||||
eq(true, pack_exists('basic'))
|
||||
eq(true, pack_exists('plugindirs'))
|
||||
|
||||
local locked_plugins = vim.tbl_keys(get_lock_tbl().plugins)
|
||||
table.sort(locked_plugins)
|
||||
eq({ 'basic', 'plugindirs' }, locked_plugins)
|
||||
local assert_on_disk = function(installed_map)
|
||||
local installed = {}
|
||||
for p_name, is_installed in pairs(installed_map) do
|
||||
eq(is_installed, pack_exists(p_name))
|
||||
if is_installed then
|
||||
installed[#installed + 1] = p_name
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(installed)
|
||||
local locked = vim.tbl_keys(get_lock_tbl().plugins)
|
||||
table.sort(locked)
|
||||
eq(installed, locked)
|
||||
end
|
||||
|
||||
assert_on_disk({ basic = true, defbranch = true, plugindirs = true })
|
||||
|
||||
-- By default should delete only non-active plugins, even if
|
||||
-- there is active one among input plugin names
|
||||
n.clear()
|
||||
exec_lua(function()
|
||||
vim.pack.add({ repos_src.defbranch })
|
||||
end)
|
||||
watch_events({ 'PackChangedPre', 'PackChanged' })
|
||||
|
||||
n.exec('messages clear')
|
||||
exec_lua(function()
|
||||
vim.pack.del({ 'basic', 'plugindirs' })
|
||||
local err = pcall_err(exec_lua, function()
|
||||
vim.pack.del({ 'basic', 'defbranch', 'plugindirs' })
|
||||
end)
|
||||
eq(false, pack_exists('basic'))
|
||||
eq(false, pack_exists('plugindirs'))
|
||||
matches('Some plugins are active and were not deleted: defbranch', err)
|
||||
|
||||
eq(
|
||||
"vim.pack: Removed plugin 'basic'\nvim.pack: Removed plugin 'plugindirs'",
|
||||
n.exec_capture('messages')
|
||||
)
|
||||
assert_on_disk({ basic = false, defbranch = true, plugindirs = false })
|
||||
|
||||
local msg = "vim.pack: Removed plugin 'basic'\nvim.pack: Removed plugin 'plugindirs'"
|
||||
eq(msg, n.exec_capture('messages'))
|
||||
|
||||
-- Should trigger relevant events in order as specified in `vim.pack.add()`
|
||||
local log = exec_lua('return _G.event_log')
|
||||
local find_event = make_find_packchanged(log)
|
||||
eq(1, find_event('Pre', 'delete', 'basic', 'feat-branch', true))
|
||||
eq(1, find_event('Pre', 'delete', 'basic', 'feat-branch', false))
|
||||
eq(2, find_event('', 'delete', 'basic', 'feat-branch', false))
|
||||
eq(3, find_event('Pre', 'delete', 'plugindirs', nil, true))
|
||||
eq(3, find_event('Pre', 'delete', 'plugindirs', nil, false))
|
||||
eq(4, find_event('', 'delete', 'plugindirs', nil, false))
|
||||
eq(4, #log)
|
||||
|
||||
-- Should update lockfile
|
||||
eq({ plugins = {} }, get_lock_tbl())
|
||||
-- Should be possible to force delete active plugins
|
||||
n.exec('messages clear')
|
||||
exec_lua('_G.event_log = {}')
|
||||
exec_lua(function()
|
||||
vim.pack.del({ 'defbranch' }, { force = true })
|
||||
end)
|
||||
|
||||
assert_on_disk({ basic = false, defbranch = false, plugindirs = false })
|
||||
|
||||
eq("vim.pack: Removed plugin 'defbranch'", n.exec_capture('messages'))
|
||||
|
||||
log = exec_lua('return _G.event_log')
|
||||
find_event = make_find_packchanged(log)
|
||||
eq(1, find_event('Pre', 'delete', 'defbranch', nil, true))
|
||||
eq(2, find_event('', 'delete', 'defbranch', nil, false))
|
||||
eq(2, #log)
|
||||
end)
|
||||
|
||||
it('works without prior `add()`', function()
|
||||
|
||||
Reference in New Issue
Block a user