mirror of
https://github.com/neovim/neovim.git
synced 2025-09-05 19:08:15 +00:00
feat(diagnostic): add support for tags
The LSP spec supports two tags that can be added to diagnostics: unnecessary and deprecated. Extend vim.diagnostic to be able to handle these.
This commit is contained in:
@@ -1113,7 +1113,8 @@ code_action({options}) *vim.lsp.buf.code_action()*
|
||||
• {options} (table|nil) Optional table which holds the following
|
||||
optional fields:
|
||||
• context: (table|nil) Corresponds to `CodeActionContext` of the LSP specification:
|
||||
• diagnostics (table|nil): LSP`Diagnostic[]` . Inferred from the current position if not provided.
|
||||
• diagnostics (table|nil): LSP `Diagnostic[]`. Inferred
|
||||
from the current position if not provided.
|
||||
• only (table|nil): List of LSP `CodeActionKind`s used to
|
||||
filter the code actions. Most language servers support
|
||||
values like `refactor` or `quickfix`.
|
||||
|
@@ -226,6 +226,9 @@ The following new APIs or features were added.
|
||||
|
||||
• Added |nvim_get_hl()| for getting highlight group definitions in a format compatible with |nvim_set_hl()|.
|
||||
|
||||
• |vim.diagnostic| now supports LSP DiagnosticsTag.
|
||||
See: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticTag
|
||||
|
||||
==============================================================================
|
||||
CHANGED FEATURES *news-changes*
|
||||
|
||||
|
@@ -483,6 +483,7 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
|
||||
local diagnostics =
|
||||
get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true)
|
||||
local line_diagnostics = diagnostic_lines(diagnostics)
|
||||
|
||||
for i = 0, line_count do
|
||||
local offset = i * (search_forward and 1 or -1)
|
||||
local lnum = position[1] + offset
|
||||
@@ -752,6 +753,7 @@ end
|
||||
---@field message string
|
||||
---@field source nil|string
|
||||
---@field code nil|string
|
||||
---@field _tags { deprecated: boolean, unnecessary: boolean}
|
||||
---@field user_data nil|any arbitrary data plugins can add
|
||||
|
||||
--- Get current diagnostics.
|
||||
@@ -948,6 +950,16 @@ M.handlers.underline = {
|
||||
higroup = underline_highlight_map.Error
|
||||
end
|
||||
|
||||
if diagnostic._tags then
|
||||
-- TODO(lewis6991): we should be able to stack these.
|
||||
if diagnostic._tags.unnecessary then
|
||||
higroup = 'DiagnosticUnnecessary'
|
||||
end
|
||||
if diagnostic._tags.deprecated then
|
||||
higroup = 'DiagnosticDeprecated'
|
||||
end
|
||||
end
|
||||
|
||||
vim.highlight.range(
|
||||
bufnr,
|
||||
underline_ns,
|
||||
|
@@ -1,13 +1,6 @@
|
||||
---@brief lsp-diagnostic
|
||||
---
|
||||
---@class Diagnostic
|
||||
---@field range Range
|
||||
---@field message string
|
||||
---@field severity DiagnosticSeverity|nil
|
||||
---@field code integer | string
|
||||
---@field source string
|
||||
---@field tags DiagnosticTag[]
|
||||
---@field relatedInformation DiagnosticRelatedInformation[]
|
||||
|
||||
local protocol = require('vim.lsp.protocol')
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -22,14 +15,16 @@ local function get_client_id(client_id)
|
||||
end
|
||||
|
||||
---@private
|
||||
---@param severity lsp.DiagnosticSeverity
|
||||
local function severity_lsp_to_vim(severity)
|
||||
if type(severity) == 'string' then
|
||||
severity = vim.lsp.protocol.DiagnosticSeverity[severity]
|
||||
severity = protocol.DiagnosticSeverity[severity]
|
||||
end
|
||||
return severity
|
||||
end
|
||||
|
||||
---@private
|
||||
---@return lsp.DiagnosticSeverity
|
||||
local function severity_vim_to_lsp(severity)
|
||||
if type(severity) == 'string' then
|
||||
severity = vim.diagnostic.severity[severity]
|
||||
@@ -38,6 +33,7 @@ local function severity_vim_to_lsp(severity)
|
||||
end
|
||||
|
||||
---@private
|
||||
---@return integer
|
||||
local function line_byte_from_position(lines, lnum, col, offset_encoding)
|
||||
if not lines or offset_encoding == 'utf-8' then
|
||||
return col
|
||||
@@ -77,12 +73,41 @@ local function get_buf_lines(bufnr)
|
||||
return lines
|
||||
end
|
||||
|
||||
--- @private
|
||||
--- @param diagnostic lsp.Diagnostic
|
||||
--- @param client_id integer
|
||||
--- @return table?
|
||||
local function tags_lsp_to_vim(diagnostic, client_id)
|
||||
local tags ---@type table?
|
||||
for _, tag in ipairs(diagnostic.tags or {}) do
|
||||
if tag == protocol.DiagnosticTag.Unnecessary then
|
||||
tags = tags or {}
|
||||
tags.unnecessary = true
|
||||
elseif tag == protocol.DiagnosticTag.Deprecated then
|
||||
tags = tags or {}
|
||||
tags.deprecated = true
|
||||
else
|
||||
vim.notify_once(
|
||||
string.format('Unknown DiagnosticTag %d from LSP client %d', tag, client_id),
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
end
|
||||
end
|
||||
return tags
|
||||
end
|
||||
|
||||
---@private
|
||||
---@param diagnostics lsp.Diagnostic[]
|
||||
---@param bufnr integer
|
||||
---@param client_id integer
|
||||
---@return Diagnostic[]
|
||||
local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
||||
local buf_lines = get_buf_lines(bufnr)
|
||||
local client = vim.lsp.get_client_by_id(client_id)
|
||||
local offset_encoding = client and client.offset_encoding or 'utf-16'
|
||||
---@diagnostic disable-next-line:no-unknown
|
||||
return vim.tbl_map(function(diagnostic)
|
||||
---@cast diagnostic lsp.Diagnostic
|
||||
local start = diagnostic.range.start
|
||||
local _end = diagnostic.range['end']
|
||||
return {
|
||||
@@ -94,12 +119,12 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
||||
message = diagnostic.message,
|
||||
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
|
||||
code = diagnostic.code,
|
||||
codeDescription = diagnostic.codeDescription,
|
||||
tags = diagnostic.tags,
|
||||
relatedInformation = diagnostic.relatedInformation,
|
||||
data = diagnostic.data,
|
||||
},
|
||||
@@ -108,9 +133,13 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
||||
end, diagnostics)
|
||||
end
|
||||
|
||||
---@private
|
||||
--- @private
|
||||
--- @param diagnostics Diagnostic[]
|
||||
--- @return lsp.Diagnostic[]
|
||||
local function diagnostic_vim_to_lsp(diagnostics)
|
||||
---@diagnostic disable-next-line:no-unknown
|
||||
return vim.tbl_map(function(diagnostic)
|
||||
---@cast diagnostic Diagnostic
|
||||
return vim.tbl_extend('keep', {
|
||||
-- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp
|
||||
range = {
|
||||
@@ -131,6 +160,7 @@ local function diagnostic_vim_to_lsp(diagnostics)
|
||||
end, diagnostics)
|
||||
end
|
||||
|
||||
---@type table<integer,integer>
|
||||
local _client_namespaces = {}
|
||||
|
||||
--- Get the diagnostic namespace associated with an LSP client |vim.diagnostic|.
|
||||
|
@@ -21,6 +21,7 @@ end
|
||||
--]=]
|
||||
|
||||
local constants = {
|
||||
--- @enum lsp.DiagnosticSeverity
|
||||
DiagnosticSeverity = {
|
||||
-- Reports an error.
|
||||
Error = 1,
|
||||
@@ -32,6 +33,7 @@ local constants = {
|
||||
Hint = 4,
|
||||
},
|
||||
|
||||
--- @enum lsp.DiagnosticTag
|
||||
DiagnosticTag = {
|
||||
-- Unused or unnecessary code
|
||||
Unnecessary = 1,
|
||||
|
@@ -18,3 +18,20 @@
|
||||
---@class lsp.FileEvent
|
||||
---@field uri string
|
||||
---@field type lsp.FileChangeType
|
||||
|
||||
---@class lsp.Position
|
||||
---@field line integer
|
||||
---@field character integer
|
||||
|
||||
---@class lsp.Range
|
||||
---@field start lsp.Position
|
||||
---@field end lsp.Position
|
||||
|
||||
---@class lsp.Diagnostic
|
||||
---@field range lsp.Range
|
||||
---@field message string
|
||||
---@field severity? lsp.DiagnosticSeverity
|
||||
---@field code integer | string
|
||||
---@field source string
|
||||
---@field tags? lsp.DiagnosticTag[]
|
||||
---@field relatedInformation DiagnosticRelatedInformation[]
|
||||
|
@@ -218,6 +218,8 @@ static const char *highlight_init_both[] = {
|
||||
"default link DiagnosticSignInfo DiagnosticInfo",
|
||||
"default link DiagnosticSignHint DiagnosticHint",
|
||||
"default link DiagnosticSignOk DiagnosticOk",
|
||||
"default DiagnosticDeprecated cterm=strikethrough gui=strikethrough guisp=Red",
|
||||
"default link DiagnosticUnnecessary Comment",
|
||||
|
||||
// Text
|
||||
"default link @text.literal Comment",
|
||||
|
@@ -86,6 +86,7 @@ describe('vim.diagnostic', function()
|
||||
it('creates highlight groups', function()
|
||||
command('runtime plugin/diagnostic.vim')
|
||||
eq({
|
||||
'DiagnosticDeprecated',
|
||||
'DiagnosticError',
|
||||
'DiagnosticFloatingError',
|
||||
'DiagnosticFloatingHint',
|
||||
@@ -105,6 +106,7 @@ describe('vim.diagnostic', function()
|
||||
'DiagnosticUnderlineInfo',
|
||||
'DiagnosticUnderlineOk',
|
||||
'DiagnosticUnderlineWarn',
|
||||
'DiagnosticUnnecessary',
|
||||
'DiagnosticVirtualTextError',
|
||||
'DiagnosticVirtualTextHint',
|
||||
'DiagnosticVirtualTextInfo',
|
||||
|
@@ -97,7 +97,6 @@ describe('vim.lsp.diagnostic', function()
|
||||
}
|
||||
|
||||
diagnostics[1].code = 42
|
||||
diagnostics[1].tags = {"foo", "bar"}
|
||||
diagnostics[1].data = "Hello world"
|
||||
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
|
||||
@@ -110,10 +109,9 @@ describe('vim.lsp.diagnostic', function()
|
||||
vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1)[1],
|
||||
}
|
||||
]]
|
||||
eq({code = 42, tags = {"foo", "bar"}, data = "Hello world"}, result[1].user_data.lsp)
|
||||
eq({code = 42, data = "Hello world"}, result[1].user_data.lsp)
|
||||
eq(42, result[1].code)
|
||||
eq(42, result[2].code)
|
||||
eq({"foo", "bar"}, result[2].tags)
|
||||
eq("Hello world", result[2].data)
|
||||
end)
|
||||
end)
|
||||
|
Reference in New Issue
Block a user