diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index bfea1e90ee..604da4d3b6 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -854,8 +854,8 @@ Highlighted with the |hl-MoreMsg| group. With |ui2|: use "g<" to show messages in the pager. When a command emits a message while a previous message is still displayed, the messages are -collected in the pager window without focusing it: use "g<" to enter the -pager, any other typed key dismisses it. +collected in the pager window without focusing it: type "g<" to focus it, or +any other typed key to dismiss it. Type Effect ~ or or j or one more line diff --git a/runtime/lua/vim/_core/ui2/messages.lua b/runtime/lua/vim/_core/ui2/messages.lua index e93eed2da3..7abcd17e80 100644 --- a/runtime/lua/vim/_core/ui2/messages.lua +++ b/runtime/lua/vim/_core/ui2/messages.lua @@ -209,14 +209,20 @@ local function set_virttext(type, tgt) end end +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 } --- Move messages to expanded cmdline, dialog or pager to show in full. --- Return updated target+buffer in case it differs from 'src'. -function M.expand_msg(src, tgt) +--- +---@param focus? boolean Enter the pager: it was explicitly requested. +function M.expand_msg(src, tgt, focus) -- Copy and clear message from src to enlarged cmdline that is dismissed by any -- key press. Append to pager instead if it isn't hidden or we want to enter it -- after cmdline was entered during expanded cmdline. - local hidden = api.nvim_win_get_config(ui.wins.pager).hide + 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) @@ -244,7 +250,7 @@ function M.expand_msg(src, tgt) api.nvim_buf_del_extmark(ui.bufs.cmd, ui.ns, id) end end - M.set_pos(tgt) + M.set_pos(tgt, focus) return tgt, ui.bufs[tgt] end @@ -413,12 +419,6 @@ function M.show_msg(tgt, kind, content, replace_last, append, id) end local in_pager = false -- Whether the pager is or will be the current window. -local pager_focus = false -- Whether the pager was explicitly requested (|g<|, :messages). - -local function pager_shown() - return not api.nvim_win_get_config(ui.wins.pager).hide -end - --- Route the message to the appropriate sink. --- ---@param kind string @@ -573,9 +573,8 @@ function M.msg_history_show(entries, prev_cmd) M.show_msg('pager', entry[1], entry[2], i == 1, entry[3], 0) end - -- Message history was explicitly requested (e.g. |g<| or |:messages|): enter the pager. - pager_focus = true - M.set_pos('pager') + -- Message history was explicitly requested (|g<|, |:messages|): enter the pager. + M.set_pos('pager', true) end local typed_g = false @@ -595,14 +594,13 @@ local function cmd_on_key(key, typed) vim.on_key(nil, ui.ns) typed = fn.keytrans(typed) - -- Check if window was entered and reopen with original config. An already open + -- Check if window was entered and reopen with original config. A shown (but not entered) -- pager is dismissed instead; "g<" passes through to reopen and enter it. - local mode = not api.nvim_get_mode().mode:match('[it]') and not pager_shown() - local enter = mode and (typed == '' or typed_g and (typed == '' or key == '<')) + local can_enter = not api.nvim_get_mode().mode:match('[it]') and not pager_shown() + local enter = can_enter and (typed == '' or typed_g and (typed == '' or key == '<')) or (typed:find('LeftMouse') and fn.getmousepos().winid == ui.wins.cmd) if enter then - pager_focus = true - M.expand_msg('cmd', 'pager') + M.expand_msg('cmd', 'pager', true) elseif not in_pager then pcall(api.nvim_win_set_config, ui.wins.pager, { hide = true }) end @@ -716,7 +714,8 @@ end --- Adjust visibility and dimensions of the message windows after certain events. --- ---@param tgt? 'cmd'|'dialog'|'msg'|'pager' Target window to be positioned (nil for all). -function M.set_pos(tgt) +---@param focus? boolean Enter the pager: it was explicitly requested. +function M.set_pos(tgt, focus) for t, win in pairs(ui.wins) do local cfg = (t == tgt or (tgt == nil and t ~= 'cmd')) and api.nvim_win_is_valid(win) @@ -747,8 +746,7 @@ function M.set_pos(tgt) -- reliably places the final message at the bottom. :noautocmd avoids #40780. fn.win_execute(ui.wins.msg, 'noautocmd norm! Gzb') elseif tgt == 'pager' and not in_pager then - if pager_focus then - pager_focus = false + if focus then enter_pager() else -- Reuse only the handler: M.cmd_on_key would mark the cmdline as expanded. diff --git a/test/functional/ui/messages2_spec.lua b/test/functional/ui/messages2_spec.lua index d0f80a6ba6..1e6e0dfb1b 100644 --- a/test/functional/ui/messages2_spec.lua +++ b/test/functional/ui/messages2_spec.lua @@ -589,6 +589,22 @@ describe('messages2', function() -- "q" closes the entered pager. feed('q') t.eq(win, api.nvim_get_current_win()) + -- ":messages" from inside the pager does not make the next unfocused pager steal focus. + command('echo "foo\nbar"') + feed(':echo "baz"') + feed('g') + n.poke_eventloop() + t.neq(win, api.nvim_get_current_win()) + feed(':messages') + n.poke_eventloop() + feed('q') + n.poke_eventloop() + t.eq(win, api.nvim_get_current_win()) + command('echo "foo\nbar"') + feed(':echo "baz"') + n.poke_eventloop() + t.eq(win, api.nvim_get_current_win()) + feed('j') -- A typed command that emits no message keeps the pager; the next key dismisses it. command('echo "foo\nbar"') feed(':echo "baz"')