fix(ui2): wildmenu hidden behind dialog window #37684

Problem:  The wildmenu is hidden behind the dialog window with "list" in 'wildmode'.
          Global ('laststatus' set to 3) statusline is hidden behind the
          pager window.
Solution: Check wildmenumode() to adjust the dialog position when necessary.
          Ensure pager is positioned above the global statusline with 'laststus' set to 3.
This commit is contained in:
luukvbaal
2026-02-03 14:17:33 +01:00
committed by GitHub
parent dbb3986f33
commit 3038f0191e
4 changed files with 57 additions and 19 deletions

View File

@@ -8,6 +8,7 @@ local M = {
srow = 0, -- Buffer row at which the current cmdline starts; > 0 in block mode.
erow = 0, -- Buffer row at which the current cmdline ends; messages appended here in block mode.
level = -1, -- Current cmdline level; 0 when inactive, -1 one loop iteration after closing.
wmnumode = 0, -- Return value of wildmenumode(), dialog position adjusted when toggled.
}
--- Set the 'cmdheight' and cmdline window height. Reposition message windows.
@@ -28,6 +29,9 @@ local function win_config(win, hide, height)
vim.o.cmdheight = height
end)
ext.msg.set_pos()
elseif M.wmnumode ~= (M.prompt and fn.wildmenumode() or 0) then
M.wmnumode = (M.wmnumode == 1 and 0 or 1)
ext.msg.set_pos()
end
end

View File

@@ -469,25 +469,20 @@ end
---@param type? 'cmd'|'dialog'|'msg'|'pager' Type of to be positioned window (nil for all).
function M.set_pos(type)
local function win_set_pos(win)
local cfg = { hide = false, relative = 'laststatus', col = 10000 }
local texth = type and api.nvim_win_text_height(win, {}) or {}
local height = type and math.min(texth.all, math.ceil(o.lines * 0.5))
local top = { vim.opt.fcs:get().msgsep or ' ', 'MsgSeparator' }
local border = win ~= ext.wins.msg and { '', top, '', '', '', '', '', '' } or nil
local config = {
hide = false,
relative = (win == ext.wins.pager or win == ext.wins.dialog) and 'editor' or 'laststatus',
border = border,
height = height,
row = (win == ext.wins.pager or win == ext.wins.dialog) and o.lines - o.cmdheight or 0,
col = 10000,
focusable = type == 'cmd' or nil, -- Allow entering the cmdline window.
}
api.nvim_win_set_config(win, config)
cfg.height = type and math.min(texth.all, math.ceil(o.lines * 0.5))
cfg.border = win ~= ext.wins.msg and { '', top, '', '', '', '', '', '' } or nil
cfg.focusable = type == 'cmd' or nil
cfg.row = (win == ext.wins.msg and 0 or 1) - ext.cmd.wmnumode
cfg.row = cfg.row - ((win == ext.wins.pager and o.laststatus == 3) and 1 or 0)
api.nvim_win_set_config(win, cfg)
if type == 'cmd' and not cmd_on_key then
-- Temporarily showing a full message in the cmdline, until next key press.
local save_spill = M.virt.msg[M.virt.idx.spill][1]
local spill = texth.all > height and (' [+%d]'):format(texth.all - height)
local spill = texth.all > cfg.height and (' [+%d]'):format(texth.all - cfg.height)
M.virt.msg[M.virt.idx.spill][1] = spill and { 0, spill } or nil
set_virttext('msg', 'cmd')
M.virt.msg[M.virt.idx.spill][1] = save_spill
@@ -554,7 +549,7 @@ function M.set_pos(type)
end)
elseif type == 'msg' then
-- Ensure last line is visible and first line is at top of window.
local row = (texth.all > height and texth.end_row or 0) + 1
local row = (texth.all > cfg.height and texth.end_row or 0) + 1
api.nvim_win_set_cursor(ext.wins.msg, { row, 0 })
elseif type == 'pager' then
if fn.getcmdwintype() ~= '' then
@@ -571,10 +566,10 @@ function M.set_pos(type)
api.nvim_create_autocmd({ 'WinEnter', 'CmdwinEnter', 'CmdwinLeave' }, {
callback = function(ev)
if api.nvim_win_is_valid(ext.wins.pager) then
local cfg = ev.event == 'CmdwinLeave' and config
local config = ev.event == 'CmdwinLeave' and cfg
or ev.event == 'WinEnter' and { hide = true }
or { relative = 'win', win = 0, row = 0, col = 0 }
api.nvim_win_set_config(ext.wins.pager, cfg)
api.nvim_win_set_config(ext.wins.pager, config)
end
return ev.event == 'WinEnter'
end,

View File

@@ -33,7 +33,7 @@ local tab = 0
---Ensure target buffers and windows are still valid.
function M.check_targets()
local curtab = api.nvim_get_current_tabpage()
for _, type in ipairs({ 'cmd', 'dialog', 'msg', 'pager' }) do
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)
@@ -50,8 +50,8 @@ function M.check_targets()
anchor = type ~= 'cmd' and 'SE' or nil,
hide = type ~= 'cmd' or M.cmdheight == 0 or nil,
border = type ~= 'msg' and 'none' or nil,
-- kZIndexMessages < zindex < kZIndexCmdlinePopupMenu (grid_defs.h), pager below others.
zindex = 200 + (type == 'cmd' and 1 or type == 'pager' and -1 or 0),
-- 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

View File

@@ -13,6 +13,7 @@ describe('cmdline2', function()
screen = Screen.new()
screen:add_extra_attr_ids({
[100] = { foreground = Screen.colors.Magenta1, bold = true },
[101] = { background = Screen.colors.Yellow, foreground = Screen.colors.Grey0 },
})
exec_lua(function()
require('vim._extui').enable({})
@@ -180,6 +181,44 @@ describe('cmdline2', function()
{16::}{15:s}{16:/f}^ |
]])
end)
it('dialog position is adjusted for toggled wildmenu', function()
exec([[
set wildmode=list:full,full wildoptions-=pum
func Foo()
endf
func Fooo()
endf
]])
feed(':call Fo<C-Z>')
screen:expect([[
|
{1:~ }|*9
{3: }|
Foo() Fooo() |
|
{16::}{15:call} Fo^ |
]])
feed('<C-Z>')
screen:expect([[
|
{1:~ }|*8
{3: }|
Foo() Fooo() |
|
{101:Foo()}{3: Fooo() }|
{16::}{15:call} {25:Foo}{16:()}^ |
]])
feed('()')
screen:expect([[
|
{1:~ }|*9
{3: }|
Foo() Fooo() |
|
{16::}{15:call} {25:Foo}{16:()()}^ |
]])
end)
end)
describe('cmdline2', function()