From 0cda0183451443df81aeeba077bd3b93fa620eb8 Mon Sep 17 00:00:00 2001 From: tris203 Date: Mon, 16 Mar 2026 16:19:52 +0000 Subject: [PATCH] fix(lsp/diagnostic): key resultId by client and identifier Previously, resultId for diagnostics was keyed only by client_id, which could cause issues when multiple identifiers are used by the same client. This change introduces a composite key of client_id and identifier for client_result_id, ensuring correct tracking of diagnostic results per identifier. Updates all relevant logic to use the new keying scheme. --- runtime/lua/vim/lsp/diagnostic.lua | 45 +++++++++++++++++++----------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 2d224722ea..1a17da9e03 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -15,7 +15,7 @@ local augroup = api.nvim_create_augroup('nvim.lsp.diagnostic', {}) ---@class (private) vim.lsp.diagnostic.BufState ---@field pull_kind 'document'|'workspace'|'disabled' Whether diagnostics are being updated via document pull, workspace pull, or disabled. ----@field client_result_id table Latest responded `resultId` +---@field client_result_id table Latest responded `resultId`, keyed by `client_id.identifier` ---@type table local bufstates = {} @@ -147,6 +147,13 @@ local function tags_vim_to_lsp(diagnostic) return tags end +---@param client_id integer +---@param identifier string|nil +---@return string +local function result_id_key(client_id, identifier) + return string.format('%d.%s', client_id, identifier or 'nil') +end + --- Converts the input `vim.Diagnostic`s to LSP diagnostics. --- @param diagnostics vim.Diagnostic[] --- @return lsp.Diagnostic[] @@ -281,14 +288,15 @@ function M.on_diagnostic(error, result, ctx) local client_id = ctx.client_id local bufnr = assert(ctx.bufnr) local bufstate = bufstates[bufnr] - bufstate.client_result_id[client_id] = result.resultId + ---@type lsp.DocumentDiagnosticParams + local params = ctx.params + local key = result_id_key(client_id, params.identifier) + bufstate.client_result_id[key] = result.resultId if result.kind == 'unchanged' then return end - ---@type lsp.DocumentDiagnosticParams - local params = ctx.params handle_diagnostics(params.textDocument.uri, client_id, result.items, params.identifier or true) for uri, related_result in pairs(result.relatedDocuments or {}) do @@ -304,7 +312,7 @@ function M.on_diagnostic(error, result, ctx) or { pull_kind = 'document', client_result_id = {} } bufstates[related_bufnr] = related_bufstate - related_bufstate.client_result_id[client_id] = related_result.resultId + related_bufstate.client_result_id[key] = related_result.resultId end end @@ -383,11 +391,12 @@ function M._refresh(bufnr, client_id, only_visible) for _, client in ipairs(clients) do ---@param cap lsp.DiagnosticRegistrationOptions client:_provider_foreach(method, function(cap) + local key = result_id_key(client.id, cap.identifier) ---@type lsp.DocumentDiagnosticParams local params = { identifier = cap.identifier, textDocument = util.make_text_document_params(bufnr), - previousResultId = bufstate.client_result_id[client.id], + previousResultId = bufstate.client_result_id[key], } client:request(method, params, nil, bufnr) end) @@ -481,19 +490,20 @@ end --- Returns the result IDs from the reports provided by the given client. --- @return lsp.PreviousResultId[] -local function previous_result_ids(client_id) +--- @param client_id integer +--- @param identifier string|nil +local function previous_result_ids(client_id, identifier) local results = {} ---@type lsp.PreviousResultId[] + local key = result_id_key(client_id, identifier) for bufnr, state in pairs(bufstates) do if state.pull_kind ~= 'disabled' then - for buf_client_id, result_id in pairs(state.client_result_id) do - if buf_client_id == client_id then - results[#results + 1] = { - uri = vim.uri_from_bufnr(bufnr), - value = result_id, - } - break - end + local result_id = state.client_result_id[key] + if result_id then + results[#results + 1] = { + uri = vim.uri_from_bufnr(bufnr), + value = result_id, + } end end end @@ -535,7 +545,8 @@ function M._workspace_diagnostics(opts) -- state if we're not pulling document diagnostics for this buffer. if bufstates[bufnr].pull_kind == 'workspace' and report.kind == 'full' then handle_diagnostics(report.uri, ctx.client_id, report.items, params.identifier or true) - bufstates[bufnr].client_result_id[ctx.client_id] = report.resultId + local key = result_id_key(ctx.client_id, params.identifier) + bufstates[bufnr].client_result_id[key] = report.resultId end end end @@ -547,7 +558,7 @@ function M._workspace_diagnostics(opts) --- @type lsp.WorkspaceDiagnosticParams local params = { identifier = cap.identifier, - previousResultIds = previous_result_ids(client.id), + previousResultIds = previous_result_ids(client.id, cap.identifier), } client:request('workspace/diagnostic', params, handler)