mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	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
					Mathias Fußenegger
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							09a17f91d0
						
					
				
				
					commit
					4a09c178a1
				
			| @@ -652,7 +652,7 @@ local function on_code_action_results(results, ctx, options) | |||||||
|     --  arguments?: any[] |     --  arguments?: any[] | ||||||
|     -- |     -- | ||||||
|     ---@type lsp.Client |     ---@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 action = action_tuple[2] | ||||||
|  |  | ||||||
|     local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = ctx.bufnr }) |     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 |     if not action.edit and client and supports_resolve then | ||||||
|       client.request(ms.codeAction_resolve, action, function(err, resolved_action) |       client.request(ms.codeAction_resolve, action, function(err, resolved_action) | ||||||
|         if err then |         if err then | ||||||
|  |           if action.command then | ||||||
|  |             apply_action(action, client) | ||||||
|  |           else | ||||||
|             vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) |             vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) | ||||||
|           return |  | ||||||
|           end |           end | ||||||
|  |         else | ||||||
|           apply_action(resolved_action, client) |           apply_action(resolved_action, client) | ||||||
|  |         end | ||||||
|       end, ctx.bufnr) |       end, ctx.bufnr) | ||||||
|     else |     else | ||||||
|       apply_action(action, client) |       apply_action(action, client) | ||||||
|   | |||||||
| @@ -3483,6 +3483,50 @@ describe('LSP', function() | |||||||
|         end |         end | ||||||
|       } |       } | ||||||
|     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) |   end) | ||||||
|   describe('vim.lsp.commands', function() |   describe('vim.lsp.commands', function() | ||||||
|     it('Accepts only string keys', function() |     it('Accepts only string keys', function() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user