fix(diagnostic): virtual_lines diagnostic columns (#32703)

When multiple diagnostics appear on a single line, the virtual lines for
all diagnostics except the first were rendered with progressively fewer
columns.
This commit is contained in:
James Trew
2025-03-04 09:59:37 -05:00
committed by GitHub
parent 859dbb6619
commit fb842dfc22
2 changed files with 105 additions and 2 deletions

View File

@ -1757,8 +1757,7 @@ local function render_virtual_lines(namespace, bufnr, diagnostics)
string.rep(
' ',
-- +1 because indexing starts at 0 in one API but at 1 in the other.
-- -1 for non-first lines, since the previous column was already drawn.
distance_between_cols(bufnr, diag.lnum, prev_col + 1, diag.col) - 1
distance_between_cols(bufnr, diag.lnum, prev_col + 1, diag.col)
),
})
else

View File

@ -2231,6 +2231,110 @@ describe('vim.diagnostic', function()
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 })