Merge #34853 from ribru17/diag_related_docs

feat(lsp): diagnostic related documents support
This commit is contained in:
Justin M. Keyes
2025-07-22 00:38:46 -04:00
committed by GitHub
4 changed files with 106 additions and 3 deletions

View File

@@ -214,6 +214,8 @@ LSP
jump to the problematic location.
• Support for `textDocument/linkedEditingRange`: |lsp-linked_editing_range|
https://microsoft.github.io/language-server-protocol/specification/#textDocument_linkedEditingRange
• Support for related documents in pull diagnostics:
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#relatedFullDocumentDiagnosticReport
LUA

View File

@@ -269,16 +269,36 @@ function M.on_diagnostic(error, result, ctx)
return
end
if result == nil or result.kind == 'unchanged' then
if result == nil then
return
end
local client_id = ctx.client_id
handle_diagnostics(ctx.params.textDocument.uri, client_id, result.items, true)
local bufnr = assert(ctx.bufnr)
local bufstate = bufstates[bufnr]
bufstate.client_result_id[client_id] = result.resultId
if result.kind == 'unchanged' then
return
end
handle_diagnostics(ctx.params.textDocument.uri, client_id, result.items, true)
for uri, related_result in pairs(result.relatedDocuments or {}) do
if related_result.kind == 'full' then
handle_diagnostics(uri, client_id, related_result.items, true)
end
local related_bufnr = vim.uri_to_bufnr(uri)
local related_bufstate = bufstates[related_bufnr]
-- Create a new bufstate if it doesn't exist for the related document. This will not enable
-- diagnostic pulling by itself, but will allow previous result IDs to be passed correctly the
-- next time this buffer's diagnostics are pulled.
or { pull_kind = 'document', client_result_id = {} }
bufstates[related_bufnr] = related_bufstate
related_bufstate.client_result_id[client_id] = related_result.resultId
end
end
--- Clear push diagnostics and diagnostic cache.

View File

@@ -350,6 +350,7 @@ function protocol.make_client_capabilities()
},
dataSupport = true,
relatedInformation = true,
relatedDocumentSupport = true,
},
inlayHint = {
dynamicRegistration = true,

View File

@@ -470,6 +470,7 @@ describe('vim.lsp.diagnostic', function()
end)
it('requests with the `previousResultId`', function()
-- Full reports
eq(
'dummy_server',
exec_lua(function()
@@ -497,6 +498,85 @@ describe('vim.lsp.diagnostic', function()
return _G.params.previousResultId
end)
)
-- Unchanged reports
eq(
'squidward',
exec_lua(function()
vim.lsp.diagnostic.on_diagnostic(nil, {
kind = 'unchanged',
resultId = 'squidward',
}, {
method = vim.lsp.protocol.Methods.textDocument_diagnostic,
params = {
textDocument = { uri = fake_uri },
},
client_id = client_id,
bufnr = diagnostic_bufnr,
})
vim.api.nvim_exec_autocmds('LspNotify', {
buffer = diagnostic_bufnr,
data = {
method = vim.lsp.protocol.Methods.textDocument_didChange,
client_id = client_id,
},
})
return _G.params.previousResultId
end)
)
end)
it('handles relatedDocuments diagnostics', function()
local fake_uri_2 = 'file:///fake/uri2'
---@type vim.Diagnostic[], vim.Diagnostic[], string?
local diagnostics, related_diagnostics, relatedPreviousResultId = exec_lua(function()
local second_buf = vim.uri_to_bufnr(fake_uri_2)
vim.fn.bufload(second_buf)
-- Attach the client to both buffers.
vim.api.nvim_win_set_buf(0, second_buf)
vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
vim.lsp.diagnostic.on_diagnostic(nil, {
kind = 'full',
relatedDocuments = {
[fake_uri_2] = {
kind = 'full',
resultId = 'spongebob',
items = {
{
range = _G.make_range(4, 4, 4, 4),
message = 'related bad!',
},
},
},
},
items = {},
}, {
params = {
textDocument = { uri = fake_uri },
},
uri = fake_uri,
client_id = client_id,
bufnr = diagnostic_bufnr,
}, {})
vim.api.nvim_exec_autocmds('LspNotify', {
buffer = second_buf,
data = {
method = vim.lsp.protocol.Methods.textDocument_didChange,
client_id = client_id,
},
})
return vim.diagnostic.get(diagnostic_bufnr),
vim.diagnostic.get(second_buf),
_G.params.previousResultId
end)
eq(0, #diagnostics)
eq(1, #related_diagnostics)
eq('related bad!', related_diagnostics[1].message)
eq('spongebob', relatedPreviousResultId)
end)
end)
end)