aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/diagnostic.lua
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/lsp/diagnostic.lua')
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua208
1 files changed, 103 insertions, 105 deletions
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index b6f0cfa0b3..08cea13548 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -1,8 +1,4 @@
----@brief lsp-diagnostic
-
-local util = require('vim.lsp.util')
local protocol = require('vim.lsp.protocol')
-local log = require('vim.lsp.log')
local ms = protocol.Methods
local api = vim.api
@@ -24,7 +20,7 @@ end
---@param severity lsp.DiagnosticSeverity
local function severity_lsp_to_vim(severity)
if type(severity) == 'string' then
- severity = protocol.DiagnosticSeverity[severity]
+ severity = protocol.DiagnosticSeverity[severity] --- @type integer
end
return severity
end
@@ -37,6 +33,10 @@ local function severity_vim_to_lsp(severity)
return severity
end
+---@param lines string[]?
+---@param lnum integer
+---@param col integer
+---@param offset_encoding string
---@return integer
local function line_byte_from_position(lines, lnum, col, offset_encoding)
if not lines or offset_encoding == 'utf-8' then
@@ -46,12 +46,14 @@ local function line_byte_from_position(lines, lnum, col, offset_encoding)
local line = lines[lnum + 1]
local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == 'utf-16')
if ok then
- return result
+ return result --- @type integer
end
return col
end
+---@param bufnr integer
+---@return string[]?
local function get_buf_lines(bufnr)
if vim.api.nvim_buf_is_loaded(bufnr) then
return vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
@@ -89,7 +91,7 @@ local function tags_lsp_to_vim(diagnostic, client_id)
tags = tags or {}
tags.deprecated = true
else
- log.info(string.format('Unknown DiagnosticTag %d from LSP client %d', tag, client_id))
+ vim.lsp.log.info(string.format('Unknown DiagnosticTag %d from LSP client %d', tag, client_id))
end
end
return tags
@@ -98,16 +100,17 @@ end
---@param diagnostics lsp.Diagnostic[]
---@param bufnr integer
---@param client_id integer
----@return Diagnostic[]
+---@return vim.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
+ --- @param diagnostic lsp.Diagnostic
+ --- @return vim.Diagnostic
return vim.tbl_map(function(diagnostic)
- ---@cast diagnostic lsp.Diagnostic
local start = diagnostic.range.start
local _end = diagnostic.range['end']
+ --- @type vim.Diagnostic
return {
lnum = start.line,
col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding),
@@ -131,12 +134,29 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
end, diagnostics)
end
---- @param diagnostics Diagnostic[]
+--- @param diagnostic vim.Diagnostic
+--- @return lsp.DiagnosticTag[]?
+local function tags_vim_to_lsp(diagnostic)
+ if not diagnostic._tags then
+ return
+ end
+
+ local tags = {} --- @type lsp.DiagnosticTag[]
+ if diagnostic._tags.unnecessary then
+ tags[#tags + 1] = protocol.DiagnosticTag.Unnecessary
+ end
+ if diagnostic._tags.deprecated then
+ tags[#tags + 1] = protocol.DiagnosticTag.Deprecated
+ end
+ return tags
+end
+
+--- @param diagnostics vim.Diagnostic[]
--- @return lsp.Diagnostic[]
local function diagnostic_vim_to_lsp(diagnostics)
- ---@diagnostic disable-next-line:no-unknown
+ ---@param diagnostic vim.Diagnostic
+ ---@return lsp.Diagnostic
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 = {
@@ -153,6 +173,7 @@ local function diagnostic_vim_to_lsp(diagnostics)
message = diagnostic.message,
source = diagnostic.source,
code = diagnostic.code,
+ tags = tags_vim_to_lsp(diagnostic),
}, diagnostic.user_data and (diagnostic.user_data.lsp or {}) or {})
end, diagnostics)
end
@@ -198,6 +219,47 @@ function M.get_namespace(client_id, is_pull)
end
end
+local function convert_severity(opt)
+ if type(opt) == 'table' and not opt.severity and opt.severity_limit then
+ vim.deprecate('severity_limit', '{min = severity} See vim.diagnostic.severity', '0.11')
+ opt.severity = { min = severity_lsp_to_vim(opt.severity_limit) }
+ end
+end
+
+--- @param uri string
+--- @param client_id? integer
+--- @param diagnostics vim.Diagnostic[]
+--- @param is_pull boolean
+--- @param config? vim.diagnostic.Opts
+local function handle_diagnostics(uri, client_id, diagnostics, is_pull, config)
+ local fname = vim.uri_to_fname(uri)
+
+ if #diagnostics == 0 and vim.fn.bufexists(fname) == 0 then
+ return
+ end
+
+ local bufnr = vim.fn.bufadd(fname)
+ if not bufnr then
+ return
+ end
+
+ client_id = get_client_id(client_id)
+ local namespace = M.get_namespace(client_id, is_pull)
+
+ if config then
+ --- @cast config table<string, table>
+ for _, opt in pairs(config) do
+ convert_severity(opt)
+ end
+ -- Persist configuration to ensure buffer reloads use the same
+ -- configuration. To make lsp.with configuration work (See :help
+ -- lsp-handler-configuration)
+ vim.diagnostic.config(config, namespace)
+ end
+
+ vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
+end
+
--- |lsp-handler| for the method "textDocument/publishDiagnostics"
---
--- See |vim.diagnostic.config()| for configuration options. Handler-specific
@@ -223,40 +285,12 @@ end
--- )
--- ```
---
----@param config table Configuration table (see |vim.diagnostic.config()|).
+---@param _ lsp.ResponseError?
+---@param result lsp.PublishDiagnosticsParams
+---@param ctx lsp.HandlerContext
+---@param config? vim.diagnostic.Opts Configuration table (see |vim.diagnostic.config()|).
function M.on_publish_diagnostics(_, result, ctx, config)
- local client_id = ctx.client_id
- local uri = result.uri
- local fname = vim.uri_to_fname(uri)
- local diagnostics = result.diagnostics
- if #diagnostics == 0 and vim.fn.bufexists(fname) == 0 then
- return
- end
- local bufnr = vim.fn.bufadd(fname)
-
- if not bufnr then
- return
- end
-
- client_id = get_client_id(client_id)
- local namespace = M.get_namespace(client_id, false)
-
- if config then
- for _, opt in pairs(config) do
- if type(opt) == 'table' then
- if not opt.severity and opt.severity_limit then
- opt.severity = { min = severity_lsp_to_vim(opt.severity_limit) }
- end
- end
- end
-
- -- Persist configuration to ensure buffer reloads use the same
- -- configuration. To make lsp.with configuration work (See :help
- -- lsp-handler-configuration)
- vim.diagnostic.config(config, namespace)
- end
-
- vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
+ handle_diagnostics(result.uri, ctx.client_id, result.diagnostics, false, config)
end
--- |lsp-handler| for the method "textDocument/diagnostic"
@@ -284,48 +318,16 @@ end
--- )
--- ```
---
----@param config table Configuration table (see |vim.diagnostic.config()|).
+---@param _ lsp.ResponseError?
+---@param result lsp.DocumentDiagnosticReport
+---@param ctx lsp.HandlerContext
+---@param config vim.diagnostic.Opts Configuration table (see |vim.diagnostic.config()|).
function M.on_diagnostic(_, result, ctx, config)
- local client_id = ctx.client_id
- local uri = ctx.params.textDocument.uri
- local fname = vim.uri_to_fname(uri)
-
- if result == nil then
- return
- end
-
- if result.kind == 'unchanged' then
- return
- end
-
- local diagnostics = result.items
- if #diagnostics == 0 and vim.fn.bufexists(fname) == 0 then
+ if result == nil or result.kind == 'unchanged' then
return
end
- local bufnr = vim.fn.bufadd(fname)
-
- if not bufnr then
- return
- end
-
- client_id = get_client_id(client_id)
-
- local namespace = M.get_namespace(client_id, true)
-
- if config then
- for _, opt in pairs(config) do
- if type(opt) == 'table' and not opt.severity and opt.severity_limit then
- opt.severity = { min = severity_lsp_to_vim(opt.severity_limit) }
- end
- end
-
- -- Persist configuration to ensure buffer reloads use the same
- -- configuration. To make lsp.with configuration work (See :help
- -- lsp-handler-configuration)
- vim.diagnostic.config(config, namespace)
- end
- vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
+ handle_diagnostics(ctx.params.textDocument.uri, ctx.client_id, result.items, true, config)
end
--- Clear push diagnostics and diagnostic cache.
@@ -335,7 +337,7 @@ end
--- implementation so it's simply marked @private rather than @deprecated.
---
---@param client_id integer
----@param buffer_client_map table map of buffers to active clients
+---@param buffer_client_map table<integer, table<integer, table>> map of buffers to active clients
---@private
function M.reset(client_id, buffer_client_map)
buffer_client_map = vim.deepcopy(buffer_client_map)
@@ -356,34 +358,28 @@ end
---
---@param bufnr integer|nil The buffer number
---@param line_nr integer|nil The line number
----@param opts table|nil Configuration keys
---- - severity: (DiagnosticSeverity, default nil)
---- - Only return diagnostics with this severity. Overrides severity_limit
---- - severity_limit: (DiagnosticSeverity, default nil)
---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
+---@param opts {severity?:lsp.DiagnosticSeverity}?
+--- - severity: (lsp.DiagnosticSeverity)
+--- - Only return diagnostics with this severity.
---@param client_id integer|nil the client id
---@return table Table with map of line number to list of diagnostics.
--- Structured: { [1] = {...}, [5] = {.... } }
---@private
function M.get_line_diagnostics(bufnr, line_nr, opts, client_id)
- opts = opts or {}
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
- end
+ convert_severity(opts)
+ local diag_opts = {} --- @type vim.diagnostic.GetOpts
- if client_id then
- opts.namespace = M.get_namespace(client_id, false)
+ if opts and opts.severity then
+ diag_opts.severity = severity_lsp_to_vim(opts.severity)
end
- if not line_nr then
- line_nr = vim.api.nvim_win_get_cursor(0)[1] - 1
+ if client_id then
+ diag_opts.namespace = M.get_namespace(client_id, false)
end
- opts.lnum = line_nr
+ diag_opts.lnum = line_nr or (api.nvim_win_get_cursor(0)[1] - 1)
- return diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, opts))
+ return diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, diag_opts))
end
--- Clear diagnostics from pull based clients
@@ -394,12 +390,13 @@ local function clear(bufnr)
end
end
----@class lsp.diagnostic.bufstate
+---@class (private) lsp.diagnostic.bufstate
---@field enabled boolean Whether inlay hints are enabled for this buffer
---@type table<integer, lsp.diagnostic.bufstate>
local bufstates = {}
--- Disable pull diagnostics for a buffer
+--- @param bufnr integer
--- @private
local function disable(bufnr)
local bufstate = bufstates[bufnr]
@@ -416,7 +413,7 @@ end
local function _refresh(bufnr, opts)
opts = opts or {}
opts['bufnr'] = bufnr
- util._refresh(ms.textDocument_diagnostic, opts)
+ vim.lsp.util._refresh(ms.textDocument_diagnostic, opts)
end
--- Enable pull diagnostics for a buffer
@@ -440,7 +437,8 @@ function M._enable(bufnr)
return
end
if bufstates[bufnr] and bufstates[bufnr].enabled then
- _refresh(bufnr, { only_visible = true, client_id = opts.data.client_id })
+ local client_id = opts.data.client_id --- @type integer?
+ _refresh(bufnr, { only_visible = true, client_id = client_id })
end
end,
group = augroup,