mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	fix(lsp): retrigger diagnostics request on server cancellation (#31345)
Co-authored-by: Jesse <github@jessebakker.com>
This commit is contained in:
		| @@ -1534,12 +1534,13 @@ get_namespace({client_id}, {is_pull}) | ||||
|                      client. Defaults to push | ||||
|  | ||||
|                                           *vim.lsp.diagnostic.on_diagnostic()* | ||||
| on_diagnostic({_}, {result}, {ctx}) | ||||
| on_diagnostic({error}, {result}, {ctx}) | ||||
|     |lsp-handler| for the method "textDocument/diagnostic" | ||||
|  | ||||
|     See |vim.diagnostic.config()| for configuration options. | ||||
|  | ||||
|     Parameters: ~ | ||||
|       • {error}   (`lsp.ResponseError?`) | ||||
|       • {result}  (`lsp.DocumentDiagnosticReport`) | ||||
|       • {ctx}     (`lsp.HandlerContext`) | ||||
|  | ||||
|   | ||||
| @@ -246,10 +246,18 @@ end | ||||
| --- | ||||
| --- See |vim.diagnostic.config()| for configuration options. | ||||
| --- | ||||
| ---@param _ lsp.ResponseError? | ||||
| ---@param error lsp.ResponseError? | ||||
| ---@param result lsp.DocumentDiagnosticReport | ||||
| ---@param ctx lsp.HandlerContext | ||||
| function M.on_diagnostic(_, result, ctx) | ||||
| function M.on_diagnostic(error, result, ctx) | ||||
|   if error ~= nil and error.code == protocol.ErrorCodes.ServerCancelled then | ||||
|     if error.data == nil or error.data.retriggerRequest ~= false then | ||||
|       local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) | ||||
|       client:request(ctx.method, ctx.params) | ||||
|     end | ||||
|     return | ||||
|   end | ||||
|  | ||||
|   if result == nil or result.kind == 'unchanged' then | ||||
|     return | ||||
|   end | ||||
|   | ||||
| @@ -659,7 +659,8 @@ for k, fn in pairs(M) do | ||||
|       }) | ||||
|     end | ||||
|  | ||||
|     if err then | ||||
|     -- ServerCancelled errors should be propagated to the request handler | ||||
|     if err and err.code ~= protocol.ErrorCodes.ServerCancelled then | ||||
|       -- LSP spec: | ||||
|       -- interface ResponseError: | ||||
|       --  code: integer; | ||||
|   | ||||
| @@ -174,6 +174,7 @@ local constants = { | ||||
|     -- Defined by the protocol. | ||||
|     RequestCancelled = -32800, | ||||
|     ContentModified = -32801, | ||||
|     ServerCancelled = -32802, | ||||
|   }, | ||||
|  | ||||
|   -- Describes the content type that a client supports in various | ||||
|   | ||||
| @@ -386,6 +386,21 @@ function tests.check_forward_content_modified() | ||||
|   } | ||||
| end | ||||
|  | ||||
| function tests.check_forward_server_cancelled() | ||||
|   skeleton { | ||||
|     on_init = function() | ||||
|       return { capabilities = {} } | ||||
|     end, | ||||
|     body = function() | ||||
|       expect_request('error_code_test', function() | ||||
|         return { code = -32802 }, nil, { method = 'error_code_test', client_id = 1 } | ||||
|       end) | ||||
|       expect_notification('finish') | ||||
|       notify('finish') | ||||
|     end, | ||||
|   } | ||||
| end | ||||
|  | ||||
| function tests.check_pending_request_tracked() | ||||
|   skeleton { | ||||
|     on_init = function(_) | ||||
|   | ||||
| @@ -209,10 +209,16 @@ describe('vim.lsp.diagnostic', function() | ||||
|     before_each(function() | ||||
|       exec_lua(create_server_definition) | ||||
|       exec_lua(function() | ||||
|         _G.requests = 0 | ||||
|         _G.server = _G._create_server({ | ||||
|           capabilities = { | ||||
|             diagnosticProvider = {}, | ||||
|           }, | ||||
|           handlers = { | ||||
|             [vim.lsp.protocol.Methods.textDocument_diagnostic] = function() | ||||
|               _G.requests = _G.requests + 1 | ||||
|             end, | ||||
|           }, | ||||
|         }) | ||||
|  | ||||
|         function _G.get_extmarks(bufnr, client_id0) | ||||
| @@ -373,5 +379,56 @@ describe('vim.lsp.diagnostic', function() | ||||
|         end) | ||||
|       ) | ||||
|     end) | ||||
|  | ||||
|     it('handles server cancellation', function() | ||||
|       eq( | ||||
|         1, | ||||
|         exec_lua(function() | ||||
|           vim.lsp.diagnostic.on_diagnostic({ | ||||
|             code = vim.lsp.protocol.ErrorCodes.ServerCancelled, | ||||
|             -- Empty data defaults to retriggering request | ||||
|             data = {}, | ||||
|             message = '', | ||||
|           }, {}, { | ||||
|             method = vim.lsp.protocol.Methods.textDocument_diagnostic, | ||||
|             client_id = client_id, | ||||
|           }) | ||||
|  | ||||
|           return _G.requests | ||||
|         end) | ||||
|       ) | ||||
|  | ||||
|       eq( | ||||
|         2, | ||||
|         exec_lua(function() | ||||
|           vim.lsp.diagnostic.on_diagnostic({ | ||||
|             code = vim.lsp.protocol.ErrorCodes.ServerCancelled, | ||||
|             data = { retriggerRequest = true }, | ||||
|             message = '', | ||||
|           }, {}, { | ||||
|             method = vim.lsp.protocol.Methods.textDocument_diagnostic, | ||||
|             client_id = client_id, | ||||
|           }) | ||||
|  | ||||
|           return _G.requests | ||||
|         end) | ||||
|       ) | ||||
|  | ||||
|       eq( | ||||
|         2, | ||||
|         exec_lua(function() | ||||
|           vim.lsp.diagnostic.on_diagnostic({ | ||||
|             code = vim.lsp.protocol.ErrorCodes.ServerCancelled, | ||||
|             data = { retriggerRequest = false }, | ||||
|             message = '', | ||||
|           }, {}, { | ||||
|             method = vim.lsp.protocol.Methods.textDocument_diagnostic, | ||||
|             client_id = client_id, | ||||
|           }) | ||||
|  | ||||
|           return _G.requests | ||||
|         end) | ||||
|       ) | ||||
|     end) | ||||
|   end) | ||||
| end) | ||||
|   | ||||
| @@ -1066,6 +1066,39 @@ describe('LSP', function() | ||||
|       } | ||||
|     end) | ||||
|  | ||||
|     it('should forward ServerCancelled to callback', function() | ||||
|       local expected_handlers = { | ||||
|         { NIL, {}, { method = 'finish', client_id = 1 } }, | ||||
|         { | ||||
|           { code = -32802 }, | ||||
|           NIL, | ||||
|           { method = 'error_code_test', bufnr = 1, client_id = 1, version = 0 }, | ||||
|         }, | ||||
|       } | ||||
|       local client --- @type vim.lsp.Client | ||||
|       test_rpc_server { | ||||
|         test_name = 'check_forward_server_cancelled', | ||||
|         on_init = function(_client) | ||||
|           _client:request('error_code_test') | ||||
|           client = _client | ||||
|         end, | ||||
|         on_exit = function(code, signal) | ||||
|           eq(0, code, 'exit code') | ||||
|           eq(0, signal, 'exit signal') | ||||
|           eq(0, #expected_handlers, 'did not call expected handler') | ||||
|         end, | ||||
|         on_handler = function(err, _, ctx) | ||||
|           eq(table.remove(expected_handlers), { err, _, ctx }, 'expected handler') | ||||
|           if ctx.method ~= 'finish' then | ||||
|             client:notify('finish') | ||||
|           end | ||||
|           if ctx.method == 'finish' then | ||||
|             client:stop() | ||||
|           end | ||||
|         end, | ||||
|       } | ||||
|     end) | ||||
|  | ||||
|     it('should forward ContentModified to callback', function() | ||||
|       local expected_handlers = { | ||||
|         { NIL, {}, { method = 'finish', client_id = 1 } }, | ||||
| @@ -1089,7 +1122,6 @@ describe('LSP', function() | ||||
|         end, | ||||
|         on_handler = function(err, _, ctx) | ||||
|           eq(table.remove(expected_handlers), { err, _, ctx }, 'expected handler') | ||||
|           -- if ctx.method == 'error_code_test' then client.notify("finish") end | ||||
|           if ctx.method ~= 'finish' then | ||||
|             client:notify('finish') | ||||
|           end | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Gregory Anders
					Gregory Anders