Files
neovim/test/functional/lua/diagnostic_spec.lua
Anton Kastritskii b79ff967ac feat(statusline): vim.diagnostic.status() #33723
Problem:
Not easy to get a status string for diagnostics.

Solution:
- Add vim.diagnostic.status().
- Add it to the default 'statusline'.
2025-07-29 18:53:57 -07:00

4253 lines
137 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local command = n.command
local clear = n.clear
local exec_lua = n.exec_lua
local eq = t.eq
local neq = t.neq
local matches = t.matches
local retry = t.retry
local api = n.api
local pcall_err = t.pcall_err
local fn = n.fn
describe('vim.diagnostic', function()
before_each(function()
clear()
exec_lua(function()
require('vim.diagnostic')
local function make_diagnostic(msg, lnum, col, end_lnum, end_col, severity, source, code)
return {
lnum = lnum,
col = col,
end_lnum = end_lnum,
end_col = end_col,
message = msg,
severity = severity,
source = source,
code = code,
}
end
function _G.make_error(msg, lnum, col, end_lnum, end_col, source, code)
return make_diagnostic(
msg,
lnum,
col,
end_lnum,
end_col,
vim.diagnostic.severity.ERROR,
source,
code
)
end
function _G.make_warning(msg, lnum, col, end_lnum, end_col, source, code)
return make_diagnostic(
msg,
lnum,
col,
end_lnum,
end_col,
vim.diagnostic.severity.WARN,
source,
code
)
end
function _G.make_info(msg, lnum, col, end_lnum, end_col, source, code)
return make_diagnostic(
msg,
lnum,
col,
end_lnum,
end_col,
vim.diagnostic.severity.INFO,
source,
code
)
end
function _G.make_hint(msg, lnum, col, end_lnum, end_col, source, code)
return make_diagnostic(
msg,
lnum,
col,
end_lnum,
end_col,
vim.diagnostic.severity.HINT,
source,
code
)
end
function _G.count_diagnostics(bufnr, severity, namespace)
return #vim.diagnostic.get(bufnr, { severity = severity, namespace = namespace })
end
function _G.count_extmarks(bufnr, namespace)
local ns = vim.diagnostic.get_namespace(namespace)
local extmarks = 0
if ns.user_data.virt_text_ns then
extmarks = extmarks
+ #vim.api.nvim_buf_get_extmarks(bufnr, ns.user_data.virt_text_ns, 0, -1, {})
end
if ns.user_data.underline_ns then
extmarks = extmarks
+ #vim.api.nvim_buf_get_extmarks(bufnr, ns.user_data.underline_ns, 0, -1, {})
end
return extmarks
end
function _G.get_virt_text_extmarks(ns)
ns = vim.diagnostic.get_namespace(ns)
local virt_text_ns = ns.user_data.virt_text_ns
return vim.api.nvim_buf_get_extmarks(
_G.diagnostic_bufnr,
virt_text_ns,
0,
-1,
{ details = true }
)
end
function _G.get_virt_lines_extmarks(ns)
ns = vim.diagnostic.get_namespace(ns)
local virt_lines_ns = ns.user_data.virt_lines_ns
return vim.api.nvim_buf_get_extmarks(
_G.diagnostic_bufnr,
virt_lines_ns,
0,
-1,
{ details = true }
)
end
---@param ns integer
function _G.get_underline_extmarks(ns)
---@type integer
local underline_ns = vim.diagnostic.get_namespace(ns).user_data.underline_ns
return vim.api.nvim_buf_get_extmarks(
_G.diagnostic_bufnr,
underline_ns,
0,
-1,
{ details = true }
)
end
end)
exec_lua(function()
_G.diagnostic_ns = vim.api.nvim_create_namespace('diagnostic_spec')
_G.other_ns = vim.api.nvim_create_namespace('other_namespace')
_G.diagnostic_bufnr = vim.api.nvim_create_buf(true, false)
local lines = { '1st line of text', '2nd line of text', 'wow', 'cool', 'more', 'lines' }
vim.fn.bufload(_G.diagnostic_bufnr)
vim.api.nvim_buf_set_lines(_G.diagnostic_bufnr, 0, 1, false, lines)
end)
end)
it('creates highlight groups', function()
command('runtime plugin/diagnostic.vim')
eq({
'DiagnosticDeprecated',
'DiagnosticError',
'DiagnosticFloatingError',
'DiagnosticFloatingHint',
'DiagnosticFloatingInfo',
'DiagnosticFloatingOk',
'DiagnosticFloatingWarn',
'DiagnosticHint',
'DiagnosticInfo',
'DiagnosticOk',
'DiagnosticSignError',
'DiagnosticSignHint',
'DiagnosticSignInfo',
'DiagnosticSignOk',
'DiagnosticSignWarn',
'DiagnosticUnderlineError',
'DiagnosticUnderlineHint',
'DiagnosticUnderlineInfo',
'DiagnosticUnderlineOk',
'DiagnosticUnderlineWarn',
'DiagnosticUnnecessary',
'DiagnosticVirtualLinesError',
'DiagnosticVirtualLinesHint',
'DiagnosticVirtualLinesInfo',
'DiagnosticVirtualLinesOk',
'DiagnosticVirtualLinesWarn',
'DiagnosticVirtualTextError',
'DiagnosticVirtualTextHint',
'DiagnosticVirtualTextInfo',
'DiagnosticVirtualTextOk',
'DiagnosticVirtualTextWarn',
'DiagnosticWarn',
}, fn.getcompletion('Diagnostic', 'highlight'))
end)
it('retrieves diagnostics from all buffers and namespaces', function()
local result = exec_lua(function()
local other_bufnr = vim.api.nvim_create_buf(true, false)
local lines = vim.api.nvim_buf_get_lines(_G.diagnostic_bufnr, 0, -1, true)
vim.api.nvim_buf_set_lines(other_bufnr, 0, 1, false, lines)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
_G.make_error('Diagnostic #2', 2, 1, 2, 1),
})
vim.diagnostic.set(_G.other_ns, other_bufnr, {
_G.make_error('Diagnostic #3', 3, 1, 3, 1),
})
return vim.diagnostic.get()
end)
eq(3, #result)
eq(
2,
exec_lua(function()
return #vim.tbl_filter(function(d)
return d.bufnr == _G.diagnostic_bufnr
end, result)
end)
)
eq('Diagnostic #1', result[1].message)
end)
it('removes diagnostics from the cache when a buffer is removed', function()
eq(
2,
exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
local other_bufnr = vim.fn.bufadd('test | test')
local lines = vim.api.nvim_buf_get_lines(_G.diagnostic_bufnr, 0, -1, true)
vim.api.nvim_buf_set_lines(other_bufnr, 0, 1, false, lines)
vim.cmd('bunload! ' .. other_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
_G.make_error('Diagnostic #2', 2, 1, 2, 1),
})
vim.diagnostic.set(_G.diagnostic_ns, other_bufnr, {
_G.make_error('Diagnostic #3', 3, 1, 3, 1),
})
vim.api.nvim_set_current_buf(other_bufnr)
vim.opt_local.buflisted = true
vim.cmd('bwipeout!')
return #vim.diagnostic.get()
end)
)
eq(
2,
exec_lua(function()
vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
vim.opt_local.buflisted = false
return #vim.diagnostic.get()
end)
)
eq(
0,
exec_lua(function()
vim.cmd('bwipeout!')
return #vim.diagnostic.get()
end)
)
end)
it('removes diagnostic from stale cache on reset', function()
local diagnostics = exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
_G.make_error('Diagnostic #2', 2, 1, 2, 1),
})
vim.fn.bufadd('test | test')
vim.cmd('noautocmd bwipeout! ' .. _G.diagnostic_bufnr)
return vim.diagnostic.get(_G.diagnostic_bufnr)
end)
eq(2, #diagnostics)
diagnostics = exec_lua(function()
vim.diagnostic.reset()
return vim.diagnostic.get()
end)
eq(0, #diagnostics)
end)
it('always returns a copy of diagnostic tables', function()
local result = exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
})
local diag = vim.diagnostic.get()
diag[1].col = 10000
return vim.diagnostic.get()[1].col == 10000
end)
eq(false, result)
end)
it('resolves buffer number 0 to the current buffer', function()
eq(
2,
exec_lua(function()
vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
_G.make_error('Diagnostic #2', 2, 1, 2, 1),
})
return #vim.diagnostic.get(0)
end)
)
end)
it('saves and count a single error', function()
eq(
1,
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
})
return _G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
)
end)
)
end)
it('saves and count multiple errors', function()
eq(
2,
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
_G.make_error('Diagnostic #2', 2, 1, 2, 1),
})
return _G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
)
end)
)
end)
it('saves and count from multiple namespaces', function()
eq(
{ 1, 1, 2 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic From Server 1', 1, 1, 1, 1),
})
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic From Server 2', 1, 1, 1, 1),
})
return {
-- First namespace
_G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
),
-- Second namespace
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.other_ns),
-- All namespaces
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.ERROR),
}
end)
)
end)
it('saves and count from multiple namespaces with respect to severity', function()
eq(
{ 3, 0, 3 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic From Server 1:1', 1, 1, 1, 1),
_G.make_error('Diagnostic From Server 1:2', 2, 2, 2, 2),
_G.make_error('Diagnostic From Server 1:3', 2, 3, 3, 2),
})
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, {
_G.make_warning('Warning From Server 2', 3, 3, 3, 3),
})
return {
-- Namespace 1
_G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
),
-- Namespace 2
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.other_ns),
-- All namespaces
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.ERROR),
}
end)
)
end)
it('handles one namespace clearing highlights while the other still has highlights', function()
exec_lua(function()
vim.diagnostic.config({ virtual_text = true })
end)
-- 1 Error (1)
-- 1 Warning (2)
-- 1 Warning (2) + 1 Warning (1)
-- 2 highlights and 2 underlines (since error)
-- 1 highlight + 1 underline
local all_highlights = { 1, 1, 2, 4, 2 }
eq(
all_highlights,
exec_lua(function()
local ns_1_diags = {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 2, 1, 2, 3),
}
local ns_2_diags = {
_G.make_warning('Warning 1', 2, 1, 2, 3),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diags)
return {
_G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN, _G.other_ns),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN),
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns),
_G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns),
}
end)
)
-- Clear diagnostics from namespace 1, and make sure we have the right amount of stuff for namespace 2
eq(
{ 1, 1, 2, 0, 2 },
exec_lua(function()
vim.diagnostic.enable(false, { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns })
return {
_G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN, _G.other_ns),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN),
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns),
_G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns),
}
end)
)
-- Show diagnostics from namespace 1 again
eq(
all_highlights,
exec_lua(function()
vim.diagnostic.enable(true, { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns })
return {
_G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN, _G.other_ns),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN),
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns),
_G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns),
}
end)
)
end)
it('does not display diagnostics when disabled', function()
exec_lua(function()
vim.diagnostic.config({ virtual_text = true })
end)
eq(
{ 0, 2 },
exec_lua(function()
local ns_1_diags = {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 2, 1, 2, 3),
}
local ns_2_diags = {
_G.make_warning('Warning 1', 2, 1, 2, 3),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diags)
vim.diagnostic.enable(false, { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns })
return {
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns),
_G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns),
}
end)
)
eq(
{ 4, 0 },
exec_lua(function()
vim.diagnostic.enable(true, { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns })
vim.diagnostic.enable(false, { bufnr = _G.diagnostic_bufnr, ns_id = _G.other_ns })
return {
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns),
_G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns),
}
end)
)
end)
describe('show() and hide()', function()
it('works', function()
local result = exec_lua(function()
local other_bufnr = vim.api.nvim_create_buf(true, false)
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
local result = {}
vim.diagnostic.config({ underline = false, virtual_text = true })
local ns_1_diags = {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 2, 1, 2, 5),
}
local ns_2_diags = {
_G.make_warning('Warning 1', 2, 1, 2, 5),
}
local other_buffer_diags = {
_G.make_info('This is interesting', 0, 0, 0, 0),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diags)
vim.diagnostic.set(_G.diagnostic_ns, other_bufnr, other_buffer_diags)
-- All buffers and namespaces
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
-- Hide one namespace
vim.diagnostic.hide(_G.diagnostic_ns)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
-- Show one namespace
vim.diagnostic.show(_G.diagnostic_ns)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
-- Hide one buffer
vim.diagnostic.hide(nil, other_bufnr)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
-- Hide everything
vim.diagnostic.hide()
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
-- Show one buffer
vim.diagnostic.show(nil, _G.diagnostic_bufnr)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
return result
end)
eq(4, result[1])
eq(1, result[2])
eq(4, result[3])
eq(3, result[4])
eq(0, result[5])
eq(3, result[6])
end)
it("doesn't error after bwipeout on buffer", function()
exec_lua(function()
vim.diagnostic.set(
_G.diagnostic_ns,
_G.diagnostic_bufnr,
{ { message = '', lnum = 0, end_lnum = 0, col = 0, end_col = 0 } }
)
vim.cmd('bwipeout! ' .. _G.diagnostic_bufnr)
vim.diagnostic.show(_G.diagnostic_ns)
vim.diagnostic.hide(_G.diagnostic_ns)
end)
end)
end)
describe('enable() and disable()', function()
it('validation', function()
matches('expected boolean, got table', pcall_err(exec_lua, [[vim.diagnostic.enable({})]]))
matches(
'filter: expected table, got string',
pcall_err(exec_lua, [[vim.diagnostic.enable(false, '')]])
)
matches(
'Invalid buffer id: 42',
pcall_err(exec_lua, [[vim.diagnostic.enable(true, { bufnr = 42 })]])
)
matches(
'expected boolean, got number',
pcall_err(exec_lua, [[vim.diagnostic.enable(42, {})]])
)
matches('expected boolean, got table', pcall_err(exec_lua, [[vim.diagnostic.enable({}, 42)]]))
end)
it('without arguments', function()
local result = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
local result = {}
vim.diagnostic.config({ underline = false, virtual_text = true })
local ns_1_diags = {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 2, 1, 2, 5),
}
local ns_2_diags = {
_G.make_warning('Warning 1', 2, 1, 2, 5),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diags)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
)
vim.diagnostic.enable(false)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
)
-- Create a new buffer
local other_bufnr = vim.api.nvim_create_buf(true, false)
local other_buffer_diags = {
_G.make_info('This is interesting', 0, 0, 0, 0),
}
vim.diagnostic.set(_G.diagnostic_ns, other_bufnr, other_buffer_diags)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
vim.diagnostic.enable()
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
return result
end)
eq(3, result[1])
eq(0, result[2])
eq(0, result[3])
eq(4, result[4])
end)
it('with buffer argument', function()
local result = exec_lua(function()
local other_bufnr = vim.api.nvim_create_buf(true, false)
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
local result = {}
vim.diagnostic.config({ underline = false, virtual_text = true })
local ns_1_diags = {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 2, 1, 2, 5),
}
local ns_2_diags = {
_G.make_warning('Warning 1', 2, 1, 2, 5),
}
local other_buffer_diags = {
_G.make_info('This is interesting', 0, 0, 0, 0),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diags)
vim.diagnostic.set(_G.diagnostic_ns, other_bufnr, other_buffer_diags)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
vim.diagnostic.enable(false, { bufnr = _G.diagnostic_bufnr })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
vim.diagnostic.enable(true, { bufnr = _G.diagnostic_bufnr })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
vim.diagnostic.enable(false, { bufnr = other_bufnr })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
return result
end)
eq(4, result[1])
eq(1, result[2])
eq(4, result[3])
eq(3, result[4])
end)
it('with a namespace argument', function()
local result = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
local result = {}
vim.diagnostic.config({ underline = false, virtual_text = true })
local ns_1_diags = {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 2, 1, 2, 5),
}
local ns_2_diags = {
_G.make_warning('Warning 1', 2, 1, 2, 5),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diags)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
)
vim.diagnostic.enable(false, { ns_id = _G.diagnostic_ns })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
)
vim.diagnostic.enable(true, { ns_id = _G.diagnostic_ns })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
)
vim.diagnostic.enable(false, { ns_id = _G.other_ns })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
)
return result
end)
eq(3, result[1])
eq(1, result[2])
eq(3, result[3])
eq(2, result[4])
end)
--- @return table
local function test_enable()
return exec_lua(function()
local other_bufnr = vim.api.nvim_create_buf(true, false)
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
local result = {}
vim.diagnostic.config({ underline = false, virtual_text = true })
local ns_1_diags = {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 2, 1, 2, 5),
}
local ns_2_diags = {
_G.make_warning('Warning 1', 2, 1, 2, 5),
}
local other_buffer_diags = {
_G.make_info('This is interesting', 0, 0, 0, 0),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diags)
vim.diagnostic.set(_G.diagnostic_ns, other_bufnr, other_buffer_diags)
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
vim.diagnostic.enable(false, { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
vim.diagnostic.enable(false, { bufnr = _G.diagnostic_bufnr, ns_id = _G.other_ns })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
vim.diagnostic.enable(true, { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
-- Should have no effect
vim.diagnostic.enable(false, { bufnr = other_bufnr, ns_id = _G.other_ns })
table.insert(
result,
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
+ _G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns)
+ _G.count_extmarks(other_bufnr, _G.diagnostic_ns)
)
return result
end)
end
it('with both buffer and namespace arguments', function()
local result = test_enable()
eq(4, result[1])
eq(2, result[2])
eq(1, result[3])
eq(3, result[4])
eq(3, result[5])
end)
end)
describe('reset()', function()
it('diagnostic count is 0 and displayed diagnostics are 0 after call', function()
exec_lua(function()
vim.diagnostic.config({ virtual_text = true })
end)
-- 1 Error (1)
-- 1 Warning (2)
-- 1 Warning (2) + 1 Warning (1)
-- 2 highlights and 2 underlines (since error)
-- 1 highlight + 1 underline
local all_highlights = { 1, 1, 2, 4, 2 }
eq(
all_highlights,
exec_lua(function()
local ns_1_diags = {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 2, 1, 2, 3),
}
local ns_2_diags = {
_G.make_warning('Warning 1', 2, 1, 2, 3),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diags)
return {
_G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN, _G.other_ns),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN),
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns),
_G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns),
}
end)
)
-- Reset diagnostics from namespace 1
exec_lua([[ vim.diagnostic.reset( _G.diagnostic_ns) ]])
-- Make sure we have the right diagnostic count
eq(
{ 0, 1, 1, 0, 2 },
exec_lua(function()
local diagnostic_count = {}
vim.wait(100, function()
diagnostic_count = {
_G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN, _G.other_ns),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN),
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns),
_G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns),
}
end)
return diagnostic_count
end)
)
-- Reset diagnostics from namespace 2
exec_lua([[ vim.diagnostic.reset(_G.other_ns) ]])
-- Make sure we have the right diagnostic count
eq(
{ 0, 0, 0, 0, 0 },
exec_lua(function()
local diagnostic_count = {}
vim.wait(100, function()
diagnostic_count = {
_G.count_diagnostics(
_G.diagnostic_bufnr,
vim.diagnostic.severity.ERROR,
_G.diagnostic_ns
),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN, _G.other_ns),
_G.count_diagnostics(_G.diagnostic_bufnr, vim.diagnostic.severity.WARN),
_G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns),
_G.count_extmarks(_G.diagnostic_bufnr, _G.other_ns),
}
end)
return diagnostic_count
end)
)
end)
it("doesn't error after bwipeout called on buffer", function()
exec_lua(function()
vim.diagnostic.set(
_G.diagnostic_ns,
_G.diagnostic_bufnr,
{ { message = '', lnum = 0, end_lnum = 0, col = 0, end_col = 0 } }
)
vim.cmd('bwipeout! ' .. _G.diagnostic_bufnr)
vim.diagnostic.reset(_G.diagnostic_ns)
end)
end)
end)
describe('get_next()', function()
it('can find the next pos with only one namespace', function()
eq(
{ 1, 1 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
local next = vim.diagnostic.get_next()
return { next.lnum, next.col }
end)
)
end)
it('can find next pos with two errors', function()
eq(
{ 4, 4 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
_G.make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 3, 1 })
local next = vim.diagnostic.get_next({ namespace = _G.diagnostic_ns })
return { next.lnum, next.col }
end)
)
end)
it('can cycle when position is past error', function()
eq(
{ 1, 1 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 3, 1 })
local next = vim.diagnostic.get_next({ namespace = _G.diagnostic_ns })
return { next.lnum, next.col }
end)
)
end)
it('will not cycle when wrap is off', function()
eq(
nil,
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 3, 1 })
local next = vim.diagnostic.get_next({ namespace = _G.diagnostic_ns, wrap = false })
return next
end)
)
end)
it('can cycle even from the last line', function()
eq(
{ 4, 4 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { vim.api.nvim_buf_line_count(0), 1 })
local prev = vim.diagnostic.get_prev({ namespace = _G.diagnostic_ns })
return { prev.lnum, prev.col }
end)
)
end)
it('works with diagnostics past the end of the line #16349', function()
eq(
{ 4, 0 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 3, 9001, 3, 9001),
_G.make_error('Diagnostic #2', 4, 0, 4, 0),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 1, 1 })
vim.diagnostic.jump({ count = 1 })
local next = vim.diagnostic.get_next({ namespace = _G.diagnostic_ns })
return { next.lnum, next.col }
end)
)
end)
it('works with diagnostics before the start of the line', function()
eq(
{ 4, 0 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 3, 9001, 3, 9001),
_G.make_error('Diagnostic #2', 4, -1, 4, -1),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 1, 1 })
vim.diagnostic.jump({ count = 1 })
local next = vim.diagnostic.get_next({ namespace = _G.diagnostic_ns })
return { next.lnum, next.col }
end)
)
end)
it('jumps to diagnostic with highest severity', function()
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_info('Info', 1, 0, 1, 1),
_G.make_error('Error', 2, 0, 2, 1),
_G.make_warning('Warning', 3, 0, 3, 1),
_G.make_error('Error', 4, 0, 4, 1),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 1, 0 })
end)
eq(
{ 3, 0 },
exec_lua(function()
vim.diagnostic.jump({ count = 1, _highest = true })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 5, 0 },
exec_lua(function()
vim.diagnostic.jump({ count = 1, _highest = true })
return vim.api.nvim_win_get_cursor(0)
end)
)
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_info('Info', 1, 0, 1, 1),
_G.make_hint('Hint', 2, 0, 2, 1),
_G.make_warning('Warning', 3, 0, 3, 1),
_G.make_hint('Hint', 4, 0, 4, 1),
_G.make_warning('Warning', 5, 0, 5, 1),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 1, 0 })
end)
eq(
{ 4, 0 },
exec_lua(function()
vim.diagnostic.jump({ count = 1, _highest = true })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 6, 0 },
exec_lua(function()
vim.diagnostic.jump({ count = 1, _highest = true })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
it('jumps to next diagnostic if severity is non-nil', function()
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_info('Info', 1, 0, 1, 1),
_G.make_error('Error', 2, 0, 2, 1),
_G.make_warning('Warning', 3, 0, 3, 1),
_G.make_error('Error', 4, 0, 4, 1),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 1, 0 })
end)
eq(
{ 2, 0 },
exec_lua(function()
vim.diagnostic.jump({ count = 1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 3, 0 },
exec_lua(function()
vim.diagnostic.jump({ count = 1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 4, 0 },
exec_lua(function()
vim.diagnostic.jump({ count = 1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
end)
describe('get_prev()', function()
it('can find the previous diagnostic with only one namespace', function()
eq(
{ 1, 1 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 3, 1 })
local prev = vim.diagnostic.get_prev()
return { prev.lnum, prev.col }
end)
)
end)
it('can find the previous diagnostic with two errors', function()
eq(
{ 1, 1 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
_G.make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 3, 1 })
local prev = vim.diagnostic.get_prev({ namespace = _G.diagnostic_ns })
return { prev.lnum, prev.col }
end)
)
end)
it('can cycle when position is past error', function()
eq(
{ 4, 4 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 3, 1 })
local prev = vim.diagnostic.get_prev({ namespace = _G.diagnostic_ns })
return { prev.lnum, prev.col }
end)
)
end)
it('respects wrap parameter', function()
eq(
nil,
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, { 3, 1 })
local prev = vim.diagnostic.get_prev({ namespace = _G.diagnostic_ns, wrap = false })
return prev
end)
)
end)
it('works on blank line #28397', function()
eq(
{ 0, 2 },
exec_lua(function()
local test_bufnr = vim.api.nvim_create_buf(true, false)
vim.api.nvim_buf_set_lines(test_bufnr, 0, -1, false, {
'first line',
'',
'',
'end line',
})
vim.diagnostic.set(_G.diagnostic_ns, test_bufnr, {
_G.make_info('Diagnostic #1', 0, 2, 0, 2),
_G.make_info('Diagnostic #2', 2, 0, 2, 0),
_G.make_info('Diagnostic #3', 2, 0, 2, 0),
})
vim.api.nvim_win_set_buf(0, test_bufnr)
vim.api.nvim_win_set_cursor(0, { 3, 0 })
return vim.diagnostic.get_prev_pos { namespace = _G.diagnostic_ns }
end)
)
end)
end)
describe('jump()', function()
before_each(function()
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 0, 0, 0, 2),
_G.make_error('Diagnostic #2', 1, 1, 1, 4),
_G.make_warning('Diagnostic #3', 2, -1, 2, -1),
_G.make_info('Diagnostic #4', 3, 0, 3, 3),
})
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
end)
end)
it('can move forward', function()
eq(
{ 2, 1 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.jump({ count = 1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 4, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.jump({ count = 3 })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 4, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.jump({ count = math.huge, wrap = false })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
it('can move backward', function()
eq(
{ 3, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 4, 0 })
vim.diagnostic.jump({ count = -1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 1, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 4, 0 })
vim.diagnostic.jump({ count = -3 })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 1, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 4, 0 })
vim.diagnostic.jump({ count = -math.huge, wrap = false })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
it('can filter by severity', function()
eq(
{ 3, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.jump({ count = 1, severity = vim.diagnostic.severity.WARN })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 3, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.jump({ count = 9999, severity = vim.diagnostic.severity.WARN })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
it('can wrap', function()
eq(
{ 1, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 4, 0 })
vim.diagnostic.jump({ count = 1, wrap = true })
return vim.api.nvim_win_get_cursor(0)
end)
)
eq(
{ 4, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.jump({ count = -1, wrap = true })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
it('supports on_jump() handler', function()
exec_lua(function()
_G.jumped = false
vim.diagnostic.jump({
count = 1,
on_jump = function()
_G.jumped = true
end,
})
end)
retry(nil, nil, function()
eq(true, exec_lua('return _G.jumped'))
end)
end)
describe('after inserting text before diagnostic position', function()
before_each(function()
api.nvim_buf_set_text(0, 3, 0, 3, 0, { 'new line', 'new ' })
end)
it('finds next diagnostic at a logical location', function()
eq(
{ 5, 4 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 3, 1 })
vim.diagnostic.jump({ count = 1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
it('finds previous diagnostic at a logical location', function()
eq(
{ 5, 4 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 6, 4 })
vim.diagnostic.jump({ count = -1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
end)
describe('if diagnostic is set after last character in line', function()
before_each(function()
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 2, 3, 3, 4),
})
end)
end)
it('finds next diagnostic at the end of the line', function()
eq(
{ 3, 2 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 3, 0 })
vim.diagnostic.jump({ count = 1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
it('finds previous diagnostic at the end of the line', function()
eq(
{ 3, 2 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 4, 2 })
vim.diagnostic.jump({ count = -1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
end)
describe('after entire text range with a diagnostic was deleted', function()
before_each(function()
api.nvim_buf_set_text(0, 1, 1, 1, 4, {})
end)
it('does not find next diagnostic inside the deleted range', function()
eq(
{ 3, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.jump({ count = 1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
it('does not find previous diagnostic inside the deleted range', function()
eq(
{ 1, 0 },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 3, 0 })
vim.diagnostic.jump({ count = -1 })
return vim.api.nvim_win_get_cursor(0)
end)
)
end)
end)
end)
describe('get()', function()
it('returns an empty table when no diagnostics are present', function()
eq(
{},
exec_lua [[return vim.diagnostic.get( _G.diagnostic_bufnr, {namespace=diagnostic_ns})]]
)
end)
it('returns all diagnostics when no severity is supplied', function()
eq(
2,
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 1, 1, 2, 3),
})
return #vim.diagnostic.get(_G.diagnostic_bufnr)
end)
)
end)
it('returns only requested diagnostics when severity range is supplied', function()
eq(
{ 2, 3, 2 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 1, 1, 2, 3),
_G.make_info('Ignored information', 1, 1, 2, 3),
_G.make_hint("Here's a hint", 1, 1, 2, 3),
})
return {
#vim.diagnostic.get(
_G.diagnostic_bufnr,
{ severity = { min = vim.diagnostic.severity.WARN } }
),
#vim.diagnostic.get(
_G.diagnostic_bufnr,
{ severity = { max = vim.diagnostic.severity.WARN } }
),
#vim.diagnostic.get(_G.diagnostic_bufnr, {
severity = {
min = vim.diagnostic.severity.INFO,
max = vim.diagnostic.severity.WARN,
},
}),
}
end)
)
end)
it('returns only requested diagnostics when severities are supplied', function()
eq(
{ 1, 1, 2 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 1, 1, 2, 3),
_G.make_info('Ignored information', 1, 1, 2, 3),
_G.make_hint("Here's a hint", 1, 1, 2, 3),
})
return {
#vim.diagnostic.get(
_G.diagnostic_bufnr,
{ severity = { vim.diagnostic.severity.WARN } }
),
#vim.diagnostic.get(
_G.diagnostic_bufnr,
{ severity = { vim.diagnostic.severity.ERROR } }
),
#vim.diagnostic.get(_G.diagnostic_bufnr, {
severity = {
vim.diagnostic.severity.INFO,
vim.diagnostic.severity.WARN,
},
}),
}
end)
)
end)
it('allows filtering by line', function()
eq(
2,
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 1, 1, 2, 3),
_G.make_info('Ignored information', 1, 1, 2, 3),
_G.make_error('Error On Other Line', 3, 1, 3, 5),
})
return #vim.diagnostic.get(_G.diagnostic_bufnr, { lnum = 2 })
end)
)
end)
it('allows filtering by enablement', function()
eq(
{ 3, 1, 2 },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 5),
})
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 2', 1, 1, 1, 5),
_G.make_error('Error 3', 3, 1, 3, 5),
})
vim.diagnostic.enable(false, { ns_id = _G.other_ns })
return {
#vim.diagnostic.get(_G.diagnostic_bufnr),
#vim.diagnostic.get(_G.diagnostic_bufnr, { enabled = true }),
#vim.diagnostic.get(_G.diagnostic_bufnr, { enabled = false }),
}
end)
)
end)
end)
describe('count', function()
it('returns actually present severity counts', function()
eq(
exec_lua(function()
return {
[vim.diagnostic.severity.ERROR] = 4,
[vim.diagnostic.severity.WARN] = 3,
[vim.diagnostic.severity.INFO] = 2,
[vim.diagnostic.severity.HINT] = 1,
}
end),
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 2),
_G.make_error('Error 2', 1, 3, 1, 4),
_G.make_error('Error 3', 1, 5, 1, 6),
_G.make_error('Error 4', 1, 7, 1, 8),
_G.make_warning('Warning 1', 2, 1, 2, 2),
_G.make_warning('Warning 2', 2, 3, 2, 4),
_G.make_warning('Warning 3', 2, 5, 2, 6),
_G.make_info('Info 1', 3, 1, 3, 2),
_G.make_info('Info 2', 3, 3, 3, 4),
_G.make_hint('Hint 1', 4, 1, 4, 2),
})
return vim.diagnostic.count(_G.diagnostic_bufnr)
end)
)
eq(
exec_lua(function()
return {
[vim.diagnostic.severity.ERROR] = 2,
[vim.diagnostic.severity.INFO] = 1,
}
end),
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 2),
_G.make_error('Error 2', 1, 3, 1, 4),
_G.make_info('Info 1', 3, 1, 3, 2),
})
return vim.diagnostic.count(_G.diagnostic_bufnr)
end)
)
end)
it('returns only requested diagnostics count when severity range is supplied', function()
eq(
exec_lua(function()
return {
{ [vim.diagnostic.severity.ERROR] = 1, [vim.diagnostic.severity.WARN] = 1 },
{
[vim.diagnostic.severity.WARN] = 1,
[vim.diagnostic.severity.INFO] = 1,
[vim.diagnostic.severity.HINT] = 1,
},
{ [vim.diagnostic.severity.WARN] = 1, [vim.diagnostic.severity.INFO] = 1 },
}
end),
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 1, 1, 2, 3),
_G.make_info('Ignored information', 1, 1, 2, 3),
_G.make_hint("Here's a hint", 1, 1, 2, 3),
})
return {
vim.diagnostic.count(
_G.diagnostic_bufnr,
{ severity = { min = vim.diagnostic.severity.WARN } }
),
vim.diagnostic.count(
_G.diagnostic_bufnr,
{ severity = { max = vim.diagnostic.severity.WARN } }
),
vim.diagnostic.count(_G.diagnostic_bufnr, {
severity = {
min = vim.diagnostic.severity.INFO,
max = vim.diagnostic.severity.WARN,
},
}),
}
end)
)
end)
it('returns only requested diagnostics when severities are supplied', function()
eq(
exec_lua(function()
return {
{ [vim.diagnostic.severity.WARN] = 1 },
{ [vim.diagnostic.severity.ERROR] = 1 },
{ [vim.diagnostic.severity.WARN] = 1, [vim.diagnostic.severity.INFO] = 1 },
}
end),
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 1, 1, 2, 3),
_G.make_info('Ignored information', 1, 1, 2, 3),
_G.make_hint("Here's a hint", 1, 1, 2, 3),
})
return {
vim.diagnostic.count(
_G.diagnostic_bufnr,
{ severity = { vim.diagnostic.severity.WARN } }
),
vim.diagnostic.count(
_G.diagnostic_bufnr,
{ severity = { vim.diagnostic.severity.ERROR } }
),
vim.diagnostic.count(_G.diagnostic_bufnr, {
severity = {
vim.diagnostic.severity.INFO,
vim.diagnostic.severity.WARN,
},
}),
}
end)
)
end)
it('allows filtering by line', function()
eq(
exec_lua(function()
return {
[vim.diagnostic.severity.WARN] = 1,
[vim.diagnostic.severity.INFO] = 1,
}
end),
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 1, 1, 1, 5),
_G.make_warning('Warning on Server 1', 1, 1, 2, 3),
_G.make_info('Ignored information', 1, 1, 2, 3),
_G.make_error('Error On Other Line', 3, 1, 3, 5),
})
return vim.diagnostic.count(_G.diagnostic_bufnr, { lnum = 2 })
end)
)
end)
end)
describe('config()', function()
it('works with global, namespace, and ephemeral options', function()
eq(
1,
exec_lua(function()
vim.diagnostic.config({
virtual_text = false,
})
vim.diagnostic.config({
virtual_text = true,
underline = false,
}, _G.diagnostic_ns)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Some Error', 4, 4, 4, 4),
})
return _G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
end)
)
eq(
1,
exec_lua(function()
vim.diagnostic.config({
virtual_text = false,
})
vim.diagnostic.config({
virtual_text = false,
underline = false,
}, _G.diagnostic_ns)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Some Error', 4, 4, 4, 4),
}, { virtual_text = true })
return _G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
end)
)
eq(
0,
exec_lua(function()
vim.diagnostic.config({
virtual_text = false,
})
vim.diagnostic.config({
virtual_text = { severity = vim.diagnostic.severity.ERROR },
underline = false,
}, _G.diagnostic_ns)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_warning('Some Warning', 4, 4, 4, 4),
}, { virtual_text = true })
return _G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
end)
)
eq(
1,
exec_lua(function()
vim.diagnostic.config({
virtual_text = false,
})
vim.diagnostic.config({
virtual_text = { severity = vim.diagnostic.severity.ERROR },
underline = false,
}, _G.diagnostic_ns)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_warning('Some Warning', 4, 4, 4, 4),
}, {
virtual_text = {}, -- An empty table uses default values
})
return _G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
end)
)
end)
it('can use functions for config values', function()
exec_lua(function()
vim.diagnostic.config({
virtual_text = function()
return true
end,
}, _G.diagnostic_ns)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
end)
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(2, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
-- Now, don't enable virtual text.
-- We should have one less extmark displayed.
exec_lua(function()
vim.diagnostic.config({
virtual_text = function()
return false
end,
}, _G.diagnostic_ns)
end)
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(1, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
end)
it('allows filtering by severity', function()
local get_extmark_count_with_severity = function(min_severity)
return exec_lua(function()
vim.diagnostic.config({
underline = false,
virtual_text = {
severity = { min = min_severity },
},
})
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_warning('Delayed Diagnostic', 4, 4, 4, 4),
})
return _G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
end)
end
-- No messages with Error or higher
eq(0, get_extmark_count_with_severity('ERROR'))
-- But now we don't filter it
eq(1, get_extmark_count_with_severity('WARN'))
eq(1, get_extmark_count_with_severity('HINT'))
end)
it('allows sorting by severity', function()
exec_lua(function()
vim.diagnostic.config({
underline = false,
signs = true,
virtual_text = true,
})
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_warning('Warning', 4, 4, 4, 4),
_G.make_error('Error', 4, 4, 4, 4),
_G.make_info('Info', 4, 4, 4, 4),
})
function _G.get_highest_underline_hl(severity_sort)
vim.diagnostic.config({
underline = true,
severity_sort = severity_sort,
})
local extmarks = _G.get_underline_extmarks(_G.diagnostic_ns)
table.sort(extmarks, function(a, b)
return a[4].priority > b[4].priority
end)
return extmarks[1][4].hl_group
end
function _G.get_virt_text_and_signs(severity_sort)
vim.diagnostic.config({
severity_sort = severity_sort,
})
local virt_text = _G.get_virt_text_extmarks(_G.diagnostic_ns)[1][4].virt_text
local virt_texts = {}
for i = 2, #virt_text - 1 do
table.insert(virt_texts, (string.gsub(virt_text[i][2], 'DiagnosticVirtualText', '')))
end
local ns = vim.diagnostic.get_namespace(_G.diagnostic_ns)
local sign_ns = ns.user_data.sign_ns
local signs = {}
local all_signs = vim.api.nvim_buf_get_extmarks(
_G.diagnostic_bufnr,
sign_ns,
0,
-1,
{ type = 'sign', details = true }
)
table.sort(all_signs, function(a, b)
return a[1] > b[1]
end)
for _, v in ipairs(all_signs) do
local s = v[4].sign_hl_group:gsub('DiagnosticSign', '')
if not vim.tbl_contains(signs, s) then
signs[#signs + 1] = s
end
end
return { virt_texts, signs }
end
end)
local result = exec_lua [[return _G.get_virt_text_and_signs(false)]]
-- Virt texts are defined lowest priority to highest, signs from
-- highest to lowest
eq({ 'Warn', 'Error', 'Info' }, result[1])
eq({ 'Info', 'Error', 'Warn' }, result[2])
result = exec_lua [[return _G.get_virt_text_and_signs(true)]]
eq({ 'Info', 'Warn', 'Error' }, result[1])
eq({ 'Error', 'Warn', 'Info' }, result[2])
result = exec_lua [[return _G.get_virt_text_and_signs({ reverse = true })]]
eq({ 'Error', 'Warn', 'Info' }, result[1])
eq({ 'Info', 'Warn', 'Error' }, result[2])
local underline_hl = exec_lua [[return _G.get_highest_underline_hl(true)]]
eq('DiagnosticUnderlineError', underline_hl)
underline_hl = exec_lua [[return _G.get_highest_underline_hl({ reverse = true })]]
eq('DiagnosticUnderlineInfo', underline_hl)
end)
it('can show diagnostic sources in virtual text', function()
local result = exec_lua(function()
local diagnostics = {
_G.make_error('Some error', 0, 0, 0, 0, 'source x'),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics, {
underline = false,
virtual_text = {
prefix = '',
source = 'always',
},
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
local virt_text = extmarks[1][4].virt_text[3][1]
return virt_text
end)
eq(' source x: Some error', result)
result = exec_lua(function()
vim.diagnostic.config({
underline = false,
virtual_text = {
prefix = '',
source = 'if_many',
},
}, _G.diagnostic_ns)
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
local virt_text = extmarks[1][4].virt_text[3][1]
return virt_text
end)
eq(' Some error', result)
result = exec_lua(function()
local diagnostics = {
_G.make_error('Some error', 0, 0, 0, 0, 'source x'),
_G.make_error('Another error', 1, 1, 1, 1, 'source y'),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics, {
underline = false,
virtual_text = {
prefix = '',
source = 'if_many',
},
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
local virt_text = { extmarks[1][4].virt_text[3][1], extmarks[2][4].virt_text[3][1] }
return virt_text
end)
eq(' source x: Some error', result[1])
eq(' source y: Another error', result[2])
end)
it('supports a format function for diagnostic messages', function()
local result = exec_lua(function()
vim.diagnostic.config({
underline = false,
virtual_text = {
prefix = '',
format = function(diagnostic)
if diagnostic.severity == vim.diagnostic.severity.ERROR then
return string.format('🔥 %s', diagnostic.message)
end
return string.format('👀 %s', diagnostic.message)
end,
},
})
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_warning('Warning', 0, 0, 0, 0),
_G.make_error('Error', 1, 0, 1, 0),
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
return { extmarks[1][4].virt_text, extmarks[2][4].virt_text }
end)
eq(' 👀 Warning', result[1][3][1])
eq(' 🔥 Error', result[2][3][1])
end)
it('includes source for formatted diagnostics', function()
local result = exec_lua(function()
vim.diagnostic.config({
underline = false,
virtual_text = {
prefix = '',
source = 'always',
format = function(diagnostic)
if diagnostic.severity == vim.diagnostic.severity.ERROR then
return string.format('🔥 %s', diagnostic.message)
end
return string.format('👀 %s', diagnostic.message)
end,
},
})
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_warning('Warning', 0, 0, 0, 0, 'some_linter'),
_G.make_error('Error', 1, 0, 1, 0, 'another_linter'),
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
return { extmarks[1][4].virt_text, extmarks[2][4].virt_text }
end)
eq(' some_linter: 👀 Warning', result[1][3][1])
eq(' another_linter: 🔥 Error', result[2][3][1])
end)
it('can add a prefix to virtual text', function()
eq(
'E Some error',
exec_lua(function()
local diagnostics = {
_G.make_error('Some error', 0, 0, 0, 0),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics, {
underline = false,
virtual_text = {
prefix = 'E',
suffix = '',
},
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
local prefix = extmarks[1][4].virt_text[2][1]
local message = extmarks[1][4].virt_text[3][1]
return prefix .. message
end)
)
eq(
'[(1/1) err-code] Some error',
exec_lua(function()
local diagnostics = {
_G.make_error('Some error', 0, 0, 0, 0, nil, 'err-code'),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics, {
underline = false,
virtual_text = {
prefix = function(diag, i, total)
return string.format('[(%d/%d) %s]', i, total, diag.code)
end,
suffix = '',
},
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
local prefix = extmarks[1][4].virt_text[2][1]
local message = extmarks[1][4].virt_text[3][1]
return prefix .. message
end)
)
end)
it('can add a suffix to virtual text', function()
eq(
' Some error ✘',
exec_lua(function()
local diagnostics = {
_G.make_error('Some error', 0, 0, 0, 0),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics, {
underline = false,
virtual_text = {
prefix = '',
suffix = '',
},
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
local virt_text = extmarks[1][4].virt_text[3][1]
return virt_text
end)
)
eq(
' Some error [err-code]',
exec_lua(function()
local diagnostics = {
_G.make_error('Some error', 0, 0, 0, 0, nil, 'err-code'),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics, {
underline = false,
virtual_text = {
prefix = '',
suffix = function(diag)
return string.format(' [%s]', diag.code)
end,
},
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
local virt_text = extmarks[1][4].virt_text[3][1]
return virt_text
end)
)
end)
it('can filter diagnostics by returning nil when formatting', function()
local result = exec_lua(function()
vim.diagnostic.config {
virtual_text = {
format = function(diagnostic)
if diagnostic.code == 'foo_err' then
return nil
end
return diagnostic.message
end,
},
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('An error here!', 0, 0, 0, 0, 'foo_server', 'foo_err'),
_G.make_error('An error there!', 1, 1, 1, 1, 'bar_server', 'bar_err'),
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
return extmarks
end)
eq(1, #result)
eq(' An error there!', result[1][4].virt_text[3][1])
end)
it('can only show virtual_text for the current line', function()
local result = exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.config({ virtual_text = { current_line = true } })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error here!', 0, 0, 0, 0, 'foo_server'),
_G.make_error('Another error there!', 1, 0, 1, 0, 'foo_server'),
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
return extmarks
end)
eq(1, #result)
eq(' Error here!', result[1][4].virt_text[3][1])
end)
it('can hide virtual_text for the current line', function()
local result = exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.config({ virtual_text = { current_line = false } })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error here!', 0, 0, 0, 0, 'foo_server'),
_G.make_error('Another error there!', 1, 0, 1, 0, 'foo_server'),
})
local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
return extmarks
end)
eq(1, #result)
eq(' Another error there!', result[1][4].virt_text[3][1])
end)
it('only renders virtual_line diagnostics within buffer length', function()
local result = exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.config({ virtual_text = { current_line = false } })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Hidden Error here!', 0, 0, 0, 0, 'foo_server'),
_G.make_error('First Error here!', 1, 0, 1, 0, 'foo_server'),
_G.make_error('Second Error here!', 2, 0, 2, 0, 'foo_server'),
_G.make_error('First Ignored Error here!', 3, 0, 3, 0, 'foo_server'),
_G.make_error('Second Ignored Error here!', 6, 0, 6, 0, 'foo_server'),
_G.make_error('Third Ignored Error here!', 8, 0, 8, 0, 'foo_server'),
})
vim.api.nvim_buf_set_lines(_G.diagnostic_bufnr, 2, 5, false, {})
vim.api.nvim_exec_autocmds('CursorMoved', { buffer = _G.diagnostic_bufnr })
return _G.get_virt_text_extmarks(_G.diagnostic_ns)
end)
eq(2, #result)
eq(' First Error here!', result[1][4].virt_text[3][1])
eq(' Second Error here!', result[2][4].virt_text[3][1])
end)
end)
describe('handlers.virtual_lines', function()
it('includes diagnostic code and message', function()
local result = exec_lua(function()
vim.diagnostic.config({ virtual_lines = true })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Missed symbol `,`', 0, 0, 0, 0, 'lua_ls', 'miss-symbol'),
})
local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
return extmarks[1][4].virt_lines
end)
eq('miss-symbol: Missed symbol `,`', result[1][3][1])
end)
it('adds space to the left of the diagnostic', function()
local error_offset = 5
local result = exec_lua(function()
vim.diagnostic.config({ virtual_lines = true })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error here!', 0, error_offset, 0, error_offset, 'foo_server'),
})
local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
return extmarks[1][4].virt_lines
end)
eq(error_offset, result[1][1][1]:len())
end)
it('highlights diagnostics in multiple lines by default', function()
local result = exec_lua(function()
vim.diagnostic.config({ virtual_lines = true })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error here!', 0, 0, 0, 0, 'foo_server'),
_G.make_error('Another error there!', 1, 0, 1, 0, 'foo_server'),
})
local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
return extmarks
end)
eq(2, #result)
eq('Error here!', result[1][4].virt_lines[1][3][1])
eq('Another error there!', result[2][4].virt_lines[1][3][1])
end)
it('highlights multiple diagnostics in a single line by default', function()
local result = exec_lua(function()
vim.api.nvim_buf_set_lines(
_G.diagnostic_bufnr,
0,
-1,
false,
{ 'def foo(x: int, /, y: str, *, z: float) -> None: ...' }
)
vim.diagnostic.config({ virtual_lines = true })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error here!', 0, 8, 0, 9, 'foo_server'),
_G.make_error('Another error there!', 0, 19, 0, 20, 'foo_server'),
_G.make_error('And another one!', 0, 30, 0, 31, 'foo_server'),
})
local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
return extmarks
end)
--[[
|def foo(x: int, /, y: str, *, z: float) -> None: ...
| │ │ └──── And another one!
| │ └──── Another error there!
| └──── Error here!
| ^ col=8
| ^ col=19
| ^ col=30
11 cols between each diagnostic after the first one (10 spaces + |)
]]
eq(1, #result)
local virt_lines = result[1][4].virt_lines
eq(8, virt_lines[1][1][1]:len()) -- first space
eq(10, virt_lines[1][3][1]:len()) -- second space
eq(10, virt_lines[1][5][1]:len()) -- third space
eq('And another one!', virt_lines[1][7][1])
eq(8, virt_lines[2][1][1]:len()) -- first space
eq(10, virt_lines[2][3][1]:len()) -- second space
eq('Another error there!', virt_lines[2][5][1])
eq(8, virt_lines[3][1][1]:len()) -- first space
eq('Error here!', virt_lines[3][3][1])
end)
it('highlights overlapping diagnostics on a single line', function()
local result = exec_lua(function()
vim.diagnostic.config({ virtual_lines = true })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error here!', 0, 10, 0, 11, 'foo_server'),
_G.make_error('Another error here!', 0, 10, 0, 11, 'foo_server'),
})
local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
return extmarks
end)
--[[
|1234567890x
| ├──── Another error here!
| └──── Error here!
]]
eq(1, #result)
local virt_lines = result[1][4].virt_lines
eq(10, virt_lines[1][1][1]:len()) -- first space
eq('├──── ', virt_lines[1][2][1])
eq('Another error here!', virt_lines[1][3][1])
eq(10, virt_lines[2][1][1]:len()) -- second space
eq('└──── ', virt_lines[2][2][1])
eq('Error here!', virt_lines[2][3][1])
end)
it('handles multi-line diagnostic message', function()
local result = exec_lua(function()
vim.diagnostic.config({ virtual_lines = true })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error here!\ngot another line', 0, 10, 0, 11, 'foo_server'),
})
local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
return extmarks
end)
--[[
|1234567890x
| └──── Error here!
| got another line
]]
eq(1, #result)
local virt_lines = result[1][4].virt_lines
eq(10, virt_lines[1][1][1]:len()) -- first space
eq('└──── ', virt_lines[1][2][1])
eq('Error here!', virt_lines[1][3][1])
eq(10, virt_lines[2][1][1]:len()) -- second line space
eq(6, virt_lines[2][2][1]:len()) -- extra padding
eq('got another line', virt_lines[2][3][1])
end)
it('can highlight diagnostics only in the current line', function()
local result = exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.diagnostic.config({ virtual_lines = { current_line = true } })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error here!', 0, 0, 0, 0, 'foo_server'),
_G.make_error('Another error there!', 1, 0, 1, 0, 'foo_server'),
})
local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
return extmarks
end)
eq(1, #result)
eq('Error here!', result[1][4].virt_lines[1][3][1])
end)
it('supports a format function for diagnostic messages', function()
local result = exec_lua(function()
vim.diagnostic.config({
virtual_lines = {
format = function()
return 'Error here!'
end,
},
})
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Invalid syntax', 0, 0, 0, 0),
})
local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
return extmarks[1][4].virt_lines
end)
eq('Error here!', result[1][3][1])
end)
end)
describe('set()', function()
it('validation', function()
matches(
'expected a list of diagnostics',
pcall_err(exec_lua, [[vim.diagnostic.set(1, 0, {lnum = 1, col = 2})]])
)
end)
it('can perform updates after insert_leave', function()
exec_lua(function()
vim.diagnostic.config({ virtual_text = true })
vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
end)
api.nvim_input('o')
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
-- Save the diagnostics
exec_lua(function()
vim.diagnostic.config({
update_in_insert = false,
})
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
end)
-- No diagnostics displayed yet.
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(0, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
api.nvim_input('<esc>')
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(2, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
end)
it('does not perform updates when not needed', function()
exec_lua [[vim.api.nvim_set_current_buf( _G.diagnostic_bufnr)]]
api.nvim_input('o')
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
-- Save the diagnostics
exec_lua(function()
vim.diagnostic.config({
update_in_insert = false,
virtual_text = true,
})
_G.DisplayCount = 0
local set_virtual_text = vim.diagnostic.handlers.virtual_text.show
vim.diagnostic.handlers.virtual_text.show = function(...)
_G.DisplayCount = _G.DisplayCount + 1
return set_virtual_text(...)
end
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
end)
-- No diagnostics displayed yet.
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(0, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
eq(0, exec_lua [[return _G.DisplayCount]])
api.nvim_input('<esc>')
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(2, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
eq(1, exec_lua [[return _G.DisplayCount]])
-- Go in and out of insert mode one more time.
api.nvim_input('o')
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
api.nvim_input('<esc>')
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
-- Should not have set the virtual text again.
eq(1, exec_lua [[return _G.DisplayCount]])
end)
it('never sets virtual text, in combination with insert leave', function()
exec_lua [[vim.api.nvim_set_current_buf( _G.diagnostic_bufnr)]]
api.nvim_input('o')
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
-- Save the diagnostics
exec_lua(function()
vim.diagnostic.config({
update_in_insert = false,
virtual_text = false,
})
_G.DisplayCount = 0
local set_virtual_text = vim.diagnostic.handlers.virtual_text.show
vim.diagnostic.handlers.virtual_text.show = function(...)
_G.DisplayCount = _G.DisplayCount + 1
return set_virtual_text(...)
end
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
end)
-- No diagnostics displayed yet.
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(0, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
eq(0, exec_lua [[return _G.DisplayCount]])
api.nvim_input('<esc>')
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(1, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
eq(0, exec_lua [[return _G.DisplayCount]])
-- Go in and out of insert mode one more time.
api.nvim_input('o')
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
api.nvim_input('<esc>')
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
-- Should not have set the virtual text still.
eq(0, exec_lua [[return _G.DisplayCount]])
end)
it('can perform updates while in insert mode, if desired', function()
exec_lua(function()
vim.diagnostic.config({ virtual_text = true })
vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
end)
api.nvim_input('o')
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
-- Save the diagnostics
exec_lua(function()
vim.diagnostic.config({
update_in_insert = true,
})
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
end)
-- Diagnostics are displayed, because the user wanted them that way!
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(2, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
api.nvim_input('<esc>')
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
eq(
1,
exec_lua [[return _G.count_diagnostics( _G.diagnostic_bufnr, vim.diagnostic.severity.ERROR, _G.diagnostic_ns)]]
)
eq(2, exec_lua [[return _G.count_extmarks( _G.diagnostic_bufnr, _G.diagnostic_ns)]])
end)
it('can set diagnostics without displaying them', function()
exec_lua(function()
vim.diagnostic.config({ virtual_text = true })
end)
eq(
0,
exec_lua(function()
vim.diagnostic.enable(false, { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic From Server 1:1', 1, 1, 1, 1),
})
return _G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
end)
)
eq(
2,
exec_lua(function()
vim.diagnostic.enable(true, { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns })
return _G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
end)
)
end)
it('can set display options', function()
eq(
0,
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic From Server 1:1', 1, 1, 1, 1),
}, { virtual_text = false, underline = false })
return _G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
end)
)
eq(
1,
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic From Server 1:1', 1, 1, 1, 1),
}, { virtual_text = true, underline = false })
return _G.count_extmarks(_G.diagnostic_bufnr, _G.diagnostic_ns)
end)
)
end)
it('sets and clears signs #26193 #26555', function()
do
local result = exec_lua(function()
vim.diagnostic.config({
signs = true,
})
local diagnostics = {
_G.make_error('Error', 1, 1, 1, 2),
_G.make_warning('Warning', 3, 3, 3, 3),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local ns = vim.diagnostic.get_namespace(_G.diagnostic_ns)
local sign_ns = ns.user_data.sign_ns
local signs = vim.api.nvim_buf_get_extmarks(
_G.diagnostic_bufnr,
sign_ns,
0,
-1,
{ type = 'sign', details = true }
)
local result = {}
for _, s in ipairs(signs) do
result[#result + 1] = { lnum = s[2] + 1, name = s[4].sign_hl_group }
end
return result
end)
eq({ 2, 'DiagnosticSignError' }, { result[1].lnum, result[1].name })
eq({ 4, 'DiagnosticSignWarn' }, { result[2].lnum, result[2].name })
end
do
local result = exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {})
local ns = vim.diagnostic.get_namespace(_G.diagnostic_ns)
local sign_ns = ns.user_data.sign_ns
return vim.api.nvim_buf_get_extmarks(
_G.diagnostic_bufnr,
sign_ns,
0,
-1,
{ type = 'sign', details = true }
)
end)
eq({}, result)
end
end)
it('always passes a table to DiagnosticChanged autocommand', function()
local result = exec_lua(function()
local changed_diags --- @type vim.Diagnostic[]?
vim.api.nvim_create_autocmd('DiagnosticChanged', {
buffer = _G.diagnostic_bufnr,
callback = function(args)
--- @type vim.Diagnostic[]
changed_diags = args.data.diagnostics
end,
})
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {})
return changed_diags
end)
eq('table', type(result))
eq(0, #result)
end)
end)
describe('open_float()', function()
it('can display a header', function()
eq(
{ 'Diagnostics:', '1. Syntax error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
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)
)
eq(
{ "We're no strangers to love...", '1. Syntax error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr =
vim.diagnostic.open_float({ header = "We're no strangers to love..." })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
{ 'You know the rules', '1. Syntax error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr =
vim.diagnostic.open_float({ header = { 'You know the rules', 'Search' } })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
end)
it('can show diagnostics from the whole buffer', function()
eq(
{ '1. Syntax error', '2. Some warning' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
_G.make_warning('Some warning', 1, 1, 1, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, scope = 'buffer' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
end)
it('can show diagnostics from a single line', function()
-- Using cursor position
eq(
{ '1. Some warning' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
_G.make_warning('Some warning', 1, 1, 1, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, { 2, 1 })
local float_bufnr, winnr = vim.diagnostic.open_float({ 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)
)
-- With specified position
eq(
{ '1. Some warning' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
_G.make_warning('Some warning', 1, 1, 1, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, { 1, 1 })
local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, 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)
)
-- End position is exclusive
eq(
nil,
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 1, 1, 2, 0),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, { 1, 1 })
local _, winnr = vim.diagnostic.open_float(0, { header = false, pos = { 2, 0 } })
return winnr
end)
)
-- Works when width == 0
eq(
{ '1. Syntax error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 2, 0, 2, 0),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, { 1, 1 })
local float_bufnr, winnr =
vim.diagnostic.open_float(0, { header = false, pos = { 2, 1 } })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
end)
it('can show diagnostics from a specific position', function()
-- Using cursor position
eq(
{ 'Syntax error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 1, 1, 1, 3),
_G.make_warning('Some warning', 1, 3, 1, 4),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, { 2, 2 })
local float_bufnr, winnr = vim.diagnostic.open_float({ 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
end)
)
-- With specified position
eq(
{ 'Some warning' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 1, 1, 1, 3),
_G.make_warning('Some warning', 1, 3, 1, 4),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, { 1, 1 })
local float_bufnr, winnr =
vim.diagnostic.open_float({ 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)
)
-- With column position past the end of the line. #16062
eq(
{ 'Syntax error' },
exec_lua(function()
local first_line_len = #vim.api.nvim_buf_get_lines(_G.diagnostic_bufnr, 0, 1, true)[1]
local diagnostics = {
_G.make_error('Syntax error', 0, first_line_len + 1, 1, 0),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, { 1, 1 })
local float_bufnr, winnr = vim.diagnostic.open_float({
header = false,
scope = 'cursor',
pos = { 0, first_line_len - 1 },
})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
-- End position is exclusive
eq(
nil,
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 1, 1, 1, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, { 1, 1 })
local _, winnr =
vim.diagnostic.open_float(0, { header = false, scope = 'cursor', pos = { 1, 3 } })
return winnr
end)
)
-- Works when width == 0
eq(
{ 'Syntax error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 2, 0, 2, 0),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.api.nvim_win_set_cursor(0, { 1, 1 })
local float_bufnr, winnr =
vim.diagnostic.open_float({ header = false, scope = 'cursor', pos = { 2, 0 } })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
end)
it(
'creates floating window and returns float bufnr and winnr if current line contains diagnostics',
function()
-- Two lines:
-- Diagnostic:
-- 1. <msg>
eq(
2,
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(_G.diagnostic_bufnr)
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return #lines
end)
)
end
)
it('only reports diagnostics from the current buffer when bufnr is omitted #15710', function()
eq(
2,
exec_lua(function()
local other_bufnr = vim.api.nvim_create_buf(true, false)
local buf_1_diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
local buf_2_diagnostics = {
_G.make_warning('Some warning', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, buf_1_diagnostics)
vim.diagnostic.set(_G.other_ns, other_bufnr, buf_2_diagnostics)
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)
)
end)
it('allows filtering by namespace', function()
eq(
{ 'Diagnostics:', '1. Syntax error' },
exec_lua(function()
local ns_1_diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
local ns_2_diagnostics = {
_G.make_warning('Some warning', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diagnostics)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diagnostics)
local float_bufnr, winnr =
vim.diagnostic.open_float(_G.diagnostic_bufnr, { namespace = _G.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)
)
end)
it('allows filtering by multiple namespaces', function()
eq(
{ 'Diagnostics:', '1. Syntax error', '2. Some warning' },
exec_lua(function()
local ns_1_diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
local ns_2_diagnostics = {
_G.make_warning('Some warning', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, ns_1_diagnostics)
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, ns_2_diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(
_G.diagnostic_bufnr,
{ namespace = { _G.diagnostic_ns, _G.other_ns } }
)
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
end)
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(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr =
vim.diagnostic.open_float(_G.diagnostic_bufnr, { 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)
)
end
)
it('clamps diagnostic line numbers within the valid range', function()
eq(
1,
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 6, 0, 6, 0),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr =
vim.diagnostic.open_float(_G.diagnostic_bufnr, { header = false, 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)
)
end)
it('can show diagnostic source', function()
exec_lua [[vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)]]
eq(
{ '1. Syntax error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3, 'source x'),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(_G.diagnostic_bufnr, {
header = false,
source = 'if_many',
})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
{ '1. source x: Syntax error' },
exec_lua(function()
local float_bufnr, winnr = vim.diagnostic.open_float(_G.diagnostic_bufnr, {
header = false,
source = 'always',
})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
{ '1. source x: Syntax error', '2. source y: Another error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3, 'source x'),
_G.make_error('Another error', 0, 1, 0, 3, 'source y'),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(_G.diagnostic_bufnr, {
header = false,
source = 'if_many',
})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
end)
it('respects severity_sort', function()
exec_lua [[vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)]]
eq(
{ '1. Syntax error', '2. Info', '3. Error', '4. Warning' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
_G.make_info('Info', 0, 3, 0, 4),
_G.make_error('Error', 0, 2, 0, 2),
_G.make_warning('Warning', 0, 0, 0, 1),
}
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
vim.diagnostic.config({ severity_sort = false })
local float_bufnr, winnr =
vim.diagnostic.open_float(_G.diagnostic_bufnr, { 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)
)
eq(
{ '1. Syntax error', '2. Error', '3. Warning', '4. Info' },
exec_lua(function()
vim.diagnostic.config({ severity_sort = true })
local float_bufnr, winnr =
vim.diagnostic.open_float(_G.diagnostic_bufnr, { 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)
)
eq(
{ '1. Info', '2. Warning', '3. Error', '4. Syntax error' },
exec_lua(function()
vim.diagnostic.config({ severity_sort = { reverse = true } })
local float_bufnr, winnr =
vim.diagnostic.open_float(_G.diagnostic_bufnr, { 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)
)
end)
it('can filter by severity', function()
local count_diagnostics_with_severity = function(min_severity, max_severity)
return exec_lua(function()
vim.diagnostic.config({
float = {
severity = { min = min_severity, max = max_severity },
},
})
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Syntax error', 0, 1, 0, 3),
_G.make_info('Info', 0, 3, 0, 4),
_G.make_error('Error', 0, 2, 0, 2),
_G.make_warning('Warning', 0, 0, 0, 1),
})
local float_bufnr, winnr =
vim.diagnostic.open_float(_G.diagnostic_bufnr, { header = false })
if not float_bufnr then
return 0
end
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return #lines
end)
end
eq(2, count_diagnostics_with_severity('ERROR'))
eq(3, count_diagnostics_with_severity('WARN'))
eq(1, count_diagnostics_with_severity('WARN', 'WARN'))
eq(4, count_diagnostics_with_severity('HINT'))
eq(0, count_diagnostics_with_severity('HINT', 'HINT'))
end)
it('can add a prefix to diagnostics', function()
-- Default is to add a number
eq(
{ '1. Syntax error', '2. Some warning' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
_G.make_warning('Some warning', 1, 1, 1, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, scope = 'buffer' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
{ 'Syntax error', 'Some warning' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
_G.make_warning('Some warning', 1, 1, 1, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr =
vim.diagnostic.open_float({ header = false, scope = 'buffer', prefix = '' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
{ '1. Syntax error', '2. Some warning' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
_G.make_warning('Some warning', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float({
header = false,
prefix = function(_, i, total)
-- Only show a number if there is more than one diagnostic
if total > 1 then
return string.format('%d. ', i)
end
return ''
end,
})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
{ 'Syntax error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float({
header = false,
prefix = function(_, i, total)
-- Only show a number if there is more than one diagnostic
if total > 1 then
return string.format('%d. ', i)
end
return ''
end,
})
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
'.../diagnostic.lua:0: prefix: expected string|table|function, got number',
pcall_err(exec_lua, [[ vim.diagnostic.open_float({ prefix = 42 }) ]])
)
end)
it('can add a suffix to diagnostics', function()
-- Default is to render the diagnostic error code
eq(
{ '1. Syntax error [code-x]', '2. Some warning [code-y]' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3, nil, 'code-x'),
_G.make_warning('Some warning', 1, 1, 1, 3, nil, 'code-y'),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, scope = 'buffer' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
{ '1. Syntax error', '2. Some warning' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3, nil, 'code-x'),
_G.make_warning('Some warning', 1, 1, 1, 3, nil, 'code-y'),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr =
vim.diagnostic.open_float({ header = false, scope = 'buffer', suffix = '' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
-- Suffix is rendered on the last line of a multiline diagnostic
eq(
{ '1. Syntax error', ' More context [code-x]' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error\nMore context', 0, 1, 0, 3, nil, 'code-x'),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, scope = 'buffer' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
'.../diagnostic.lua:0: suffix: expected string|table|function, got number',
pcall_err(exec_lua, [[ vim.diagnostic.open_float({ suffix = 42 }) ]])
)
end)
it('can add LSP related information to a diagnostic', function()
local related_info_uri = 'file:///fake/uri'
-- Populate the related info buffer.
exec_lua(function()
local fake_bufnr = vim.uri_to_bufnr(related_info_uri)
vim.fn.bufload(fake_bufnr)
vim.api.nvim_buf_set_lines(fake_bufnr, 0, 1, false, {
'O, the Pelican,',
'so smoothly doth he crest.',
'a wind god!',
})
vim.api.nvim_win_set_buf(0, fake_bufnr)
end)
-- Displays related info.
eq(
{
'1. Some warning',
' uri:1:0: Some extra info',
' uri:2:3: Some more extra info',
},
exec_lua(function()
---@type vim.Diagnostic
local diagnostic = _G.make_warning('Some warning', 1, 1, 1, 3)
diagnostic.user_data = {
-- Related information comes from LSP user_data
lsp = {
relatedInformation = {
{
message = 'Some extra info',
location = {
uri = related_info_uri,
range = {
start = {
line = 1,
character = 0,
},
['end'] = {
line = 1,
character = 1,
},
},
},
},
{
message = 'Some more extra info',
location = {
uri = related_info_uri,
range = {
start = {
line = 2,
character = 3,
},
['end'] = {
line = 4,
character = 5,
},
},
},
},
},
},
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, { diagnostic })
local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, scope = 'buffer' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
-- Put the cursor on a line with related info
vim.api.nvim_tabpage_set_win(0, winnr)
vim.api.nvim_win_set_cursor(0, { 2, 0 })
return lines
end)
)
-- Jumps to related info.
eq(
'so smoothly doth he crest.',
exec_lua(function()
vim.cmd.norm('gf')
vim.wait(20, function() end)
return vim.api.nvim_get_current_line()
end)
)
end)
it('works with the old signature', function()
eq(
{ '1. Syntax error' },
exec_lua(function()
local diagnostics = {
_G.make_error('Syntax error', 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
local float_bufnr, winnr = vim.diagnostic.open_float(0, { 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)
)
end)
it('works for multi-line diagnostics #21949', function()
-- create diagnostic
exec_lua(function()
local diagnostics = {
_G.make_error('Error in two lines lnum is 1 and end_lnum is 2', 1, 1, 2, 3),
}
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, diagnostics)
end)
-- open float failed non diagnostic lnum
eq(
nil,
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
local _, winnr = vim.diagnostic.open_float(0, { header = false })
return winnr
end)
)
eq(
nil,
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 1, 0 })
local _, winnr = vim.diagnostic.open_float(0, { header = false, scope = 'cursor' })
return winnr
end)
)
-- can open a float window on lnum 1
eq(
{ '1. Error in two lines lnum is 1 and end_lnum is 2' },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 2, 0 })
local float_bufnr, winnr = vim.diagnostic.open_float(0, { 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)
)
-- can open a cursor-scoped float window on lnum 1
eq(
{ 'Error in two lines lnum is 1 and end_lnum is 2' },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 2, 1 })
local float_bufnr, winnr =
vim.diagnostic.open_float(0, { 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
end)
)
-- can open a float window on end_lnum 2
eq(
{ '1. Error in two lines lnum is 1 and end_lnum is 2' },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 3, 0 })
local float_bufnr, winnr = vim.diagnostic.open_float(0, { 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)
)
-- can open a cursor-scoped float window on end_lnum 2
eq(
{ 'Error in two lines lnum is 1 and end_lnum is 2' },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 3, 2 })
local float_bufnr, winnr =
vim.diagnostic.open_float(0, { 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
end)
)
end)
it('shows diagnostics at their logical locations after text changes before', function()
exec_lua(function()
vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 4, 1, 7),
_G.make_error('Diagnostic #2', 3, 0, 3, 3),
})
end)
api.nvim_buf_set_text(0, 3, 0, 3, 0, { 'new line', 'new ' })
eq(
{ 'Diagnostic #1' },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 2, 4 })
local float_bufnr, winnr = vim.diagnostic.open_float({ header = '', scope = 'cursor' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
eq(
{ 'Diagnostic #2' },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 5, 4 })
local float_bufnr, winnr = vim.diagnostic.open_float({ header = '', scope = 'cursor' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
end)
it('shows diagnostics at their logical locations after text changes inside', function()
exec_lua(function()
vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 0, 1, 7),
})
end)
api.nvim_buf_set_text(0, 1, 4, 1, 4, { 'new ' })
eq(
{ 'Diagnostic #1' },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 2, 10 })
local float_bufnr, winnr = vim.diagnostic.open_float({ header = '', scope = 'cursor' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
end)
it(
'shows diagnostics at the end of the line if diagnostic is set after last character in line',
function()
exec_lua(function()
vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 2, 3, 3, 4),
})
end)
eq(
{ 'Diagnostic #1' },
exec_lua(function()
vim.api.nvim_win_set_cursor(0, { 3, 2 })
local float_bufnr, winnr = vim.diagnostic.open_float({ header = '', scope = 'cursor' })
local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
vim.api.nvim_win_close(winnr, true)
return lines
end)
)
end
)
end)
describe('setloclist()', function()
it('sets diagnostics in lnum order', function()
local loc_list = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Farther Diagnostic', 4, 4, 4, 4),
_G.make_error('Lower Diagnostic', 1, 1, 1, 1),
})
vim.diagnostic.setloclist()
return vim.fn.getloclist(0)
end)
assert(loc_list[1].lnum < loc_list[2].lnum)
end)
it('sets diagnostics in lnum order, regardless of namespace', function()
local loc_list = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Lower Diagnostic', 1, 1, 1, 1),
})
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, {
_G.make_warning('Farther Diagnostic', 4, 4, 4, 4),
})
vim.diagnostic.setloclist()
return vim.fn.getloclist(0)
end)
assert(loc_list[1].lnum < loc_list[2].lnum)
end)
it('sets diagnostics from the specified namespaces', function()
local loc_list = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error here!', 1, 1, 1, 1),
})
vim.diagnostic.set(_G.other_ns, _G.diagnostic_bufnr, {
_G.make_warning('Error there!', 2, 2, 2, 2),
})
vim.diagnostic.setloclist({ namespace = { _G.diagnostic_ns } })
return vim.fn.getloclist(0)
end)
eq(1, #loc_list)
eq('Error here!', loc_list[1].text)
end)
it('supports format function', function()
local loc_list = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error', 1, 1, 1, 1, 'foo_ls'),
_G.make_error('Another error', 2, 2, 2, 2, 'foo_ls'),
})
vim.diagnostic.setloclist({
format = function(diagnostic)
if diagnostic.lnum > 1 then
return nil
end
return string.format('%s: %s', diagnostic.source, diagnostic.message)
end,
})
return vim.fn.getloclist(0)
end)
eq(1, #loc_list)
eq('foo_ls: Error', loc_list[1].text)
end)
end)
describe('setqflist()', function()
it('updates existing diagnostics quickfix if one already exists', function()
local result = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.fn.setqflist({}, ' ', { title = 'Diagnostics' })
local diagnostics_qf_id = vim.fn.getqflist({ id = 0 }).id
vim.diagnostic.setqflist({ title = 'Diagnostics' })
local qf_id = vim.fn.getqflist({ id = 0, nr = '$' }).id
return { diagnostics_qf_id, qf_id }
end)
eq(result[1], result[2])
end)
it('navigates to existing diagnostics quickfix if one already exists and open=true', function()
local result = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.fn.setqflist({}, ' ', { title = 'Diagnostics' })
local diagnostics_qf_id = vim.fn.getqflist({ id = 0 }).id
vim.fn.setqflist({}, ' ', { title = 'Other' })
vim.diagnostic.setqflist({ title = 'Diagnostics', open = true })
local qf_id = vim.fn.getqflist({ id = 0 }).id
return { diagnostics_qf_id, qf_id }
end)
eq(result[1], result[2])
end)
it('sets new diagnostics quickfix as active when open=true', function()
local result = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.fn.setqflist({}, ' ', { title = 'Other' })
local other_qf_id = vim.fn.getqflist({ id = 0 }).id
vim.diagnostic.setqflist({ title = 'Diagnostics', open = true })
local qf_id = vim.fn.getqflist({ id = 0 }).id
return { other_qf_id, qf_id }
end)
neq(result[1], result[2])
end)
it('opens quickfix window when open=true', function()
local qf_winid = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error', 1, 1, 1, 1),
})
vim.diagnostic.setqflist({ open = true })
return vim.fn.getqflist({ winid = 0 }).winid
end)
neq(0, qf_winid)
end)
it('supports format function', function()
local qf_list = exec_lua(function()
vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error', 1, 1, 1, 1, 'foo_ls'),
_G.make_error('Another error', 2, 2, 2, 2, 'foo_ls'),
})
vim.diagnostic.setqflist({
format = function(diagnostic)
if diagnostic.lnum > 1 then
return nil
end
return string.format('%s: %s', diagnostic.source, diagnostic.message)
end,
})
return vim.fn.getqflist()
end)
eq(1, #qf_list)
eq('foo_ls: Error', qf_list[1].text)
end)
end)
describe('match()', function()
it('matches a string', function()
local msg = 'ERROR: george.txt:19:84:Two plus two equals five'
local diagnostic = {
severity = exec_lua [[return vim.diagnostic.severity.ERROR]],
lnum = 18,
col = 83,
end_lnum = 18,
end_col = 83,
message = 'Two plus two equals five',
}
eq(
diagnostic,
exec_lua(function()
return vim.diagnostic.match(
msg,
'^(%w+): [^:]+:(%d+):(%d+):(.+)$',
{ 'severity', 'lnum', 'col', 'message' }
)
end)
)
end)
it('returns nil if the pattern fails to match', function()
eq(
nil,
exec_lua(function()
local msg = 'The answer to life, the universe, and everything is'
return vim.diagnostic.match(msg, 'This definitely will not match', {})
end)
)
end)
it('respects default values', function()
local msg = 'anna.txt:1:Happy families are all alike'
local diagnostic = {
severity = exec_lua [[return vim.diagnostic.severity.INFO]],
lnum = 0,
col = 0,
end_lnum = 0,
end_col = 0,
message = 'Happy families are all alike',
}
eq(
diagnostic,
exec_lua(function()
return vim.diagnostic.match(
msg,
'^[^:]+:(%d+):(.+)$',
{ 'lnum', 'message' },
nil,
{ severity = vim.diagnostic.severity.INFO }
)
end)
)
end)
it('accepts a severity map', function()
local msg = '46:FATAL:Et tu, Brute?'
local diagnostic = {
severity = exec_lua [[return vim.diagnostic.severity.ERROR]],
lnum = 45,
col = 0,
end_lnum = 45,
end_col = 0,
message = 'Et tu, Brute?',
}
eq(
diagnostic,
exec_lua(function()
return vim.diagnostic.match(
msg,
'^(%d+):(%w+):(.+)$',
{ 'lnum', 'severity', 'message' },
{ FATAL = vim.diagnostic.severity.ERROR }
)
end)
)
end)
end)
describe('toqflist() and fromqflist()', function()
it('works', function()
local result = exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Error 1', 0, 1, 0, 1),
_G.make_error('Error 2', 1, 1, 1, 1),
_G.make_warning('Warning', 2, 2, 2, 2),
})
local diagnostics = vim.diagnostic.get(_G.diagnostic_bufnr)
vim.fn.setqflist(vim.diagnostic.toqflist(diagnostics))
local list = vim.fn.getqflist()
local new_diagnostics = vim.diagnostic.fromqflist(list)
-- Remove extra properties not present in the return value of fromlist()
for _, v in ipairs(diagnostics) do
v._extmark_id = nil
v.namespace = nil
end
return { diagnostics, new_diagnostics }
end)
eq(result[1], result[2])
end)
end)
describe('status()', function()
it('returns empty string if no diagnostics', function()
local result = exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {})
return vim.diagnostic.status()
end)
eq('', result)
end)
it('returns count for each diagnostic kind', function()
local result = exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, 0, {
_G.make_error('Error 1', 0, 1, 0, 1),
_G.make_warning('Warning 1', 2, 2, 2, 2),
_G.make_warning('Warning 2', 2, 2, 2, 2),
_G.make_info('Info 1', 3, 3, 3, 3),
_G.make_info('Info 2', 3, 3, 3, 3),
_G.make_info('Info 3', 3, 3, 3, 3),
_G.make_hint('Hint 1', 4, 4, 4, 4),
_G.make_hint('Hint 2', 4, 4, 4, 4),
_G.make_hint('Hint 3', 4, 4, 4, 4),
_G.make_hint('Hint 4', 4, 4, 4, 4),
})
return vim.diagnostic.status()
end)
eq('E:1 W:2 I:3 H:4', result)
exec_lua('vim.cmd.enew()')
-- Empty diagnostics for a buffer without diagnostics
eq(
'',
exec_lua(function()
return vim.diagnostic.status()
end)
)
end)
it('uses text from diagnostic.config().signs.text[severity]', function()
local result = exec_lua(function()
vim.diagnostic.config({
signs = {
text = {
[vim.diagnostic.severity.ERROR] = '',
[vim.diagnostic.severity.WARN] = '⚠︎',
},
},
})
vim.diagnostic.set(_G.diagnostic_ns, 0, {
_G.make_error('Error 1', 0, 1, 0, 1),
_G.make_warning('Warning 1', 2, 2, 2, 2),
})
return vim.diagnostic.status()
end)
eq(':1 ⚠︎:1', result)
end)
end)
describe('handlers', function()
it('checks that a new handler is a table', function()
matches(
[[.*handler: expected table, got string.*]],
pcall_err(exec_lua, [[ vim.diagnostic.handlers.foo = "bar" ]])
)
matches(
[[.*handler: expected table, got function.*]],
pcall_err(exec_lua, [[ vim.diagnostic.handlers.foo = function() end ]])
)
end)
it('can add new handlers', function()
eq(
true,
exec_lua(function()
local handler_called = false
vim.diagnostic.handlers.test = {
show = function(namespace, bufnr, diagnostics, opts)
assert(namespace == _G.diagnostic_ns)
assert(bufnr == _G.diagnostic_bufnr)
assert(#diagnostics == 1)
assert(opts.test.some_opt == 42)
handler_called = true
end,
}
vim.diagnostic.config({ test = { some_opt = 42 } })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_warning('Warning', 0, 0, 0, 0),
})
return handler_called
end)
)
end)
it('can disable handlers by setting the corresponding option to false', function()
eq(
false,
exec_lua(function()
local handler_called = false
vim.diagnostic.handlers.test = {
show = function(_, _, _, _)
handler_called = true
end,
}
vim.diagnostic.config({ test = false })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_warning('Warning', 0, 0, 0, 0),
})
return handler_called
end)
)
end)
it("always calls a handler's hide function if defined", function()
eq(
{ false, true },
exec_lua(function()
local hide_called = false
local show_called = false
vim.diagnostic.handlers.test = {
show = function(_, _, _, _)
show_called = true
end,
hide = function(namespace, bufnr)
assert(namespace == _G.diagnostic_ns)
assert(bufnr == _G.diagnostic_bufnr)
hide_called = true
end,
}
vim.diagnostic.config({ test = false })
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_warning('Warning', 0, 0, 0, 0),
})
vim.diagnostic.hide(_G.diagnostic_ns, _G.diagnostic_bufnr)
return { show_called, hide_called }
end)
)
end)
it('triggers the autocommand when diagnostics are set', function()
eq(
{ true, true },
exec_lua(function()
-- Set a different buffer as current to test that <abuf> is being set properly in
-- DiagnosticChanged callbacks
local tmp = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(tmp)
local triggered = {}
vim.api.nvim_create_autocmd('DiagnosticChanged', {
callback = function(args)
triggered = { args.buf, #args.data.diagnostics }
end,
})
vim.api.nvim_buf_set_name(_G.diagnostic_bufnr, 'test | test')
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic', 0, 0, 0, 0),
})
return {
triggered[1] == _G.diagnostic_bufnr,
triggered[2] == 1,
}
end)
)
end)
it('triggers the autocommand when diagnostics are cleared', function()
eq(
true,
exec_lua(function()
local tmp = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(tmp)
vim.g.diagnostic_autocmd_triggered = 0
vim.cmd(
'autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = +expand("<abuf>")'
)
vim.api.nvim_buf_set_name(_G.diagnostic_bufnr, 'test | test')
vim.diagnostic.reset(_G.diagnostic_ns, _G.diagnostic_bufnr)
return vim.g.diagnostic_autocmd_triggered == _G.diagnostic_bufnr
end)
)
end)
it('is_enabled', function()
eq(
{ false, false, false, false, false },
exec_lua(function()
vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
_G.make_error('Diagnostic #1', 1, 1, 1, 1),
})
vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
vim.diagnostic.enable(false)
return {
vim.diagnostic.is_enabled(),
vim.diagnostic.is_enabled { bufnr = 0 },
vim.diagnostic.is_enabled { bufnr = _G.diagnostic_bufnr },
vim.diagnostic.is_enabled { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns },
vim.diagnostic.is_enabled { bufnr = 0, ns_id = _G.diagnostic_ns },
}
end)
)
eq(
{ true, true, true, true, true },
exec_lua(function()
vim.diagnostic.enable()
return {
vim.diagnostic.is_enabled(),
vim.diagnostic.is_enabled { bufnr = 0 },
vim.diagnostic.is_enabled { bufnr = _G.diagnostic_bufnr },
vim.diagnostic.is_enabled { bufnr = _G.diagnostic_bufnr, ns_id = _G.diagnostic_ns },
vim.diagnostic.is_enabled { bufnr = 0, ns_id = _G.diagnostic_ns },
}
end)
)
end)
end)
end)