aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLewis Russell <lewis6991@gmail.com>2023-03-30 14:49:58 +0100
committerGitHub <noreply@github.com>2023-03-30 14:49:58 +0100
commit226a6c3eaef2a7220841d3d5e69e1baf543b3d6f (patch)
tree394cafe6a57a124635941517b2acd94d9d850efa
parent8fa7d833cf3a6c906c91c5acf9187b4544cf94be (diff)
downloadrneovim-226a6c3eaef2a7220841d3d5e69e1baf543b3d6f.tar.gz
rneovim-226a6c3eaef2a7220841d3d5e69e1baf543b3d6f.tar.bz2
rneovim-226a6c3eaef2a7220841d3d5e69e1baf543b3d6f.zip
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.
-rw-r--r--runtime/doc/lsp.txt3
-rw-r--r--runtime/doc/news.txt3
-rw-r--r--runtime/lua/vim/diagnostic.lua12
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua54
-rw-r--r--runtime/lua/vim/lsp/protocol.lua2
-rw-r--r--runtime/lua/vim/lsp/types.lua17
-rw-r--r--src/nvim/highlight_group.c2
-rw-r--r--test/functional/lua/diagnostic_spec.lua2
-rw-r--r--test/functional/plugin/lsp/diagnostic_spec.lua4
9 files changed, 83 insertions, 16 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 0164b34efb..f5695669ae 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -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`.
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 890a033268..401719b432 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -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*
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 56532d0184..714038f8e4 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -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,
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index b27bf6e425..dcc8f6549c 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -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|.
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 1686e22c48..f4489ad17d 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -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,
diff --git a/runtime/lua/vim/lsp/types.lua b/runtime/lua/vim/lsp/types.lua
index 1aea6841ee..779f313aa7 100644
--- a/runtime/lua/vim/lsp/types.lua
+++ b/runtime/lua/vim/lsp/types.lua
@@ -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[]
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 38af2a708a..057447c9f4 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -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",
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index d364986ad7..7b4d68c9cd 100644
--- a/test/functional/lua/diagnostic_spec.lua
+++ b/test/functional/lua/diagnostic_spec.lua
@@ -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',
diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua
index f73ffc29b0..f58016bf01 100644
--- a/test/functional/plugin/lsp/diagnostic_spec.lua
+++ b/test/functional/plugin/lsp/diagnostic_spec.lua
@@ -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)