neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 4a09c178a19097c295521892c889f1f196fff100
parent 09a17f91d0d362c6e58bfdbe3ccdeacffb0b44b9
Author: Mathias Fußenegger <mfussenegger@users.noreply.github.com>
Date:   Mon,  2 Oct 2023 22:14:19 +0200

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 = _
Diffstat:
Mruntime/lua/vim/lsp/buf.lua | 12++++++++----
Mtest/functional/plugin/lsp_spec.lua | 44++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 52 insertions(+), 4 deletions(-)

diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua @@ -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) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua @@ -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()