mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
refactor(diagnostic)!: replace 'show_*' functions with 'open_float' (#16057)
'show_line_diagnostics()' and 'show_position_diagnostics()' are almost identical; they differ only in the fact that the latter also accepts a column to form a full position, rather than just a line. This is not enough to justify two separate interfaces for this common functionality. Renaming this to simply 'show_diagnostics()' is one step forward, but that is also not a good name as the '_diagnostics()' suffix is redundant. However, we cannot name it simply 'show()' since that function already exists with entirely different semantics. Instead, combine these two into a single 'open_float()' function that handles all of the cases of showing diagnostics in a floating window. Also add a "float" key to 'vim.diagnostic.config()' to provide global values of configuration options that can be overridden ephemerally. This makes the float API consistent with the rest of the diagnostic API. BREAKING CHANGE
This commit is contained in:
parent
aa4f0879e3
commit
064411ea7f
@ -262,12 +262,12 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
|
||||
|
||||
For example, if a user enables virtual text globally with >
|
||||
|
||||
vim.diagnostic.config({virt_text = true})
|
||||
vim.diagnostic.config({virtual_text = true})
|
||||
<
|
||||
|
||||
and a diagnostic producer sets diagnostics with >
|
||||
|
||||
vim.diagnostic.set(ns, 0, diagnostics, {virt_text = false})
|
||||
vim.diagnostic.set(ns, 0, diagnostics, {virtual_text = false})
|
||||
<
|
||||
|
||||
then virtual text will not be enabled for those diagnostics.
|
||||
@ -302,7 +302,7 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
|
||||
• format: (function) A function that takes
|
||||
a diagnostic as input and returns a
|
||||
string. The return value is the text used
|
||||
to display the diagnostic. Example:>
|
||||
to display the diagnostic. Example: >
|
||||
|
||||
function(diagnostic)
|
||||
if diagnostic.severity == vim.diagnostic.severity.ERROR then
|
||||
@ -324,6 +324,18 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
|
||||
Otherwise, all signs use the same
|
||||
priority.
|
||||
|
||||
• float: Options for floating windows:
|
||||
• severity: See |diagnostic-severity|.
|
||||
• show_header: (boolean, default true) Show
|
||||
"Diagnostics:" header
|
||||
• source: (string) Include the diagnostic
|
||||
source in the message. One of "always" or
|
||||
"if_many".
|
||||
• format: (function) A function that takes
|
||||
a diagnostic as input and returns a
|
||||
string. The return value is the text used
|
||||
to display the diagnostic.
|
||||
|
||||
• update_in_insert: (default false) Update
|
||||
diagnostics in Insert mode (if false,
|
||||
diagnostics are updated on InsertLeave)
|
||||
@ -442,12 +454,10 @@ goto_next({opts}) *vim.diagnostic.goto_next()*
|
||||
• wrap: (boolean, default true) Whether to loop
|
||||
around file or not. Similar to 'wrapscan'.
|
||||
• severity: See |diagnostic-severity|.
|
||||
• enable_popup: (boolean, default true) Call
|
||||
|vim.diagnostic.show_line_diagnostics()| on
|
||||
jump.
|
||||
• popup_opts: (table) Table to pass as {opts}
|
||||
parameter to
|
||||
|vim.diagnostic.show_line_diagnostics()|
|
||||
• float: (boolean or table, default true) If
|
||||
"true", call |vim.diagnostic.open_float()| after
|
||||
moving. If a table, pass the table as the {opts}
|
||||
parameter to |vim.diagnostic.open_float()|.
|
||||
• win_id: (number, default 0) Window ID
|
||||
|
||||
goto_prev({opts}) *vim.diagnostic.goto_prev()*
|
||||
@ -508,6 +518,46 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults})
|
||||
diagnostic |diagnostic-structure| or `nil` if {pat} fails
|
||||
to match {str}.
|
||||
|
||||
open_float({bufnr}, {opts}) *vim.diagnostic.open_float()*
|
||||
Show diagnostics in a floating window.
|
||||
|
||||
Parameters: ~
|
||||
{bufnr} number|nil Buffer number. Defaults to the current
|
||||
buffer.
|
||||
{opts} table|nil Configuration table with the same keys
|
||||
as |vim.lsp.util.open_floating_preview()| in
|
||||
addition to the following:
|
||||
• namespace: (number) Limit diagnostics to the
|
||||
given namespace
|
||||
• scope: (string, default "buffer") Show
|
||||
diagnostics from the whole buffer ("buffer"),
|
||||
the current cursor line ("line"), or the
|
||||
current cursor position ("cursor").
|
||||
• pos: (number or table) If {scope} is "line" or
|
||||
"cursor", use this position rather than the
|
||||
cursor position. If a number, interpreted as a
|
||||
line number; otherwise, a (row, col) tuple.
|
||||
• severity_sort: (default false) Sort diagnostics
|
||||
by severity. Overrides the setting from
|
||||
|vim.diagnostic.config()|.
|
||||
• severity: See |diagnostic-severity|. Overrides
|
||||
the setting from |vim.diagnostic.config()|.
|
||||
• show_header: (boolean, default true) Show
|
||||
"Diagnostics:" header. Overrides the setting
|
||||
from |vim.diagnostic.config()|.
|
||||
• source: (string) Include the diagnostic source
|
||||
in the message. One of "always" or "if_many".
|
||||
Overrides the setting from
|
||||
|vim.diagnostic.config()|.
|
||||
• format: (function) A function that takes a
|
||||
diagnostic as input and returns a string. The
|
||||
return value is the text used to display the
|
||||
diagnostic. Overrides the setting from
|
||||
|vim.diagnostic.config()|.
|
||||
|
||||
Return: ~
|
||||
tuple ({float_bufnr}, {win_id})
|
||||
|
||||
reset({namespace}, {bufnr}) *vim.diagnostic.reset()*
|
||||
Remove all diagnostics from the given namespace.
|
||||
|
||||
@ -581,51 +631,6 @@ show({namespace}, {bufnr}, {diagnostics}, {opts})
|
||||
{opts} table|nil Display options. See
|
||||
|vim.diagnostic.config()|.
|
||||
|
||||
*vim.diagnostic.show_line_diagnostics()*
|
||||
show_line_diagnostics({opts}, {bufnr}, {lnum})
|
||||
Open a floating window with the diagnostics from the given
|
||||
line.
|
||||
|
||||
Parameters: ~
|
||||
{opts} table Configuration table. See
|
||||
|vim.diagnostic.show_position_diagnostics()|.
|
||||
{bufnr} number|nil Buffer number. Defaults to the current
|
||||
buffer.
|
||||
{lnum} number|nil Line number. Defaults to line number
|
||||
of cursor.
|
||||
|
||||
Return: ~
|
||||
tuple ({popup_bufnr}, {win_id})
|
||||
|
||||
*vim.diagnostic.show_position_diagnostics()*
|
||||
show_position_diagnostics({opts}, {bufnr}, {position})
|
||||
Open a floating window with the diagnostics at the given
|
||||
position.
|
||||
|
||||
Parameters: ~
|
||||
{opts} table|nil Configuration table with the same
|
||||
keys as |vim.lsp.util.open_floating_preview()|
|
||||
in addition to the following:
|
||||
• namespace: (number) Limit diagnostics to the
|
||||
given namespace
|
||||
• severity: See |diagnostic-severity|.
|
||||
• show_header: (boolean, default true) Show
|
||||
"Diagnostics:" header
|
||||
• source: (string) Include the diagnostic
|
||||
source in the message. One of "always" or
|
||||
"if_many".
|
||||
• format: (function) A function that takes a
|
||||
diagnostic as input and returns a string.
|
||||
The return value is the text used to display
|
||||
the diagnostic.
|
||||
{bufnr} number|nil Buffer number. Defaults to the
|
||||
current buffer.
|
||||
{position} table|nil The (0,0)-indexed position. Defaults
|
||||
to the current cursor position.
|
||||
|
||||
Return: ~
|
||||
tuple ({popup_bufnr}, {win_id})
|
||||
|
||||
toqflist({diagnostics}) *vim.diagnostic.toqflist()*
|
||||
Convert a list of diagnostics to a list of quickfix items that
|
||||
can be passed to |setqflist()| or |setloclist()|.
|
||||
|
@ -19,6 +19,7 @@ local global_diagnostic_options = {
|
||||
signs = true,
|
||||
underline = true,
|
||||
virtual_text = true,
|
||||
float = true,
|
||||
update_in_insert = false,
|
||||
severity_sort = false,
|
||||
}
|
||||
@ -119,8 +120,8 @@ end
|
||||
|
||||
---@private
|
||||
local function enabled_value(option, namespace)
|
||||
local ns = get_namespace(namespace)
|
||||
if type(ns.opts[option]) == "table" then
|
||||
local ns = namespace and get_namespace(namespace) or {}
|
||||
if ns.opts and type(ns.opts[option]) == "table" then
|
||||
return ns.opts[option]
|
||||
end
|
||||
|
||||
@ -153,8 +154,9 @@ end
|
||||
|
||||
---@private
|
||||
local function get_resolved_options(opts, namespace, bufnr)
|
||||
local ns = get_namespace(namespace)
|
||||
local resolved = vim.tbl_extend('keep', opts or {}, ns.opts, global_diagnostic_options)
|
||||
local ns = namespace and get_namespace(namespace) or {}
|
||||
-- Do not use tbl_deep_extend so that an empty table can be used to reset to default values
|
||||
local resolved = vim.tbl_extend('keep', opts or {}, ns.opts or {}, global_diagnostic_options)
|
||||
for k in pairs(global_diagnostic_options) do
|
||||
if resolved[k] ~= nil then
|
||||
resolved[k] = resolve_optional_value(k, resolved[k], namespace, bufnr)
|
||||
@ -354,19 +356,15 @@ local function schedule_display(namespace, bufnr, args)
|
||||
|
||||
local key = make_augroup_key(namespace, bufnr)
|
||||
if not registered_autocmds[key] then
|
||||
vim.cmd(string.format("augroup %s", key))
|
||||
vim.cmd(" au!")
|
||||
vim.cmd(
|
||||
string.format(
|
||||
[[autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s)]],
|
||||
vim.cmd(string.format([[augroup %s
|
||||
au!
|
||||
autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s)
|
||||
augroup END]],
|
||||
key,
|
||||
table.concat(insert_leave_auto_cmds, ","),
|
||||
bufnr,
|
||||
namespace,
|
||||
bufnr
|
||||
)
|
||||
)
|
||||
vim.cmd("augroup END")
|
||||
|
||||
bufnr))
|
||||
registered_autocmds[key] = true
|
||||
end
|
||||
end
|
||||
@ -376,76 +374,13 @@ local function clear_scheduled_display(namespace, bufnr)
|
||||
local key = make_augroup_key(namespace, bufnr)
|
||||
|
||||
if registered_autocmds[key] then
|
||||
vim.cmd(string.format("augroup %s", key))
|
||||
vim.cmd(" au!")
|
||||
vim.cmd("augroup END")
|
||||
|
||||
vim.cmd(string.format([[augroup %s
|
||||
au!
|
||||
augroup END]], key))
|
||||
registered_autocmds[key] = nil
|
||||
end
|
||||
end
|
||||
|
||||
---@private
|
||||
--- Open a floating window with the provided diagnostics
|
||||
---@param opts table Configuration table
|
||||
--- - show_header (boolean, default true): Show "Diagnostics:" header
|
||||
--- - all opts for |vim.util.open_floating_preview()| can be used here
|
||||
---@param diagnostics table: The diagnostics to display
|
||||
---@return table {popup_bufnr, win_id}
|
||||
local function show_diagnostics(opts, diagnostics)
|
||||
if not diagnostics or vim.tbl_isempty(diagnostics) then
|
||||
return
|
||||
end
|
||||
local lines = {}
|
||||
local highlights = {}
|
||||
local show_header = vim.F.if_nil(opts.show_header, true)
|
||||
if show_header then
|
||||
table.insert(lines, "Diagnostics:")
|
||||
table.insert(highlights, {0, "Bold"})
|
||||
end
|
||||
|
||||
if opts.format then
|
||||
diagnostics = reformat_diagnostics(opts.format, diagnostics)
|
||||
end
|
||||
|
||||
if opts.source then
|
||||
diagnostics = prefix_source(opts.source, diagnostics)
|
||||
end
|
||||
|
||||
-- Use global setting for severity_sort since 'show_diagnostics' is namespace
|
||||
-- independent
|
||||
local severity_sort = global_diagnostic_options.severity_sort
|
||||
if severity_sort then
|
||||
if type(severity_sort) == "table" and severity_sort.reverse then
|
||||
table.sort(diagnostics, function(a, b) return a.severity > b.severity end)
|
||||
else
|
||||
table.sort(diagnostics, function(a, b) return a.severity < b.severity end)
|
||||
end
|
||||
end
|
||||
|
||||
for i, diagnostic in ipairs(diagnostics) do
|
||||
local prefix = string.format("%d. ", i)
|
||||
local hiname = floating_highlight_map[diagnostic.severity]
|
||||
assert(hiname, 'unknown severity: ' .. tostring(diagnostic.severity))
|
||||
|
||||
local message_lines = vim.split(diagnostic.message, '\n', true)
|
||||
table.insert(lines, prefix..message_lines[1])
|
||||
table.insert(highlights, {#prefix, hiname})
|
||||
for j = 2, #message_lines do
|
||||
table.insert(lines, string.rep(' ', #prefix) .. message_lines[j])
|
||||
table.insert(highlights, {0, hiname})
|
||||
end
|
||||
end
|
||||
|
||||
local popup_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts)
|
||||
for i, hi in ipairs(highlights) do
|
||||
local prefixlen, hiname = unpack(hi)
|
||||
-- Start highlight after the prefix
|
||||
vim.api.nvim_buf_add_highlight(popup_bufnr, -1, hiname, i-1, prefixlen, -1)
|
||||
end
|
||||
|
||||
return popup_bufnr, winnr
|
||||
end
|
||||
|
||||
---@private
|
||||
local function set_list(loclist, opts)
|
||||
opts = opts or {}
|
||||
@ -469,6 +404,7 @@ local function set_list(loclist, opts)
|
||||
end
|
||||
|
||||
---@private
|
||||
--- To (slightly) improve performance, modifies diagnostics in place.
|
||||
local function clamp_line_numbers(bufnr, diagnostics)
|
||||
local buf_line_count = vim.api.nvim_buf_line_count(bufnr)
|
||||
if buf_line_count == 0 then
|
||||
@ -526,7 +462,7 @@ end
|
||||
local function diagnostic_move_pos(opts, pos)
|
||||
opts = opts or {}
|
||||
|
||||
local enable_popup = vim.F.if_nil(opts.enable_popup, true)
|
||||
local float = vim.F.if_nil(opts.float, true)
|
||||
local win_id = opts.win_id or vim.api.nvim_get_current_win()
|
||||
|
||||
if not pos then
|
||||
@ -539,10 +475,13 @@ local function diagnostic_move_pos(opts, pos)
|
||||
|
||||
vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]})
|
||||
|
||||
if enable_popup then
|
||||
-- This is a bit weird... I'm surprised that we need to wait til the next tick to do this.
|
||||
if float then
|
||||
local float_opts = type(float) == "table" and float or {}
|
||||
vim.schedule(function()
|
||||
M.show_position_diagnostics(opts.popup_opts, vim.api.nvim_win_get_buf(win_id))
|
||||
M.open_float(
|
||||
vim.api.nvim_win_get_buf(win_id),
|
||||
vim.tbl_extend("keep", float_opts, {scope="cursor"})
|
||||
)
|
||||
end)
|
||||
end
|
||||
end
|
||||
@ -561,12 +500,12 @@ end
|
||||
---
|
||||
--- For example, if a user enables virtual text globally with
|
||||
--- <pre>
|
||||
--- vim.diagnostic.config({virt_text = true})
|
||||
--- vim.diagnostic.config({virtual_text = true})
|
||||
--- </pre>
|
||||
---
|
||||
--- and a diagnostic producer sets diagnostics with
|
||||
--- <pre>
|
||||
--- vim.diagnostic.set(ns, 0, diagnostics, {virt_text = false})
|
||||
--- vim.diagnostic.set(ns, 0, diagnostics, {virtual_text = false})
|
||||
--- </pre>
|
||||
---
|
||||
--- then virtual text will not be enabled for those diagnostics.
|
||||
@ -603,6 +542,13 @@ end
|
||||
--- * priority: (number, default 10) Base priority to use for signs. When
|
||||
--- {severity_sort} is used, the priority of a sign is adjusted based on
|
||||
--- its severity. Otherwise, all signs use the same priority.
|
||||
--- - float: Options for floating windows:
|
||||
--- * severity: See |diagnostic-severity|.
|
||||
--- * show_header: (boolean, default true) Show "Diagnostics:" header
|
||||
--- * source: (string) Include the diagnostic source in
|
||||
--- the message. One of "always" or "if_many".
|
||||
--- * format: (function) A function that takes a diagnostic as input and returns a
|
||||
--- string. The return value is the text used to display the diagnostic.
|
||||
--- - update_in_insert: (default false) Update diagnostics in Insert mode (if false,
|
||||
--- diagnostics are updated on InsertLeave)
|
||||
--- - severity_sort: (default false) Sort diagnostics by severity. This affects the order in
|
||||
@ -825,10 +771,9 @@ end
|
||||
--- |nvim_win_get_cursor()|. Defaults to the current cursor position.
|
||||
--- - wrap: (boolean, default true) Whether to loop around file or not. Similar to 'wrapscan'.
|
||||
--- - severity: See |diagnostic-severity|.
|
||||
--- - enable_popup: (boolean, default true) Call |vim.diagnostic.show_line_diagnostics()|
|
||||
--- on jump.
|
||||
--- - popup_opts: (table) Table to pass as {opts} parameter to
|
||||
--- |vim.diagnostic.show_line_diagnostics()|
|
||||
--- - float: (boolean or table, default true) If "true", call |vim.diagnostic.open_float()|
|
||||
--- after moving. If a table, pass the table as the {opts} parameter to
|
||||
--- |vim.diagnostic.open_float()|.
|
||||
--- - win_id: (number, default 0) Window ID
|
||||
function M.goto_next(opts)
|
||||
return diagnostic_move_pos(
|
||||
@ -1144,68 +1089,128 @@ function M.show(namespace, bufnr, diagnostics, opts)
|
||||
save_extmarks(namespace, bufnr)
|
||||
end
|
||||
|
||||
--- Open a floating window with the diagnostics at the given position.
|
||||
--- Show diagnostics in a floating window.
|
||||
---
|
||||
---@param bufnr number|nil Buffer number. Defaults to the current buffer.
|
||||
---@param opts table|nil Configuration table with the same keys as
|
||||
--- |vim.lsp.util.open_floating_preview()| in addition to the following:
|
||||
--- - namespace: (number) Limit diagnostics to the given namespace
|
||||
--- - severity: See |diagnostic-severity|.
|
||||
--- - show_header: (boolean, default true) Show "Diagnostics:" header
|
||||
--- - source: (string) Include the diagnostic source in
|
||||
--- the message. One of "always" or "if_many".
|
||||
--- - scope: (string, default "buffer") Show diagnostics from the whole buffer ("buffer"),
|
||||
--- the current cursor line ("line"), or the current cursor position ("cursor").
|
||||
--- - pos: (number or table) If {scope} is "line" or "cursor", use this position rather
|
||||
--- than the cursor position. If a number, interpreted as a line number;
|
||||
--- otherwise, a (row, col) tuple.
|
||||
--- - severity_sort: (default false) Sort diagnostics by severity. Overrides the setting
|
||||
--- from |vim.diagnostic.config()|.
|
||||
--- - severity: See |diagnostic-severity|. Overrides the setting from
|
||||
--- |vim.diagnostic.config()|.
|
||||
--- - show_header: (boolean, default true) Show "Diagnostics:" header. Overrides the
|
||||
--- setting from |vim.diagnostic.config()|.
|
||||
--- - source: (string) Include the diagnostic source in the message. One of "always" or
|
||||
--- "if_many". Overrides the setting from |vim.diagnostic.config()|.
|
||||
--- - format: (function) A function that takes a diagnostic as input and returns a
|
||||
--- string. The return value is the text used to display the diagnostic.
|
||||
---@param bufnr number|nil Buffer number. Defaults to the current buffer.
|
||||
---@param position table|nil The (0,0)-indexed position. Defaults to the current cursor position.
|
||||
---@return tuple ({popup_bufnr}, {win_id})
|
||||
function M.show_position_diagnostics(opts, bufnr, position)
|
||||
--- Overrides the setting from |vim.diagnostic.config()|.
|
||||
---@return tuple ({float_bufnr}, {win_id})
|
||||
function M.open_float(bufnr, opts)
|
||||
vim.validate {
|
||||
opts = { opts, 't', true },
|
||||
bufnr = { bufnr, 'n', true },
|
||||
position = { position, 't', true },
|
||||
opts = { opts, 't', true },
|
||||
}
|
||||
|
||||
opts = opts or {}
|
||||
|
||||
opts.focus_id = "position_diagnostics"
|
||||
bufnr = get_bufnr(bufnr)
|
||||
if not position then
|
||||
local curr_position = vim.api.nvim_win_get_cursor(0)
|
||||
curr_position[1] = curr_position[1] - 1
|
||||
position = curr_position
|
||||
local scope = opts.scope or "buffer"
|
||||
local lnum, col
|
||||
if scope == "line" or scope == "cursor" then
|
||||
if not opts.pos then
|
||||
local pos = vim.api.nvim_win_get_cursor(0)
|
||||
lnum = pos[1] - 1
|
||||
col = pos[2]
|
||||
elseif type(opts.pos) == "number" then
|
||||
lnum = opts.pos
|
||||
elseif type(opts.pos) == "table" then
|
||||
lnum, col = unpack(opts.pos)
|
||||
else
|
||||
error("Invalid value for option 'pos'")
|
||||
end
|
||||
local match_position_predicate = function(diag)
|
||||
return position[1] == diag.lnum and
|
||||
position[2] >= diag.col and
|
||||
(position[2] <= diag.end_col or position[1] < diag.end_lnum)
|
||||
elseif scope ~= "buffer" then
|
||||
error("Invalid value for option 'scope'")
|
||||
end
|
||||
|
||||
local diagnostics = M.get(bufnr, opts)
|
||||
clamp_line_numbers(bufnr, diagnostics)
|
||||
local position_diagnostics = vim.tbl_filter(match_position_predicate, diagnostics)
|
||||
return show_diagnostics(opts, position_diagnostics)
|
||||
end
|
||||
|
||||
--- Open a floating window with the diagnostics from the given line.
|
||||
---
|
||||
---@param opts table Configuration table. See |vim.diagnostic.show_position_diagnostics()|.
|
||||
---@param bufnr number|nil Buffer number. Defaults to the current buffer.
|
||||
---@param lnum number|nil Line number. Defaults to line number of cursor.
|
||||
---@return tuple ({popup_bufnr}, {win_id})
|
||||
function M.show_line_diagnostics(opts, bufnr, lnum)
|
||||
vim.validate {
|
||||
opts = { opts, 't', true },
|
||||
bufnr = { bufnr, 'n', true },
|
||||
lnum = { lnum, 'n', true },
|
||||
}
|
||||
if scope == "line" then
|
||||
diagnostics = vim.tbl_filter(function(d)
|
||||
return d.lnum == lnum
|
||||
end, diagnostics)
|
||||
elseif scope == "cursor" then
|
||||
diagnostics = vim.tbl_filter(function(d)
|
||||
return d.lnum == lnum and d.col <= col and (d.end_col >= col or d.end_lnum > lnum)
|
||||
end, diagnostics)
|
||||
end
|
||||
|
||||
opts = opts or {}
|
||||
opts.focus_id = "line_diagnostics"
|
||||
bufnr = get_bufnr(bufnr)
|
||||
local diagnostics = M.get(bufnr, opts)
|
||||
clamp_line_numbers(bufnr, diagnostics)
|
||||
lnum = lnum or (vim.api.nvim_win_get_cursor(0)[1] - 1)
|
||||
local line_diagnostics = diagnostic_lines(diagnostics)[lnum]
|
||||
return show_diagnostics(opts, line_diagnostics)
|
||||
if vim.tbl_isempty(diagnostics) then
|
||||
return
|
||||
end
|
||||
|
||||
local severity_sort = vim.F.if_nil(opts.severity_sort, global_diagnostic_options.severity_sort)
|
||||
if severity_sort then
|
||||
if type(severity_sort) == "table" and severity_sort.reverse then
|
||||
table.sort(diagnostics, function(a, b) return a.severity > b.severity end)
|
||||
else
|
||||
table.sort(diagnostics, function(a, b) return a.severity < b.severity end)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
-- Resolve options with user settings from vim.diagnostic.config
|
||||
-- Unlike the other decoration functions (e.g. set_virtual_text, set_signs, etc.) `open_float`
|
||||
-- does not have a dedicated table for configuration options; instead, the options are mixed in
|
||||
-- with its `opts` table which also includes "keyword" parameters. So we create a dedicated
|
||||
-- options table that inherits missing keys from the global configuration before resolving.
|
||||
local t = global_diagnostic_options.float
|
||||
local float_opts = vim.tbl_extend("keep", opts, type(t) == "table" and t or {})
|
||||
opts = get_resolved_options({ float = float_opts }, nil, bufnr).float
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
local highlights = {}
|
||||
local show_header = vim.F.if_nil(opts.show_header, true)
|
||||
if show_header then
|
||||
table.insert(lines, "Diagnostics:")
|
||||
table.insert(highlights, {0, "Bold"})
|
||||
end
|
||||
|
||||
if opts.format then
|
||||
diagnostics = reformat_diagnostics(opts.format, diagnostics)
|
||||
end
|
||||
|
||||
if opts.source then
|
||||
diagnostics = prefix_source(opts.source, diagnostics)
|
||||
end
|
||||
|
||||
for i, diagnostic in ipairs(diagnostics) do
|
||||
local prefix = string.format("%d. ", i)
|
||||
local hiname = floating_highlight_map[diagnostic.severity]
|
||||
local message_lines = vim.split(diagnostic.message, '\n')
|
||||
table.insert(lines, prefix..message_lines[1])
|
||||
table.insert(highlights, {#prefix, hiname})
|
||||
for j = 2, #message_lines do
|
||||
table.insert(lines, string.rep(' ', #prefix) .. message_lines[j])
|
||||
table.insert(highlights, {0, hiname})
|
||||
end
|
||||
end
|
||||
|
||||
local float_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts)
|
||||
for i, hi in ipairs(highlights) do
|
||||
local prefixlen, hiname = unpack(hi)
|
||||
-- Start highlight after the prefix
|
||||
vim.api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i-1, prefixlen, -1)
|
||||
end
|
||||
|
||||
return float_bufnr, winnr
|
||||
end
|
||||
|
||||
--- Remove all diagnostics from the given namespace.
|
||||
|
@ -551,14 +551,15 @@ end
|
||||
---@param position table|nil The (0,0)-indexed position
|
||||
---@return table {popup_bufnr, win_id}
|
||||
function M.show_position_diagnostics(opts, buf_nr, position)
|
||||
if opts then
|
||||
opts = opts or {}
|
||||
opts.where = "cursor"
|
||||
opts.pos = position
|
||||
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
|
||||
end
|
||||
return vim.diagnostic.show_position_diagnostics(opts, buf_nr, position)
|
||||
return vim.diagnostic.open_float(buf_nr, opts)
|
||||
end
|
||||
|
||||
--- Open a floating window with the diagnostics from {line_nr}
|
||||
@ -573,11 +574,13 @@ end
|
||||
---@param client_id number|nil the client id
|
||||
---@return table {popup_bufnr, win_id}
|
||||
function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id)
|
||||
if client_id then
|
||||
opts = opts or {}
|
||||
opts.where = "line"
|
||||
opts.pos = line_nr
|
||||
if client_id then
|
||||
opts.namespace = M.get_namespace(client_id)
|
||||
end
|
||||
return vim.diagnostic.show_line_diagnostics(opts, buf_nr, line_nr)
|
||||
return vim.diagnostic.open_float(buf_nr, opts)
|
||||
end
|
||||
|
||||
--- Redraw diagnostics for the given buffer and client
|
||||
|
@ -968,8 +968,87 @@ describe('vim.diagnostic', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('show_line_diagnostics()', function()
|
||||
it('creates floating window and returns popup bufnr and winnr if current line contains diagnostics', function()
|
||||
describe('open_float()', function()
|
||||
it('can show diagnostics from the whole buffer', function()
|
||||
eq({'1. Syntax error', '2. Some warning'}, exec_lua [[
|
||||
local diagnostics = {
|
||||
make_error("Syntax error", 0, 1, 0, 3),
|
||||
make_warning("Some warning", 1, 1, 1, 3),
|
||||
}
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header = false})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
end)
|
||||
|
||||
it('can show diagnostics from a single line', function()
|
||||
-- Using cursor position
|
||||
eq({'1. Some warning'}, exec_lua [[
|
||||
local diagnostics = {
|
||||
make_error("Syntax error", 0, 1, 0, 3),
|
||||
make_warning("Some warning", 1, 1, 1, 3),
|
||||
}
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
vim.api.nvim_win_set_cursor(0, {2, 1})
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="line"})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
|
||||
-- With specified position
|
||||
eq({'1. Some warning'}, exec_lua [[
|
||||
local diagnostics = {
|
||||
make_error("Syntax error", 0, 1, 0, 3),
|
||||
make_warning("Some warning", 1, 1, 1, 3),
|
||||
}
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
vim.api.nvim_win_set_cursor(0, {1, 1})
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="line", pos=1})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
end)
|
||||
|
||||
it('can show diagnostics from a specific position', function()
|
||||
-- Using cursor position
|
||||
eq({'1. Syntax error'}, exec_lua [[
|
||||
local diagnostics = {
|
||||
make_error("Syntax error", 1, 1, 1, 2),
|
||||
make_warning("Some warning", 1, 3, 1, 4),
|
||||
}
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
vim.api.nvim_win_set_cursor(0, {2, 2})
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="cursor"})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
|
||||
-- With specified position
|
||||
eq({'1. Some warning'}, exec_lua [[
|
||||
local diagnostics = {
|
||||
make_error("Syntax error", 1, 1, 1, 2),
|
||||
make_warning("Some warning", 1, 3, 1, 4),
|
||||
}
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
vim.api.nvim_win_set_cursor(0, {1, 1})
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="cursor", pos={1,3}})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
end)
|
||||
|
||||
it('creates floating window and returns float bufnr and winnr if current line contains diagnostics', function()
|
||||
-- Two lines:
|
||||
-- Diagnostic:
|
||||
-- 1. <msg>
|
||||
@ -979,8 +1058,10 @@ describe('vim.diagnostic', function()
|
||||
}
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics()
|
||||
return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {scope="line"})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return #lines
|
||||
]])
|
||||
end)
|
||||
|
||||
@ -996,8 +1077,10 @@ describe('vim.diagnostic', function()
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, buf_1_diagnostics)
|
||||
vim.diagnostic.set(other_ns, other_bufnr, buf_2_diagnostics)
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics()
|
||||
return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float()
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return #lines
|
||||
]])
|
||||
end)
|
||||
|
||||
@ -1012,12 +1095,14 @@ describe('vim.diagnostic', function()
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diagnostics)
|
||||
vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diagnostics)
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics({namespace = diagnostic_ns})
|
||||
return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {namespace = diagnostic_ns})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return #lines
|
||||
]])
|
||||
end)
|
||||
|
||||
it('creates floating window and returns popup bufnr and winnr without header, if requested', function()
|
||||
it('creates floating window and returns float bufnr and winnr without header, if requested', function()
|
||||
-- One line (since no header):
|
||||
-- 1. <msg>
|
||||
eq(1, exec_lua [[
|
||||
@ -1026,8 +1111,10 @@ describe('vim.diagnostic', function()
|
||||
}
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics {show_header = false}
|
||||
return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {show_header = false})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return #lines
|
||||
]])
|
||||
end)
|
||||
|
||||
@ -1038,8 +1125,10 @@ describe('vim.diagnostic', function()
|
||||
}
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics({show_header = false}, diagnostic_bufnr, 5)
|
||||
return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {show_header = false, scope = "line", pos = 5})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return #lines
|
||||
]])
|
||||
end)
|
||||
|
||||
@ -1051,21 +1140,21 @@ describe('vim.diagnostic', function()
|
||||
make_error("Syntax error", 0, 1, 0, 3, "source x"),
|
||||
}
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics {
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {
|
||||
show_header = false,
|
||||
source = "if_many",
|
||||
}
|
||||
local lines = vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
|
||||
eq({"1. source x: Syntax error"}, exec_lua [[
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics {
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {
|
||||
show_header = false,
|
||||
source = "always",
|
||||
}
|
||||
local lines = vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
@ -1076,11 +1165,11 @@ describe('vim.diagnostic', function()
|
||||
make_error("Another error", 0, 1, 0, 3, "source y"),
|
||||
}
|
||||
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics {
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {
|
||||
show_header = false,
|
||||
source = "if_many",
|
||||
}
|
||||
local lines = vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
})
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
@ -1101,24 +1190,24 @@ describe('vim.diagnostic', function()
|
||||
|
||||
vim.diagnostic.config({severity_sort = false})
|
||||
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics { show_header = false }
|
||||
local lines = vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false })
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
|
||||
eq({"1. Syntax error", "2. Error", "3. Warning", "4. Info"}, exec_lua [[
|
||||
vim.diagnostic.config({severity_sort = true})
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics { show_header = false }
|
||||
local lines = vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false })
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
|
||||
eq({"1. Info", "2. Warning", "3. Error", "4. Syntax error"}, exec_lua [[
|
||||
vim.diagnostic.config({severity_sort = { reverse = true } })
|
||||
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics { show_header = false }
|
||||
local lines = vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
|
||||
local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false })
|
||||
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
|
||||
vim.api.nvim_win_close(winnr, true)
|
||||
return lines
|
||||
]])
|
||||
|
Loading…
Reference in New Issue
Block a user