Skip to content

Commit

Permalink
feat(lsp): add vim.lsp.diagnostic.relatedInformation field
Browse files Browse the repository at this point in the history
For now this just adds the field to the toplevel Diagnostic structure.
We can decide in a follow-up if and how we should display these to the
user.
  • Loading branch information
tom-anders committed May 10, 2024
1 parent 064f3e4 commit a02d006
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 25 deletions.
53 changes: 37 additions & 16 deletions runtime/doc/diagnostic.txt
Original file line number Diff line number Diff line change
Expand Up @@ -350,22 +350,43 @@ Lua module: vim.diagnostic *diagnostic-api*
0-based rows and columns). |api-indexing|

Fields: ~
{bufnr}? (`integer`) Buffer number
{lnum} (`integer`) The starting line of the diagnostic
(0-indexed)
• {end_lnum}? (`integer`) The final line of the diagnostic (0-indexed)
{col} (`integer`) The starting column of the diagnostic
(0-indexed)
• {end_col}? (`integer`) The final column of the diagnostic
(0-indexed)
{severity}? (`vim.diagnostic.Severity`) The severity of the
diagnostic |vim.diagnostic.severity|
{message} (`string`) The diagnostic text
{source}? (`string`) The source of the diagnostic
{code}? (`string|integer`) The diagnostic code
• {_tags}? (`{ deprecated: boolean, unnecessary: boolean}`)
• {user_data}? (`any`) arbitrary data plugins can add
{namespace}? (`integer`)
{bufnr}? (`integer`) Buffer number
{lnum} (`integer`) The starting line of the
diagnostic (0-indexed)
• {end_lnum}? (`integer`) The final line of the diagnostic
(0-indexed)
{col} (`integer`) The starting column of the
diagnostic (0-indexed)
• {end_col}? (`integer`) The final column of the diagnostic
(0-indexed)
{severity}? (`vim.diagnostic.Severity`) The severity of
the diagnostic |vim.diagnostic.severity|
{message} (`string`) The diagnostic text
• {related_information}? (`vim.DiagnosticRelatedInformation[]`) An
array of related diagnostic information. See
|vim.DiagnosticRelatedInformation|.
{source}? (`string`) The source of the diagnostic
{code}? (`string|integer`) The diagnostic code
• {_tags}? (`{ deprecated: boolean, unnecessary: boolean}`)
• {user_data}? (`any`) arbitrary data plugins can add
{namespace}? (`integer`)

*vim.DiagnosticRelatedInformation*
Related message and location for a diagnostic. This can be used to point
to code locations that cause or are related to a diagnostics, e.g when
duplicating a symbol in a scope.

Fields: ~
{bufnr}? (`integer`) Buffer number
{lnum} (`integer`) The starting line of the related information
(0-indexed)
• {end_lnum}? (`integer`) The final line of the related information
(0-indexed)
{col} (`integer`) The starting column of the related
information (0-indexed)
• {end_col}? (`integer`) The final column of the related information
(0-indexed)
{message} (`string`) The related information text

*vim.diagnostic.GetOpts*
A table with the following keys:
Expand Down
4 changes: 4 additions & 0 deletions runtime/doc/news.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ The following new APIs and features were added.
|vim.lsp.buf.definition()|, |vim.lsp.buf.declaration()|,
|vim.lsp.buf.type_definition()|, and |vim.lsp.buf.implementation()| now
support the `loclist` field of |vim.lsp.ListOpts|.
• Add `vim.Diagnostic.related_information` field and fill it with
`lsp.DiagnosticRelatedInformation[]`. Deprecate
`vim.Diagnostic.user_data.lsp.relatedInformation` in favor of this new
field.

• Treesitter
• Bundled parsers and queries (highlight, folds) for Markdown, Python, and
Expand Down
26 changes: 26 additions & 0 deletions runtime/lua/vim/diagnostic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ local M = {}
--- The diagnostic text
--- @field message string
---
--- An array of related diagnostic information
--- @field related_information? vim.DiagnosticRelatedInformation[]
---
--- The source of the diagnostic
--- @field source? string
---
Expand All @@ -42,6 +45,29 @@ local M = {}
---
--- @field namespace? integer

--- Related message and location for a diagnostic.
--- This can be used to point to code locations that cause or are related to
--- a diagnostic, e.g when duplicating a symbol in a scope.
--- @class vim.DiagnosticRelatedInformation
---
--- Buffer number
--- @field bufnr? integer
---
--- The starting line of the related information (0-indexed)
--- @field lnum integer
---
--- The final line of the related information (0-indexed)
--- @field end_lnum? integer
---
--- The starting column of the related information (0-indexed)
--- @field col integer
---
--- The final column of the related information (0-indexed)
--- @field end_col? integer
---
--- The related information text
--- @field message string

--- Each of the configuration options below accepts one of the following:
--- - `false`: Disable this feature
--- - `true`: Enable this feature, use default settings.
Expand Down
44 changes: 37 additions & 7 deletions runtime/lua/vim/lsp/diagnostic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,33 @@ local function tags_lsp_to_vim(diagnostic, client_id)
return tags
end

