diff --git a/runtime/lua/vim/_core/ui2.lua b/runtime/lua/vim/_core/ui2.lua index b7ce79a76b..701e6e5d38 100644 --- a/runtime/lua/vim/_core/ui2.lua +++ b/runtime/lua/vim/_core/ui2.lua @@ -51,46 +51,35 @@ local M = { }, }, } ---- @type vim.api.keyset.win_config -local wincfg = { -- Default cfg for nvim_open_win(). - relative = 'laststatus', - style = 'minimal', - col = 0, - row = 1, - width = 10000, - height = 1, - noautocmd = true, - focusable = false, -} local tab = 0 ---Ensure target buffers and windows are still valid. function M.check_targets() local curtab = api.nvim_get_current_tabpage() for i, type in ipairs({ 'cmd', 'dialog', 'msg', 'pager' }) do - local setopt = not api.nvim_buf_is_valid(M.bufs[type]) - if setopt then - M.bufs[type] = api.nvim_create_buf(false, false) - end + local oldbuf = api.nvim_buf_is_valid(M.bufs[type]) and M.bufs[type] + local oldwin, setopt = api.nvim_win_is_valid(M.wins[type]) and M.wins[type], not oldbuf + M.bufs[type] = oldbuf or api.nvim_create_buf(false, false) - if - tab ~= curtab - or not api.nvim_win_is_valid(M.wins[type]) - or not api.nvim_win_get_config(M.wins[type]).zindex -- no longer floating - then - local cfg = vim.tbl_deep_extend('force', wincfg, { - mouse = type ~= 'cmd' and true or nil, - anchor = type ~= 'cmd' and 'SE' or nil, - hide = type ~= 'cmd' or M.cmdheight == 0 or nil, - border = type ~= 'msg' and 'none' or nil, - -- kZIndexMessages < cmd zindex < kZIndexCmdlinePopupMenu (grid_defs.h), pager below others. - zindex = 201 - i, - _cmdline_offset = type == 'cmd' and 0 or nil, - }) - if tab ~= curtab and api.nvim_win_is_valid(M.wins[type]) then - cfg = api.nvim_win_get_config(M.wins[type]) - api.nvim_win_close(M.wins[type], true) - end + if tab ~= curtab and oldwin then + -- Ensure dynamically set window configuration (M.msg.set_pos()) is copied + -- over when switching tabpage. TODO: move to tabpage instead after #35816. + M.wins[type] = api.nvim_open_win(M.bufs[type], false, api.nvim_win_get_config(oldwin)) + api.nvim_win_close(oldwin, true) + setopt = true + elseif not oldwin or not api.nvim_win_get_config(M.wins[type]).zindex then + -- Open a new window when closed or no longer floating (e.g. wincmd J). + local cfg = { col = 0, row = 1, width = 10000, height = 1, mouse = false, noautocmd = true } + cfg.focusable = false + cfg.style = 'minimal' + cfg.relative = 'laststatus' + cfg.anchor = type ~= 'cmd' and 'SE' or nil + cfg.mouse = type == 'pager' or nil + cfg.border = type ~= 'msg' and 'none' or nil + cfg._cmdline_offset = type == 'cmd' and 0 or nil + cfg.hide = type ~= 'cmd' or M.cmdheight == 0 or nil + -- kZIndexMessages < cmd zindex < kZIndexCmdlinePopupMenu (grid_defs.h), pager below others. + cfg.zindex = 201 - i M.wins[type] = api.nvim_open_win(M.bufs[type], false, cfg) setopt = true elseif api.nvim_win_get_buf(M.wins[type]) ~= M.bufs[type] then @@ -170,14 +159,10 @@ function M.enable(opts) if M.cfg.enable == false then -- Detach and cleanup windows, buffers and autocommands. for _, win in pairs(M.wins) do - if api.nvim_win_is_valid(win) then - api.nvim_win_close(win, true) - end + pcall(api.nvim_win_close, win, true) end for _, buf in pairs(M.bufs) do - if api.nvim_buf_is_valid(buf) then - api.nvim_buf_delete(buf, {}) - end + pcall(api.nvim_buf_delete, buf, {}) end api.nvim_clear_autocmds({ group = M.augroup }) vim.ui_detach(M.ns) @@ -232,6 +217,7 @@ function M.enable(opts) api.nvim_create_autocmd({ 'VimResized', 'TabEnter' }, { group = M.augroup, callback = function() + M.check_targets() M.msg.set_pos() end, desc = 'Set cmdline and message window dimensions after shell resize or tabpage change.', diff --git a/runtime/lua/vim/_core/ui2/messages.lua b/runtime/lua/vim/_core/ui2/messages.lua index 1f946553c5..9132ce4464 100644 --- a/runtime/lua/vim/_core/ui2/messages.lua +++ b/runtime/lua/vim/_core/ui2/messages.lua @@ -504,7 +504,7 @@ function M.set_pos(tgt) local lines = o.lines - (win == ui.wins.pager and ui.cmdheight + (o.ls == 3 and 2 or 0) or 0) cfg.height = math.min(texth.all, math.ceil(lines * (win == ui.wins.pager and 1 or 0.5))) cfg.border = win ~= ui.wins.msg and { '', top, '', '', '', '', '', '' } or nil - cfg.focusable = tgt == 'cmd' or nil + cfg.mouse = tgt == 'cmd' or nil cfg.row = (win == ui.wins.msg and 0 or 1) - ui.cmd.wmnumode cfg.row = cfg.row - ((win == ui.wins.pager and o.laststatus == 3) and 1 or 0) local title = { 'f/d/j: screen/page/line down, b/u/k: up, : stop paging', 'MsgSeparator' } @@ -561,6 +561,7 @@ function M.set_pos(tgt) api.nvim_win_set_config(ui.wins.dialog, { title = '' }) api.nvim__redraw({ flush = true }) vim.on_key(nil, M.dialog_on_key) + M.dialog_on_key = nil return '' end diff --git a/test/functional/ui/messages2_spec.lua b/test/functional/ui/messages2_spec.lua index 6a9279abb9..f1e90085ce 100644 --- a/test/functional/ui/messages2_spec.lua +++ b/test/functional/ui/messages2_spec.lua @@ -129,7 +129,7 @@ describe('messages2', function() -- Switching tabpage closes expanded cmdline #37659. command('tabnew | echo "foo\nbar"') screen:expect([[ - {24: + [No Name] }{5: }{100:2}{5: [No Name] }{2: }{24:X}| + {24: + [No Name] }{5: [No Name] }{2: }{24:X}| ^ | {1:~ }|*9 {3: }| @@ -170,7 +170,7 @@ describe('messages2', function() ]]) end) - it('new buffer, window and options after closing a buffer', function() + it('new buffer, window and options after closing a buffer or switching tabpage', function() command('set nomodifiable | echom "foo" | messages') screen:expect([[ | @@ -181,6 +181,15 @@ describe('messages2', function() ]]) command('bdelete | messages') screen:expect_unchanged() + set_msg_target_zero_ch() + command('quit | echo "foo\nbar" | tabnew') + screen:expect([[ + {24: [No Name] }{5: [No Name] }{2: }{24:X}| + ^ | + {1:~ }|*10 + {1:~ }{4:foo}| + {1:~ }{4:bar}| + ]]) end) it('screenclear and empty message clears messages', function()