From 84fd9214cc71860e8bcbcdbdb4bd45bf00cabf50 Mon Sep 17 00:00:00 2001 From: penglei Date: Mon, 20 Jul 2026 18:59:21 +0800 Subject: [PATCH] fix(diagnostic): skip out-of-range lnum in underline handler #40839 Problem: `vim.diagnostic.handlers.underline.show` throws `E565: Index out of bounds` when it tries to underline a diagnostic whose `lnum` is past the end of the buffer. This happens with stale diagnostics set on an unloaded buffer (e.g. via `bufadd()`) that are drawn only after the buffer is loaded: by then the file on disk can be shorter than the `lnum` the diagnostic carried. File pickers (snacks.nvim) that open files via `bufadd` + `:buffer` hit this whenever an LSP server has already emitted diagnostics for that URI. Solution: Skip diagnostics with an out-of-range `lnum` in the underline handler. Other handlers `M.virtual_text.show()`, `M.signs.show()`, have a similar condition. --- runtime/lua/vim/diagnostic/_handlers.lua | 43 +++++++++++++----------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/runtime/lua/vim/diagnostic/_handlers.lua b/runtime/lua/vim/diagnostic/_handlers.lua index 1490ceb76e..36233d9b85 100644 --- a/runtime/lua/vim/diagnostic/_handlers.lua +++ b/runtime/lua/vim/diagnostic/_handlers.lua @@ -265,29 +265,34 @@ function M.underline.show(namespace, bufnr, diagnostics, opts) local underline_ns = ns.user_data.underline_ns local get_priority = severity_to_extmark_priority(vim.hl.priorities.diagnostics, opts) + -- skip diagnostics with an out-of-range lnum. + local line_count = api.nvim_buf_line_count(bufnr) + for _, diagnostic0 in ipairs(diagnostics) do - local higroups = { underline_highlight_map[diagnostic0.severity] } + if diagnostic0.lnum < line_count then + local higroups = { underline_highlight_map[diagnostic0.severity] } - if diagnostic0._tags then - if diagnostic0._tags.unnecessary then - table.insert(higroups, 'DiagnosticUnnecessary') - end - if diagnostic0._tags.deprecated then - table.insert(higroups, 'DiagnosticDeprecated') + if diagnostic0._tags then + if diagnostic0._tags.unnecessary then + table.insert(higroups, 'DiagnosticUnnecessary') + end + if diagnostic0._tags.deprecated then + table.insert(higroups, 'DiagnosticDeprecated') + end end + + local lines = + api.nvim_buf_get_lines(diagnostic0.bufnr, diagnostic0.lnum, diagnostic0.lnum + 1, true) + + vim.hl.range( + bufnr, + underline_ns, + higroups, + { diagnostic0.lnum, math.min(diagnostic0.col, #lines[1] - 1) }, + { diagnostic0.end_lnum, diagnostic0.end_col }, + { priority = get_priority(diagnostic0.severity) } + ) end - - local lines = - api.nvim_buf_get_lines(diagnostic0.bufnr, diagnostic0.lnum, diagnostic0.lnum + 1, true) - - vim.hl.range( - bufnr, - underline_ns, - higroups, - { diagnostic0.lnum, math.min(diagnostic0.col, #lines[1] - 1) }, - { diagnostic0.end_lnum, diagnostic0.end_col }, - { priority = get_priority(diagnostic0.severity) } - ) end save_extmarks(underline_ns, bufnr)