---@param related_informations lsp.DiagnosticRelatedInformation[]
---@param offset_encoding string
---@return vim.DiagnosticRelatedInformation[]
local function related_information_lsp_to_vim(related_informations, offset_encoding)
return vim.iter(related_informations or {}):map(
--- @param related_information lsp.DiagnosticRelatedInformation
--- @return vim.DiagnosticRelatedInformation?
function(related_information)
local fname = vim.uri_to_fname(related_information.location.uri)
local bufnr = vim.fn.bufadd(fname)
if not bufnr then
return nil
end
local buf_lines = get_buf_lines(bufnr)
local start = related_information.location.range.start
local _end = related_information.location.range['end']
--- @type vim.DiagnosticRelatedInformation
return {
lnum = start.line,
col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding),
end_lnum = _end.line,
end_col = line_byte_from_position(buf_lines, _end.line, _end.character, offset_encoding),
message = related_information.message,
}
end):totable()
end

---@param diagnostics lsp.Diagnostic[]
---@param bufnr integer
---@param client_id integer
Expand All @@ -118,12 +145,15 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
end_col = line_byte_from_position(buf_lines, _end.line, _end.character, offset_encoding),
severity = severity_lsp_to_vim(diagnostic.severity),
message = diagnostic.message,
related_information = related_information_lsp_to_vim(diagnostic.relatedInformation,
offset_encoding),
source = diagnostic.source,
code = diagnostic.code,
_tags = tags_lsp_to_vim(diagnostic, client_id),
user_data = {
lsp = {
-- usage of user_data.lsp.code is deprecated in favor of the top-level code field
-- usage of user_data.lsp.code and user_data.lsp.relatedInformation is deprecated
-- in favor of the top-level code and related_information fields
code = diagnostic.code,
codeDescription = diagnostic.codeDescription,
relatedInformation = diagnostic.relatedInformation,
Expand Down Expand Up @@ -194,7 +224,7 @@ function M.get_namespace(client_id, is_pull)
local client = vim.lsp.get_client_by_id(client_id)
if is_pull then
local server_id =
vim.tbl_get((client or {}).server_capabilities, 'diagnosticProvider', 'identifier')
vim.tbl_get((client or {}).server_capabilities, 'diagnosticProvider', 'identifier')
local key = string.format('%d:%s', client_id, server_id or 'nil')
local name = string.format(
'vim.lsp.%s.%d.%s',
Expand Down Expand Up @@ -431,8 +461,8 @@ function M._enable(bufnr)
buffer = bufnr,
callback = function(opts)
if
opts.data.method ~= ms.textDocument_didChange
and opts.data.method ~= ms.textDocument_didOpen
opts.data.method ~= ms.textDocument_didChange
and opts.data.method ~= ms.textDocument_didOpen
then
return
end
Expand Down Expand Up @@ -461,9 +491,9 @@ function M._enable(bufnr)
local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_diagnostic })

if
not vim.iter(clients):any(function(c)
return c.id ~= args.data.client_id
end)
not vim.iter(clients):any(function(c)
return c.id ~= args.data.client_id
end)
then
disable(bufnr)
end
Expand Down
39 changes: 37 additions & 2 deletions test/functional/plugin/lsp/diagnostic_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ describe('vim.lsp.diagnostic', function()
}
end
make_related_information = function(msg, uri, x1, y1, x2, y2)
return {
location = {
uri = uri,
range = make_range(x1, y1, x2, y2),
},
message = msg,
}
end
function get_extmarks(bufnr, client_id)
local namespace = vim.lsp.diagnostic.get_namespace(client_id)
local ns = vim.diagnostic.get_namespace(namespace)
Expand Down Expand Up @@ -108,6 +118,10 @@ describe('vim.lsp.diagnostic', function()
diagnostics[1].code = 42
diagnostics[1].data = "Hello world"
diagnostics[1].relatedInformation = {
make_related_information("Related info", fake_uri, 1, 2, 3, 4),
}
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
uri = fake_uri,
diagnostics = diagnostics,
Expand All @@ -118,8 +132,23 @@ describe('vim.lsp.diagnostic', function()
vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1)[1],
}
]]
eq({ code = 42, data = 'Hello world' }, result[1].user_data.lsp)
eq({
code = 42,
data = 'Hello world',
relatedInformation = {
{
location = {
uri = fake_uri,
range = {
start = { line = 1, character = 2 },
['end'] = { line = 3, character = 4 },
},
},
message = "Related info",
} }
}, result[1].user_data.lsp)
eq(42, result[1].code)
eq("Related info", result[1].related_information[1].message)
eq(42, result[2].code)
eq('Hello world', result[2].data)
end)
Expand Down Expand Up @@ -321,11 +350,15 @@ describe('vim.lsp.diagnostic', function()

it('adds diagnostics to vim.diagnostics', function()
local diags = exec_lua([[
local error = make_error('Pull Diagnostic', 4, 4, 4, 4)
error.relatedInformation = {
make_related_information('Related info', fake_uri, 4, 4, 4, 4),
}
vim.lsp.diagnostic.on_diagnostic(nil,
{
kind = 'full',
items = {
make_error('Pull Diagnostic', 4, 4, 4, 4),
error
}
},
{
Expand All @@ -342,6 +375,8 @@ describe('vim.lsp.diagnostic', function()
]])
eq(1, #diags)
eq('Pull Diagnostic', diags[1].message)
eq(1, #diags[1].related_information)
eq('Related info', diags[1].related_information[1].message)
end)

it('allows configuring the virtual text via vim.lsp.with', function()
Expand Down

0 comments on commit a02d006

Please sign in to comment.