mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 19:38:20 +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
|
• {options} (table|nil) Optional table which holds the following
|
||||||
optional fields:
|
optional fields:
|
||||||
• context: (table|nil) Corresponds to `CodeActionContext` of the LSP specification:
|
• 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
|
• only (table|nil): List of LSP `CodeActionKind`s used to
|
||||||
filter the code actions. Most language servers support
|
filter the code actions. Most language servers support
|
||||||
values like `refactor` or `quickfix`.
|
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()|.
|
• 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*
|
CHANGED FEATURES *news-changes*
|
||||||
|
|
||||||
|
@@ -483,6 +483,7 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
|
|||||||
local diagnostics =
|
local diagnostics =
|
||||||
get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true)
|
get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true)
|
||||||
local line_diagnostics = diagnostic_lines(diagnostics)
|
local line_diagnostics = diagnostic_lines(diagnostics)
|
||||||
|
|
||||||
for i = 0, line_count do
|
for i = 0, line_count do
|
||||||
local offset = i * (search_forward and 1 or -1)
|
local offset = i * (search_forward and 1 or -1)
|
||||||
local lnum = position[1] + offset
|
local lnum = position[1] + offset
|
||||||
@@ -752,6 +753,7 @@ end
|
|||||||
---@field message string
|
---@field message string
|
||||||
---@field source nil|string
|
---@field source nil|string
|
||||||
---@field code nil|string
|
---@field code nil|string
|
||||||
|
---@field _tags { deprecated: boolean, unnecessary: boolean}
|
||||||
---@field user_data nil|any arbitrary data plugins can add
|
---@field user_data nil|any arbitrary data plugins can add
|
||||||
|
|
||||||
--- Get current diagnostics.
|
--- Get current diagnostics.
|
||||||
@@ -948,6 +950,16 @@ M.handlers.underline = {
|
|||||||
higroup = underline_highlight_map.Error
|
higroup = underline_highlight_map.Error
|
||||||
end
|
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(
|
vim.highlight.range(
|
||||||
bufnr,
|
bufnr,
|
||||||
underline_ns,
|
underline_ns,
|
||||||
|
@@ -1,13 +1,6 @@
|
|||||||
---@brief lsp-diagnostic
|
---@brief lsp-diagnostic
|
||||||
---
|
|
||||||
---@class Diagnostic
|
local protocol = require('vim.lsp.protocol')
|
||||||
---@field range Range
|
|
||||||
---@field message string
|
|
||||||
---@field severity DiagnosticSeverity|nil
|
|
||||||
---@field code integer | string
|
|
||||||
---@field source string
|
|
||||||
---@field tags DiagnosticTag[]
|
|
||||||
---@field relatedInformation DiagnosticRelatedInformation[]
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -22,14 +15,16 @@ local function get_client_id(client_id)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
|
---@param severity lsp.DiagnosticSeverity
|
||||||
local function severity_lsp_to_vim(severity)
|
local function severity_lsp_to_vim(severity)
|
||||||
if type(severity) == 'string' then
|
if type(severity) == 'string' then
|
||||||
severity = vim.lsp.protocol.DiagnosticSeverity[severity]
|
severity = protocol.DiagnosticSeverity[severity]
|
||||||
end
|
end
|
||||||
return severity
|
return severity
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
|
---@return lsp.DiagnosticSeverity
|
||||||
local function severity_vim_to_lsp(severity)
|
local function severity_vim_to_lsp(severity)
|
||||||
if type(severity) == 'string' then
|
if type(severity) == 'string' then
|
||||||
severity = vim.diagnostic.severity[severity]
|
severity = vim.diagnostic.severity[severity]
|
||||||
@@ -38,6 +33,7 @@ local function severity_vim_to_lsp(severity)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
|
---@return integer
|
||||||
local function line_byte_from_position(lines, lnum, col, offset_encoding)
|
local function line_byte_from_position(lines, lnum, col, offset_encoding)
|
||||||
if not lines or offset_encoding == 'utf-8' then
|
if not lines or offset_encoding == 'utf-8' then
|
||||||
return col
|
return col
|
||||||
@@ -78,11 +74,40 @@ local function get_buf_lines(bufnr)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- @private
|
--- @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 function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
||||||
local buf_lines = get_buf_lines(bufnr)
|
local buf_lines = get_buf_lines(bufnr)
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
local offset_encoding = client and client.offset_encoding or 'utf-16'
|
local offset_encoding = client and client.offset_encoding or 'utf-16'
|
||||||
|
---@diagnostic disable-next-line:no-unknown
|
||||||
return vim.tbl_map(function(diagnostic)
|
return vim.tbl_map(function(diagnostic)
|
||||||
|
---@cast diagnostic lsp.Diagnostic
|
||||||
local start = diagnostic.range.start
|
local start = diagnostic.range.start
|
||||||
local _end = diagnostic.range['end']
|
local _end = diagnostic.range['end']
|
||||||
return {
|
return {
|
||||||
@@ -94,12 +119,12 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
|||||||
message = diagnostic.message,
|
message = diagnostic.message,
|
||||||
source = diagnostic.source,
|
source = diagnostic.source,
|
||||||
code = diagnostic.code,
|
code = diagnostic.code,
|
||||||
|
tags = tags_lsp_to_vim(diagnostic, client_id),
|
||||||
user_data = {
|
user_data = {
|
||||||
lsp = {
|
lsp = {
|
||||||
-- usage of user_data.lsp.code is deprecated in favor of the top-level code field
|
-- usage of user_data.lsp.code is deprecated in favor of the top-level code field
|
||||||
code = diagnostic.code,
|
code = diagnostic.code,
|
||||||
codeDescription = diagnostic.codeDescription,
|
codeDescription = diagnostic.codeDescription,
|
||||||
tags = diagnostic.tags,
|
|
||||||
relatedInformation = diagnostic.relatedInformation,
|
relatedInformation = diagnostic.relatedInformation,
|
||||||
data = diagnostic.data,
|
data = diagnostic.data,
|
||||||
},
|
},
|
||||||
@@ -109,8 +134,12 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- @private
|
--- @private
|
||||||
|
--- @param diagnostics Diagnostic[]
|
||||||
|
--- @return lsp.Diagnostic[]
|
||||||
local function diagnostic_vim_to_lsp(diagnostics)
|
local function diagnostic_vim_to_lsp(diagnostics)
|
||||||
|
---@diagnostic disable-next-line:no-unknown
|
||||||
return vim.tbl_map(function(diagnostic)
|
return vim.tbl_map(function(diagnostic)
|
||||||
|
---@cast diagnostic Diagnostic
|
||||||
return vim.tbl_extend('keep', {
|
return vim.tbl_extend('keep', {
|
||||||
-- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp
|
-- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp
|
||||||
range = {
|
range = {
|
||||||
@@ -131,6 +160,7 @@ local function diagnostic_vim_to_lsp(diagnostics)
|
|||||||
end, diagnostics)
|
end, diagnostics)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@type table<integer,integer>
|
||||||
local _client_namespaces = {}
|
local _client_namespaces = {}
|
||||||
|
|
||||||
--- Get the diagnostic namespace associated with an LSP client |vim.diagnostic|.
|
--- Get the diagnostic namespace associated with an LSP client |vim.diagnostic|.
|
||||||
|
@@ -21,6 +21,7 @@ end
|
|||||||
--]=]
|
--]=]
|
||||||
|
|
||||||
local constants = {
|
local constants = {
|
||||||
|
--- @enum lsp.DiagnosticSeverity
|
||||||
DiagnosticSeverity = {
|
DiagnosticSeverity = {
|
||||||
-- Reports an error.
|
-- Reports an error.
|
||||||
Error = 1,
|
Error = 1,
|
||||||
@@ -32,6 +33,7 @@ local constants = {
|
|||||||
Hint = 4,
|
Hint = 4,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
--- @enum lsp.DiagnosticTag
|
||||||
DiagnosticTag = {
|
DiagnosticTag = {
|
||||||
-- Unused or unnecessary code
|
-- Unused or unnecessary code
|
||||||
Unnecessary = 1,
|
Unnecessary = 1,
|
||||||
|
@@ -18,3 +18,20 @@
|
|||||||
---@class lsp.FileEvent
|
---@class lsp.FileEvent
|
||||||
---@field uri string
|
---@field uri string
|
||||||
---@field type lsp.FileChangeType
|
---@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 DiagnosticSignInfo DiagnosticInfo",
|
||||||
"default link DiagnosticSignHint DiagnosticHint",
|
"default link DiagnosticSignHint DiagnosticHint",
|
||||||
"default link DiagnosticSignOk DiagnosticOk",
|
"default link DiagnosticSignOk DiagnosticOk",
|
||||||
|
"default DiagnosticDeprecated cterm=strikethrough gui=strikethrough guisp=Red",
|
||||||
|
"default link DiagnosticUnnecessary Comment",
|
||||||
|
|
||||||
// Text
|
// Text
|
||||||
"default link @text.literal Comment",
|
"default link @text.literal Comment",
|
||||||
|
@@ -86,6 +86,7 @@ describe('vim.diagnostic', function()
|
|||||||
it('creates highlight groups', function()
|
it('creates highlight groups', function()
|
||||||
command('runtime plugin/diagnostic.vim')
|
command('runtime plugin/diagnostic.vim')
|
||||||
eq({
|
eq({
|
||||||
|
'DiagnosticDeprecated',
|
||||||
'DiagnosticError',
|
'DiagnosticError',
|
||||||
'DiagnosticFloatingError',
|
'DiagnosticFloatingError',
|
||||||
'DiagnosticFloatingHint',
|
'DiagnosticFloatingHint',
|
||||||
@@ -105,6 +106,7 @@ describe('vim.diagnostic', function()
|
|||||||
'DiagnosticUnderlineInfo',
|
'DiagnosticUnderlineInfo',
|
||||||
'DiagnosticUnderlineOk',
|
'DiagnosticUnderlineOk',
|
||||||
'DiagnosticUnderlineWarn',
|
'DiagnosticUnderlineWarn',
|
||||||
|
'DiagnosticUnnecessary',
|
||||||
'DiagnosticVirtualTextError',
|
'DiagnosticVirtualTextError',
|
||||||
'DiagnosticVirtualTextHint',
|
'DiagnosticVirtualTextHint',
|
||||||
'DiagnosticVirtualTextInfo',
|
'DiagnosticVirtualTextInfo',
|
||||||
|
@@ -97,7 +97,6 @@ describe('vim.lsp.diagnostic', function()
|
|||||||
}
|
}
|
||||||
|
|
||||||
diagnostics[1].code = 42
|
diagnostics[1].code = 42
|
||||||
diagnostics[1].tags = {"foo", "bar"}
|
|
||||||
diagnostics[1].data = "Hello world"
|
diagnostics[1].data = "Hello world"
|
||||||
|
|
||||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
|
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],
|
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[1].code)
|
||||||
eq(42, result[2].code)
|
eq(42, result[2].code)
|
||||||
eq({"foo", "bar"}, result[2].tags)
|
|
||||||
eq("Hello world", result[2].data)
|
eq("Hello world", result[2].data)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user