From 28ff47b8a4142b7596639d1b717a88304be91a88 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 11 Sep 2026 01:01:16 -0400 Subject: [PATCH] fix(ui2): dialog messages offset by the pager line count #41839 Problem: expand_msg() derives its start row from the pager buffer even when the target is the cmd or dialog buffer, so the copied lines are appended instead of replacing, and the copied extmarks are placed at pager rows in a smaller buffer: "Invalid 'end_col': out of range", thrown inside a UI event handler. Solution: Offset only when appending to a visible pager, and read the line count after msg_clear(), whose autocmds may write to the pager. Copy marks with `strict = false`, since the rows and columns are recomputed for a different buffer and should clamp rather than raise. AI-assisted --- runtime/lua/vim/_core/ui2/messages.lua | 5 +++-- test/functional/ui/messages2_spec.lua | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/runtime/lua/vim/_core/ui2/messages.lua b/runtime/lua/vim/_core/ui2/messages.lua index b6084daffe..1727a8794a 100644 --- a/runtime/lua/vim/_core/ui2/messages.lua +++ b/runtime/lua/vim/_core/ui2/messages.lua @@ -228,7 +228,7 @@ local function pager_shown() return api.nvim_win_is_valid(ui.wins.pager) and not api.nvim_win_get_config(ui.wins.pager).hide end -local hlopts = { undo_restore = false, invalidate = true, priority = 1 } +local hlopts = { undo_restore = false, invalidate = true, priority = 1, strict = false } --- Move messages to expanded cmdline, dialog or pager to show in full. --- Return updated target+buffer in case it differs from 'src'. --- @@ -240,7 +240,6 @@ function M.expand_msg(src, tgt, focus) local hidden = not pager_shown() tgt = tgt or not hidden and 'pager' or 'cmd' ---@type 'cmd'|'dialog'|'msg'|'pager' if tgt ~= src then - local srow = hidden and 0 or api.nvim_buf_line_count(ui.bufs.pager) local opts = { details = true, type = 'highlight' } local marks = api.nvim_buf_get_extmarks(ui.bufs[src], -1, 0, -1, opts) local lines = api.nvim_buf_get_lines(ui.bufs[src], 0, -1, false) @@ -252,6 +251,8 @@ function M.expand_msg(src, tgt, focus) M.virt[src] = { {}, {} } end + -- Append to visible pager, replace any other target. msg_clear() events may have edited pager. + local srow = (not hidden and tgt == 'pager') and api.nvim_buf_line_count(ui.bufs.pager) or 0 api.nvim_buf_set_lines(ui.bufs[tgt], srow, -1, false, lines) for _, m in ipairs(marks) do hlopts.hl_group, hlopts.end_col, hlopts.end_row = diff --git a/test/functional/ui/messages2_spec.lua b/test/functional/ui/messages2_spec.lua index 4292b0c5ce..52b1a8197f 100644 --- a/test/functional/ui/messages2_spec.lua +++ b/test/functional/ui/messages2_spec.lua @@ -1450,5 +1450,23 @@ describe('messages2', function() {9:de} | | ]]) + + -- A prompt with pending cmd messages moves them to the dialog (cmdline.lua). The pager is + -- still shown, but its line count must not offset the marks copied into the dialog. + exec_lua(function() + local ui = require('vim._core.ui2') + vim.api.nvim_buf_set_lines(ui.bufs.cmd, 0, -1, false, { 'err' }) + local o = { end_row = 0, end_col = 3, hl_group = 'ErrorMsg' } + vim.api.nvim_buf_set_extmark(ui.bufs.cmd, ui.ns, 0, 0, o) + ui.msg.expand_msg('cmd', 'dialog') + end) + screen:expect([[ + | + {1:~ }|*9 + {3: }| + {3:^ }| + {9:err} | + | + ]]) end) end)