Skip to content

Commit

Permalink
feat(lsp): add vim.diagnostic.related_information field
Browse files Browse the repository at this point in the history
For now this just adds the field to the toplevel Diagnostic structure
and fills it with the corresponding struct from LSP.
We can decide in a follow-up if and how we should display these to the
user.
  • Loading branch information
tom-anders committed May 12, 2024
1 parent 064f3e4 commit 3c44e73
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 19 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,26 @@ 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.diagnostic.RelatedInformation[]`) An
array of related diagnostic information. See
|vim.diagnostic.RelatedInformation|.
{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.diagnostic.GetOpts*
A table with the following keys:
Expand Down Expand Up @@ -575,6 +579,23 @@ Lua module: vim.diagnostic *diagnostic-api*
• {virt_text_win_col}? (`integer`) See |nvim_buf_set_extmark()|.
• {virt_text_hide}? (`boolean`) See |nvim_buf_set_extmark()|.

*vim.diagnostic.RelatedInformation*
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.

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


config({opts}, {namespace}) *vim.diagnostic.config()*
Configure diagnostic options globally or for a specific diagnostic
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.diagnostic.RelatedInformation[]
---
--- 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.diagnostic.RelatedInformation
---
--- 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
39 changes: 38 additions & 1 deletion runtime/lua/vim/lsp/diagnostic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,38 @@ local function tags_lsp_to_vim(diagnostic, client_id)
return tags
end

---@param related_informations lsp.DiagnosticRelatedInformation[]
---@param offset_encoding string
---@return vim.diagnostic.RelatedInformation[]
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.diagnostic.RelatedInformation?
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.diagnostic.RelatedInformation
return {
bufnr = bufnr,
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 +150,17 @@ 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
40 changes: 38 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,24 @@ 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 +351,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 +376,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 3c44e73

Please sign in to comment.