feat(pack): add code actions in confirmation buffer

Problem: No way to granularly operate on plugins when inside
confirmation buffer.

Solution: Implement code actions for in-process LSP that act on "plugin
at cursor":
  - Update (if has updates).
  - Skip updating (if has updates).
  - Delete.

  Activate via default `gra` or `vim.lsp.buf.code_action()`.
This commit is contained in:
Evgeni Chasnovski
2025-10-05 19:17:52 +03:00
parent 2728b4efe0
commit 98e3a571dd
4 changed files with 262 additions and 27 deletions

View File

@@ -1136,6 +1136,7 @@ describe('vim.pack', function()
it('has in-process LSP features', function()
t.skip(not is_jit(), "Non LuaJIT reports errors differently due to 'coxpcall'")
track_nvim_echo()
exec_lua(function()
vim.pack.add({
repos_src.fetch,
@@ -1203,6 +1204,116 @@ describe('vim.pack', function()
validate_hover({ 30, 0 }, 'Add version v1.0.0')
validate_hover({ 31, 0 }, 'Add version v0.4')
validate_hover({ 32, 0 }, 'Add version 0.3.1')
-- textDocument/codeAction
n.exec_lua(function()
-- Mock `vim.ui.select()` which is a default code action selection
_G.select_idx = 0
---@diagnostic disable-next-line: duplicate-set-field
vim.ui.select = function(items, _, on_choice)
_G.select_items = items
local idx = _G.select_idx
if idx > 0 then
on_choice(items[idx], idx)
-- Minor delay before continue because LSP cmd execution is async
vim.wait(10)
end
end
end)
local ref_lockfile = get_lock_tbl() --- @type vim.pack.Lock
local function validate_action(pos, action_titles, select_idx)
api.nvim_win_set_cursor(0, pos)
local lines = api.nvim_buf_get_lines(0, 0, -1, false)
n.exec_lua(function()
_G.select_items = nil
_G.select_idx = select_idx
vim.lsp.buf.code_action()
end)
local titles = vim.tbl_map(function(x) --- @param x table
return x.action.title
end, n.exec_lua('return _G.select_items or {}'))
eq(titles, action_titles)
-- If no action is asked (like via cancel), should not delete lines
if select_idx <= 0 then
eq(lines, api.nvim_buf_get_lines(0, 0, -1, false))
end
end
-- - Should not include "namespace" header as "plugin at cursor"
validate_action({ 1, 1 }, {}, 0)
validate_action({ 2, 0 }, {}, 0)
-- - Only deletion should be available on errored plugin
validate_action({ 3, 1 }, { 'Delete `defbranch`' }, 0)
validate_action({ 7, 0 }, { 'Delete `defbranch`' }, 0)
-- - Should not include separator blank line as "plugin at cursor"
validate_action({ 8, 0 }, {}, 0)
validate_action({ 9, 0 }, {}, 0)
validate_action({ 10, 0 }, {}, 0)
-- - Should also suggest updating related actions if updates available
local fetch_actions = { 'Update `fetch`', 'Skip updating `fetch`', 'Delete `fetch`' }
validate_action({ 11, 0 }, fetch_actions, 0)
validate_action({ 14, 0 }, fetch_actions, 0)
validate_action({ 20, 0 }, fetch_actions, 0)
validate_action({ 21, 0 }, {}, 0)
validate_action({ 22, 0 }, {}, 0)
validate_action({ 23, 0 }, {}, 0)
-- - Only deletion should be available on plugins without update
validate_action({ 24, 0 }, { 'Delete `semver`' }, 0)
validate_action({ 28, 0 }, { 'Delete `semver`' }, 0)
validate_action({ 32, 0 }, { 'Delete `semver`' }, 0)
-- - Should correctly perform action and remove plugin's lines
local function line_match(lnum, pattern)
matches(pattern, api.nvim_buf_get_lines(0, lnum - 1, lnum, false)[1])
end
-- - Delete. Should remove from disk and update lockfile.
validate_action({ 3, 0 }, { 'Delete `defbranch`' }, 1)
eq(false, pack_exists('defbranch'))
line_match(1, '^# Error')
line_match(2, '^$')
line_match(3, '^# Update')
ref_lockfile.plugins.defbranch = nil
eq(ref_lockfile, get_lock_tbl())
-- - Skip udating
validate_action({ 5, 0 }, fetch_actions, 2)
eq({ 'return "fetch main"' }, fn.readfile(fetch_lua_file))
line_match(3, '^# Update')
line_match(4, '^$')
line_match(5, '^# Same')
-- - Update plugin. Should not re-fetch new data and update lockfile.
n.exec('quit')
n.exec_lua(function()
vim.pack.update({ 'fetch', 'semver' })
end)
exec_lua('_G.echo_log = {}')
ref_lockfile.plugins.fetch.rev = git_get_hash('main', 'fetch')
repo_write_file('fetch', 'lua/fetch.lua', 'return "fetch new 3"')
git_add_commit('Commit to be added 3', 'fetch')
validate_action({ 3, 0 }, fetch_actions, 1)
eq({ 'return "fetch new 2"' }, fn.readfile(fetch_lua_file))
validate_progress_report('Applying updates', { 'fetch' })
line_match(1, '^# Update')
line_match(2, '^$')
line_match(3, '^# Same')
eq(ref_lockfile, get_lock_tbl())
-- - Can still respect `:write` after action
n.exec('write')
eq('vim.pack: Nothing to update', n.exec_capture('1messages'))
eq(api.nvim_get_option_value('filetype', {}), '')
end)
it('has buffer-local mappings', function()