feat(lsp): fallback to code-action command on resolve failure (#25464)

The haskell-language-server supports resolve only for a subset of code
actions. For many code actions trying to resolve the `edit` property
results in an error, but the unresolved action already contains a
command that can be executed without issue.

The protocol specification is unfortunately a bit vague about this,
and what the haskell-language-server does seems to be valid.

Example:

    newtype Dummy = Dummy Int
    instance Num Dummy where

Triggering code actions on "Num Dummy" and choosing "Add placeholders
for all missing methods" resulted in:

    -32601: No plugin enabled for SMethod_CodeActionResolve, potentially available: explicit-fields, importLens, hlint, overloaded-record-dot

With this change it will insert the missing methods:

    instance Num Dummy where
      (+) = _
      (-) = _
      (*) = _
      negate = _
      abs = _
      signum = _
      fromInteger = _
This commit is contained in:
Mathias Fußenegger 2023-10-02 22:14:19 +02:00 committed by GitHub
parent 09a17f91d0
commit 4a09c178a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 4 deletions

View File

@ -652,7 +652,7 @@ local function on_code_action_results(results, ctx, options)
-- arguments?: any[]
--
---@type lsp.Client
local client = vim.lsp.get_client_by_id(action_tuple[1])
local client = assert(vim.lsp.get_client_by_id(action_tuple[1]))
local action = action_tuple[2]
local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = ctx.bufnr })
@ -663,10 +663,14 @@ local function on_code_action_results(results, ctx, options)
if not action.edit and client and supports_resolve then
client.request(ms.codeAction_resolve, action, function(err, resolved_action)
if err then
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
return
if action.command then
apply_action(action, client)
else
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
end
else
apply_action(resolved_action, client)
end
apply_action(resolved_action, client)
end, ctx.bufnr)
else
apply_action(action, client)

View File

@ -3483,6 +3483,50 @@ describe('LSP', function()
end
}
end)
it("Fallback to command execution on resolve error", function()
clear()
exec_lua(create_server_definition)
local result = exec_lua([[
local server = _create_server({
capabilities = {
executeCommandProvider = {
commands = {"command:1"},
},
codeActionProvider = {
resolveProvider = true
}
},
handlers = {
["textDocument/codeAction"] = function()
return {
{
title = "Code Action 1",
command = {
title = "Command 1",
command = "command:1",
}
}
}
end,
["codeAction/resolve"] = function()
return nil, "resolve failed"
end,
}
})
local client_id = vim.lsp.start({
name = "dummy",
cmd = server.cmd,
})
vim.lsp.buf.code_action({ apply = true })
vim.lsp.stop_client(client_id)
return server.messages
]])
eq("codeAction/resolve", result[4].method)
eq("workspace/executeCommand", result[5].method)
eq("command:1", result[5].params.command)
end)
end)
describe('vim.lsp.commands', function()
it('Accepts only string keys', function()