aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/diagnostic.lua
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim/diagnostic.lua')
-rw-r--r--runtime/lua/vim/diagnostic.lua266
1 files changed, 199 insertions, 67 deletions
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 348204abb7..ef00a1fa51 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -42,7 +42,7 @@ local M = {}
---
--- @field namespace? integer
---- Each of the configuration options below accepts one of the following:
+--- Many of the configuration options below accept one of the following:
--- - `false`: Disable this feature
--- - `true`: Enable this feature, use default settings.
--- - `table`: Enable this feature with overrides. Use an empty table to use default values.
@@ -78,6 +78,9 @@ local M = {}
--- - {reverse}? (boolean) Reverse sort order
--- (default: `false`)
--- @field severity_sort? boolean|{reverse?:boolean}
+---
+--- Default values for |vim.diagnostic.jump()|. See |vim.diagnostic.Opts.Jump|.
+--- @field jump? vim.diagnostic.Opts.Jump
--- @class (private) vim.diagnostic.OptsResolved
--- @field float vim.diagnostic.Opts.Float
@@ -105,7 +108,7 @@ local M = {}
--- 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.
---- @field pos? integer|{[1]:integer,[2]:integer}
+--- @field pos? integer|[integer,integer]
---
--- Sort diagnostics by severity.
--- Overrides the setting from |vim.diagnostic.config()|.
@@ -119,7 +122,7 @@ local M = {}
--- String to use as the header for the floating window. If a table, it is
--- interpreted as a `[text, hl_group]` tuple.
--- Overrides the setting from |vim.diagnostic.config()|.
---- @field header? string|{[1]:string,[2]:any}
+--- @field header? string|[string,any]
---
--- Include the diagnostic source in the message.
--- Use "if_many" to only show sources if there is more than one source of
@@ -200,7 +203,7 @@ local M = {}
--- @field hl_mode? 'replace'|'combine'|'blend'
---
--- See |nvim_buf_set_extmark()|.
---- @field virt_text? {[1]:string,[2]:any}[]
+--- @field virt_text? [string,any][]
---
--- See |nvim_buf_set_extmark()|.
--- @field virt_text_pos? 'eol'|'overlay'|'right_align'|'inline'
@@ -241,6 +244,22 @@ local M = {}
--- whole line the sign is placed in.
--- @field linehl? table<vim.diagnostic.Severity,string>
+--- @class vim.diagnostic.Opts.Jump
+---
+--- Default value of the {float} parameter of |vim.diagnostic.jump()|.
+--- (default: false)
+--- @field float? boolean|vim.diagnostic.Opts.Float
+---
+--- Default value of the {wrap} parameter of |vim.diagnostic.jump()|.
+--- (default: true)
+--- @field wrap? boolean
+---
+--- Default value of the {severity} parameter of |vim.diagnostic.jump()|.
+--- @field severity? vim.diagnostic.SeverityFilter
+---
+--- Default value of the {_highest} parameter of |vim.diagnostic.jump()|.
+--- @field package _highest? boolean
+
-- TODO: inherit from `vim.diagnostic.Opts`, implement its fields.
--- Optional filters |kwargs|, or `nil` for all.
--- @class vim.diagnostic.Filter
@@ -284,6 +303,13 @@ local global_diagnostic_options = {
float = true,
update_in_insert = false,
severity_sort = false,
+ jump = {
+ -- Do not show floating window
+ float = false,
+
+ -- Wrap around buffer
+ wrap = true,
+ },
}
--- @class (private) vim.diagnostic.Handler
@@ -835,21 +861,36 @@ local function filter_highest(diagnostics)
end
end
---- @param position {[1]: integer, [2]: integer}
--- @param search_forward boolean
---- @param bufnr integer
---- @param opts vim.diagnostic.GotoOpts
---- @param namespace integer[]|integer
+--- @param opts vim.diagnostic.JumpOpts?
--- @return vim.Diagnostic?
-local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
+local function next_diagnostic(search_forward, opts)
+ opts = opts or {}
+
+ -- Support deprecated win_id alias
+ if opts.win_id then
+ vim.deprecate('opts.win_id', 'opts.winid', '0.13')
+ opts.winid = opts.win_id
+ opts.win_id = nil
+ end
+
+ -- Support deprecated cursor_position alias
+ if opts.cursor_position then
+ vim.deprecate('opts.cursor_position', 'opts.pos', '0.13')
+ opts.pos = opts.cursor_position
+ opts.cursor_position = nil
+ end
+
+ local winid = opts.winid or api.nvim_get_current_win()
+ local bufnr = api.nvim_win_get_buf(winid)
+ local position = opts.pos or api.nvim_win_get_cursor(winid)
+
+ -- Adjust row to be 0-indexed
position[1] = position[1] - 1
- bufnr = get_bufnr(bufnr)
- local wrap = if_nil(opts.wrap, true)
- local get_opts = vim.deepcopy(opts)
- get_opts.namespace = get_opts.namespace or namespace
+ local wrap = if_nil(opts.wrap, true)
- local diagnostics = get_diagnostics(bufnr, get_opts, true)
+ local diagnostics = get_diagnostics(bufnr, opts, true)
if opts._highest then
filter_highest(diagnostics)
@@ -902,32 +943,40 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
end
end
---- @param opts vim.diagnostic.GotoOpts?
---- @param pos {[1]:integer,[2]:integer}|false
-local function diagnostic_move_pos(opts, pos)
- opts = opts or {}
-
- local float = if_nil(opts.float, true)
- local win_id = opts.win_id or api.nvim_get_current_win()
-
- if not pos then
+--- Move the cursor to the given diagnostic.
+---
+--- @param diagnostic vim.Diagnostic?
+--- @param opts vim.diagnostic.JumpOpts?
+local function goto_diagnostic(diagnostic, opts)
+ if not diagnostic then
api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
return
end
- api.nvim_win_call(win_id, function()
+ opts = opts or {}
+
+ -- Support deprecated win_id alias
+ if opts.win_id then
+ vim.deprecate('opts.win_id', 'opts.winid', '0.13')
+ opts.winid = opts.win_id
+ opts.win_id = nil
+ end
+
+ local winid = opts.winid or api.nvim_get_current_win()
+
+ vim._with({ win = winid }, function()
-- Save position in the window's jumplist
vim.cmd("normal! m'")
- api.nvim_win_set_cursor(win_id, { pos[1] + 1, pos[2] })
+ api.nvim_win_set_cursor(winid, { diagnostic.lnum + 1, diagnostic.col })
-- Open folds under the cursor
vim.cmd('normal! zv')
end)
- if float then
- local float_opts = type(float) == 'table' and float or {}
+ if opts.float then
+ local float_opts = type(opts.float) == 'table' and opts.float or {}
vim.schedule(function()
M.open_float(vim.tbl_extend('keep', float_opts, {
- bufnr = api.nvim_win_get_buf(win_id),
+ bufnr = api.nvim_win_get_buf(winid),
scope = 'cursor',
focus = false,
}))
@@ -1114,24 +1163,24 @@ end
--- Get the previous diagnostic closest to the cursor position.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
---@return vim.Diagnostic? : Previous diagnostic
function M.get_prev(opts)
- opts = opts or {}
-
- local win_id = opts.win_id or api.nvim_get_current_win()
- local bufnr = api.nvim_win_get_buf(win_id)
- local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
-
- return next_diagnostic(cursor_position, false, bufnr, opts, opts.namespace)
+ return next_diagnostic(false, opts)
end
--- Return the position of the previous diagnostic in the current buffer.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
---@return table|false: Previous diagnostic position as a `(row, col)` tuple
--- or `false` if there is no prior diagnostic.
+---@deprecated
function M.get_prev_pos(opts)
+ vim.deprecate(
+ 'vim.diagnostic.get_prev_pos()',
+ 'access the lnum and col fields from get_prev() instead',
+ '0.13'
+ )
local prev = M.get_prev(opts)
if not prev then
return false
@@ -1141,31 +1190,35 @@ function M.get_prev_pos(opts)
end
--- Move to the previous diagnostic in the current buffer.
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
+---@deprecated
function M.goto_prev(opts)
- return diagnostic_move_pos(opts, M.get_prev_pos(opts))
+ vim.deprecate('vim.diagnostic.goto_prev()', 'vim.diagnostic.jump()', '0.13')
+ opts = opts or {}
+ opts.float = if_nil(opts.float, true)
+ goto_diagnostic(M.get_prev(opts), opts)
end
--- Get the next diagnostic closest to the cursor position.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
---@return vim.Diagnostic? : Next diagnostic
function M.get_next(opts)
- opts = opts or {}
-
- local win_id = opts.win_id or api.nvim_get_current_win()
- local bufnr = api.nvim_win_get_buf(win_id)
- local cursor_position = opts.cursor_position or api.nvim_win_get_cursor(win_id)
-
- return next_diagnostic(cursor_position, true, bufnr, opts, opts.namespace)
+ return next_diagnostic(true, opts)
end
--- Return the position of the next diagnostic in the current buffer.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
---@return table|false : Next diagnostic position as a `(row, col)` tuple or false if no next
--- diagnostic.
+---@deprecated
function M.get_next_pos(opts)
+ vim.deprecate(
+ 'vim.diagnostic.get_next_pos()',
+ 'access the lnum and col fields from get_next() instead',
+ '0.13'
+ )
local next = M.get_next(opts)
if not next then
return false
@@ -1186,13 +1239,23 @@ end
--- See |diagnostic-severity|.
--- @field severity? vim.diagnostic.SeverityFilter
---- Configuration table with the following keys:
---- @class vim.diagnostic.GotoOpts : vim.diagnostic.GetOpts
+--- Configuration table with the keys listed below. Some parameters can have their default values
+--- changed with |vim.diagnostic.config()|.
+--- @class vim.diagnostic.JumpOpts : vim.diagnostic.GetOpts
+---
+--- The diagnostic to jump to. Mutually exclusive with {count}, {namespace},
+--- and {severity}.
+--- @field diagnostic? vim.Diagnostic
---
---- Cursor position as a `(row, col)` tuple.
---- See |nvim_win_get_cursor()|.
---- (default: current cursor position)
---- @field cursor_position? {[1]:integer,[2]:integer}
+--- The number of diagnostics to move by, starting from {pos}. A positive
+--- integer moves forward by {count} diagnostics, while a negative integer moves
+--- backward by {count} diagnostics. Mutually exclusive with {diagnostic}.
+--- @field count? integer
+---
+--- Cursor position as a `(row, col)` tuple. See |nvim_win_get_cursor()|. Used
+--- to find the nearest diagnostic when {count} is used. Only used when {count}
+--- is non-nil. Default is the current cursor position.
+--- @field pos? [integer,integer]
---
--- Whether to loop around file or not. Similar to 'wrapscan'.
--- (default: `true`)
@@ -1209,18 +1272,78 @@ end
--- If a table, pass the table as the {opts} parameter to |vim.diagnostic.open_float()|.
--- Unless overridden, the float will show diagnostics at the new cursor
--- position (as if "cursor" were passed to the "scope" option).
---- (default: `true`)
+--- (default: `false`)
--- @field float? boolean|vim.diagnostic.Opts.Float
---
--- Window ID
--- (default: `0`)
---- @field win_id? integer
+--- @field winid? integer
+
+--- Move to a diagnostic.
+---
+--- @param opts vim.diagnostic.JumpOpts
+--- @return vim.Diagnostic? # The diagnostic that was moved to.
+function M.jump(opts)
+ vim.validate('opts', opts, 'table')
+
+ -- One of "diagnostic" or "count" must be provided
+ assert(
+ opts.diagnostic or opts.count,
+ 'One of "diagnostic" or "count" must be specified in the options to vim.diagnostic.jump()'
+ )
+
+ -- Apply configuration options from vim.diagnostic.config()
+ opts = vim.tbl_deep_extend('keep', opts, global_diagnostic_options.jump)
+
+ if opts.diagnostic then
+ goto_diagnostic(opts.diagnostic, opts)
+ return opts.diagnostic
+ end
+
+ local count = opts.count
+ if count == 0 then
+ return nil
+ end
+
+ -- Support deprecated cursor_position alias
+ if opts.cursor_position then
+ vim.deprecate('opts.cursor_position', 'opts.pos', '0.13')
+ opts.pos = opts.cursor_position
+ opts.cursor_position = nil
+ end
+
+ local diag = nil
+ while count ~= 0 do
+ local next = next_diagnostic(count > 0, opts)
+ if not next then
+ break
+ end
+
+ -- Update cursor position
+ opts.pos = { next.lnum + 1, next.col }
+
+ if count > 0 then
+ count = count - 1
+ else
+ count = count + 1
+ end
+ diag = next
+ end
+
+ goto_diagnostic(diag, opts)
+
+ return diag
+end
--- Move to the next diagnostic.
---
----@param opts? vim.diagnostic.GotoOpts
+---@param opts? vim.diagnostic.JumpOpts
+---@deprecated
function M.goto_next(opts)
- diagnostic_move_pos(opts, M.get_next_pos(opts))
+ vim.deprecate('vim.diagnostic.goto_next()', 'vim.diagnostic.jump()', '0.13')
+ opts = opts or {}
+ opts.float = if_nil(opts.float, true)
+ goto_diagnostic(M.get_next(opts), opts)
end
M.handlers.signs = {
@@ -1239,6 +1362,10 @@ M.handlers.signs = {
bufnr = get_bufnr(bufnr)
opts = opts or {}
+ if not api.nvim_buf_is_loaded(bufnr) then
+ return
+ end
+
if opts.signs and opts.signs.severity then
diagnostics = filter_by_severity(opts.signs.severity, diagnostics)
end
@@ -1321,8 +1448,10 @@ M.handlers.signs = {
local numhl = opts.signs.numhl or {}
local linehl = opts.signs.linehl or {}
+ local line_count = api.nvim_buf_line_count(bufnr)
+
for _, diagnostic in ipairs(diagnostics) do
- if api.nvim_buf_is_loaded(diagnostic.bufnr) then
+ if diagnostic.lnum <= line_count then
api.nvim_buf_set_extmark(bufnr, ns.user_data.sign_ns, diagnostic.lnum, 0, {
sign_text = text[diagnostic.severity] or text[M.severity[diagnostic.severity]] or 'U',
sign_hl_group = sign_highlight_map[diagnostic.severity],
@@ -1688,7 +1817,7 @@ end
---
---@param opts vim.diagnostic.Opts.Float?
---@return integer? float_bufnr
----@return integer? win_id
+---@return integer? winid
function M.open_float(opts, ...)
-- Support old (bufnr, opts) signature
local bufnr --- @type integer?
@@ -1739,16 +1868,19 @@ function M.open_float(opts, ...)
if scope == 'line' then
--- @param d vim.Diagnostic
diagnostics = vim.tbl_filter(function(d)
- return lnum >= d.lnum and lnum <= d.end_lnum
+ return lnum >= d.lnum
+ and lnum <= d.end_lnum
+ and (d.lnum == d.end_lnum or lnum ~= d.end_lnum or d.end_col ~= 0)
end, diagnostics)
elseif scope == 'cursor' then
- -- LSP servers can send diagnostics with `end_col` past the length of the line
+ -- If `col` is past the end of the line, show if the cursor is on the last char in the line
local line_length = #api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
--- @param d vim.Diagnostic
diagnostics = vim.tbl_filter(function(d)
- return d.lnum == lnum
- and math.min(d.col, line_length - 1) <= col
- and (d.end_col >= col or d.end_lnum > lnum)
+ return lnum >= d.lnum
+ and lnum <= d.end_lnum
+ and (lnum ~= d.lnum or col >= math.min(d.col, line_length - 1))
+ and ((d.lnum == d.end_lnum and d.col == d.end_col) or lnum ~= d.end_lnum or col < d.end_col)
end, diagnostics)
end
@@ -1946,7 +2078,7 @@ end
--- @field title? string
---
--- See |diagnostic-severity|.
---- @field severity? vim.diagnostic.Severity
+--- @field severity? vim.diagnostic.SeverityFilter
--- Add all diagnostics to the quickfix list.
---
@@ -1974,7 +2106,7 @@ end
--- @field title? string
---
--- See |diagnostic-severity|.
---- @field severity? vim.diagnostic.Severity
+--- @field severity? vim.diagnostic.SeverityFilter
--- Add buffer diagnostics to the location list.
---