From ff95d7ff9a04150ca988e070c8c1aa65b86d54fc Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 1 Jun 2025 20:54:38 +0200 Subject: [PATCH] fix(extui): adjust "more" window routing (#34251) Problem: Message lines from multiple message events that end up spilling 'cmdheight' end up spread out over the cmdline and "more" window. Messages emitted as feedback to a typed :command (rather than its sole purpose like :echo/:=) are routed to the more window. The more window isn't closed when entering the cmdwin, and doesn't allow `vim.hl.on_yank()`. Solution: When first opening the "more" window for spilled messages, move the message buffer to the more window. Restrict routing of typed commands to echo kinds. Ignore all events but WinLeave and TextYankPost. --- runtime/lua/vim/_extui.lua | 6 ++-- runtime/lua/vim/_extui/messages.lua | 45 +++++++++++++++++------------ runtime/lua/vim/_extui/shared.lua | 3 +- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/runtime/lua/vim/_extui.lua b/runtime/lua/vim/_extui.lua index 7968049bd2..7d83e05d75 100644 --- a/runtime/lua/vim/_extui.lua +++ b/runtime/lua/vim/_extui.lua @@ -110,10 +110,8 @@ function M.enable(opts) end end - if vim.v.vim_did_enter == 1 then - ext.tab_check_wins() - check_opt('cmdheight', vim.o.cmdheight) - end + ext.tab_check_wins() + check_opt('cmdheight', vim.o.cmdheight) api.nvim_create_autocmd('OptionSet', { group = ext.augroup, diff --git a/runtime/lua/vim/_extui/messages.lua b/runtime/lua/vim/_extui/messages.lua index 7f8355bcca..c10cf50049 100644 --- a/runtime/lua/vim/_extui/messages.lua +++ b/runtime/lua/vim/_extui/messages.lua @@ -42,8 +42,8 @@ local M = { ---@param len integer Number of rows that should be removed. function M.box:start_timer(buf, len) self.timer = vim.defer_fn(function() - if self.count == 0 or not api.nvim_buf_is_valid(buf) then - return -- Messages moved to split or buffer was closed. + if buf ~= ext.bufs.box or not api.nvim_buf_is_valid(buf) then + return -- Messages moved to more or buffer was closed. end api.nvim_buf_set_lines(buf, 0, len, false, {}) self.count = self.count - 1 @@ -168,6 +168,16 @@ local function set_virttext(type) end end +---Move message buffer to more window. +local function msg_to_more(tar) + api.nvim_win_set_buf(ext.wins[ext.tab].more, ext.bufs[tar]) + api.nvim_buf_delete(ext.bufs.more, { force = true }) + api.nvim_buf_set_name(ext.bufs[tar], 'vim._extui.more') + ext.bufs.more, ext.bufs[tar], M[tar].count = ext.bufs[tar], -1, 0 + ext.tab_check_wins() + M.set_pos('more') +end + -- We need to keep track of the current message column to be able to -- append or overwrite messages for :echon or carriage returns. local col = 0 @@ -231,6 +241,7 @@ function M.show_msg(tar, content, replace_last, append, more) hl_group = chunk[3], undo_restore = false, invalidate = true, + priority = 1, }) end @@ -245,21 +256,20 @@ function M.show_msg(tar, content, replace_last, append, more) if tar == 'box' then api.nvim_win_set_width(ext.wins[ext.tab].box, width) local h = api.nvim_win_text_height(ext.wins[ext.tab].box, { start_row = start_row }) - if h.all > (more and 1 or math.ceil(o.lines * 0.5)) then - api.nvim_buf_set_lines(ext.bufs.box, start_row, -1, false, {}) + if more and h.all > 1 then + msg_to_more(tar) api.nvim_win_set_width(ext.wins[ext.tab].box, M.box.width) - M.msg_history_show({ { 'spill', content } }) -- show message in 'more' window return end M.set_pos('box') + M.box.width = width if restart then M.box.timer:stop() M.box.timer:set_repeat(4000) M.box.timer:again() else M.box:start_timer(ext.bufs.box, row - start_row + 1) - M.box.width = width end elseif tar == 'cmd' and dupe == 0 then fn.clearmatches(ext.wins[ext.tab].cmd) -- Clear matchparen highlights. @@ -272,8 +282,8 @@ function M.show_msg(tar, content, replace_last, append, more) else local h = api.nvim_win_text_height(ext.wins[ext.tab].cmd, {}) if more and h.all > ext.cmdheight then - api.nvim_buf_set_lines(ext.bufs.cmd, start_row, -1, false, {}) - M.msg_history_show({ { 'spill', content } }) -- show message in 'more' window + ext.cmd.highlighter:destroy() + msg_to_more(tar) return end @@ -302,7 +312,6 @@ function M.show_msg(tar, content, replace_last, append, more) end end -local append_more = 0 local replace_bufwrite = false --- Route the message to the appropriate sink. --- @@ -329,7 +338,7 @@ function M.msg_show(kind, content, _, _, append) -- Verbose messages are sent too often to be meaningful in the cmdline: -- always route to box regardless of cfg.msg.pos. M.show_msg('box', content, false, append) - elseif append_more > 0 and ext.cfg.msg.pos == 'cmd' then + elseif ext.cfg.msg.pos == 'cmd' and api.nvim_get_current_win() == ext.wins[ext.tab].more then -- Append message to already open 'more' window. M.msg_history_show({ { 'spill', content } }) api.nvim_command('norm! G') @@ -351,8 +360,9 @@ function M.msg_show(kind, content, _, _, append) M.virt.last[M.virt.idx.search][1] = nil end - -- Messages sent as a result of a typed command should be routed to the more window. - local more = ext.cmd.level >= 0 or kind == 'list_cmd' + -- Typed "inspection" messages should be routed to the more window. + local typed_more = { 'echo', 'echomsg', 'lua_print' } + local more = kind == 'list_cmd' or (ext.cmd.level >= 0 and vim.tbl_contains(typed_more, kind)) M.show_msg(tar, content, replace_bufwrite, append, more) -- Replace message for every second bufwrite message. replace_bufwrite = not replace_bufwrite and kind == 'bufwrite' @@ -402,13 +412,13 @@ function M.msg_history_show(entries) end -- Appending messages while 'more' window is open. - if append_more == 0 then + local append_more = api.nvim_get_current_win() == ext.wins[ext.tab].more + if not append_more then api.nvim_buf_set_lines(ext.bufs.more, 0, -1, false, {}) end - append_more = append_more + 1 for i, entry in ipairs(entries) do - M.show_msg('more', entry[2], i == 1 and append_more < 2, false) + M.show_msg('more', entry[2], i == 1 and not append_more, false) end M.set_pos('more') @@ -443,17 +453,16 @@ function M.set_pos(type) end -- It's actually closed one event iteration later so schedule in case it was open. vim.schedule(function() - api.nvim_create_autocmd('WinEnter', { + api.nvim_set_current_win(win) + api.nvim_create_autocmd('WinLeave', { once = true, callback = function() if api.nvim_win_is_valid(win) then api.nvim_win_set_config(win, { hide = true }) end - append_more = 0 end, desc = 'Hide inactive "more" window.', }) - api.nvim_set_current_win(win) end) end end diff --git a/runtime/lua/vim/_extui/shared.lua b/runtime/lua/vim/_extui/shared.lua index 9821e761a7..ff70dd7eb6 100644 --- a/runtime/lua/vim/_extui/shared.lua +++ b/runtime/lua/vim/_extui/shared.lua @@ -88,7 +88,8 @@ function M.tab_check_wins() api.nvim_set_option_value('smoothscroll', true, { scope = 'local' }) local ft = type == 'cmd' and 'cmdline' or ('msg' .. type) api.nvim_set_option_value('filetype', ft, { scope = 'local' }) - api.nvim_set_option_value('eventignorewin', 'all', { scope = 'local' }) + local ignore = 'all' .. (type == 'more' and ',-WinLeave,-TextYankPost' or '') + api.nvim_set_option_value('eventignorewin', ignore, { scope = 'local' }) end) end end