fix(cmdline): redraw cmdline after empty message (#38485)

Problem: Cmdline is not redrawn after an empty message clears it.
Remembered last drawn cursor position may be outdated but
equal to the current cmdline content with UI2.
Solution: Ensure cmdline is redrawn after an empty message clears it.
Compare wanted cursor position with actual cursor position.

(cherry picked from commit 1685ced335)
This commit is contained in:
luukvbaal
2026-03-31 14:16:55 +02:00
committed by github-actions[bot]
parent 2d9619fac7
commit 14ee84e7a5
3 changed files with 15 additions and 6 deletions

View File

@@ -121,23 +121,22 @@ function M.cmdline_special_char(c, shift)
end) end)
end end
local curpos = { 0, 0 } -- Last drawn cursor position.
--- Set the cmdline cursor position. --- Set the cmdline cursor position.
--- ---
---@param pos integer ---@param pos integer
--@param level integer --@param level integer
function M.cmdline_pos(pos) function M.cmdline_pos(pos)
local curpos = api.nvim_win_get_cursor(ui.wins.cmd)
pos = #fn.strtrans(cmdbuff:sub(1, pos)) pos = #fn.strtrans(cmdbuff:sub(1, pos))
if curpos[1] ~= M.erow + 1 or curpos[2] ~= promptlen + pos then if curpos[1] ~= M.erow + 1 or curpos[2] ~= promptlen + pos then
curpos[1], curpos[2] = M.erow + 1, promptlen + pos
-- Add matchparen highlighting to non-prompt part of cmdline. -- Add matchparen highlighting to non-prompt part of cmdline.
if pos > 0 and fn.exists('#matchparen#CursorMoved') == 1 then if pos > 0 and fn.exists('#matchparen#CursorMoved') == 1 then
api.nvim_win_set_cursor(ui.wins.cmd, { curpos[1], curpos[2] - 1 }) api.nvim_win_set_cursor(ui.wins.cmd, { M.erow + 1, promptlen + pos - 1 })
vim._with({ win = ui.wins.cmd, wo = { eventignorewin = '' } }, function() vim._with({ win = ui.wins.cmd, wo = { eventignorewin = '' } }, function()
api.nvim_exec_autocmds('CursorMoved', {}) api.nvim_exec_autocmds('CursorMoved', {})
end) end)
end end
api.nvim_win_set_cursor(ui.wins.cmd, curpos) api.nvim_win_set_cursor(ui.wins.cmd, { M.erow + 1, promptlen + pos })
end end
end end
@@ -176,7 +175,7 @@ function M.cmdline_hide(level, abort)
end end
end) end)
M.prompt, M.level, curpos[1], curpos[2] = false, 0, 0, 0 M.prompt, M.level = false, 0
win_config(ui.wins.cmd, true, ui.cmdheight) win_config(ui.wins.cmd, true, ui.cmdheight)
end end

View File

@@ -2338,6 +2338,7 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
if (*str == NUL && ui_has(kUIMessages)) { if (*str == NUL && ui_has(kUIMessages)) {
ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false, ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false,
INTEGER_OBJ(-1), (String)STRING_INIT); INTEGER_OBJ(-1), (String)STRING_INIT);
cmdline_was_last_drawn = false;
} }
return; return;
} }
@@ -3311,6 +3312,7 @@ void msg_clr_eos_force(void)
if (msg_row < Rows - 1 || msg_col == 0) { if (msg_row < Rows - 1 || msg_col == 0) {
clear_cmdline = false; // command line has been cleared clear_cmdline = false; // command line has been cleared
mode_displayed = false; // mode cleared or overwritten mode_displayed = false; // mode cleared or overwritten
cmdline_was_last_drawn = false;
} }
} }

View File

@@ -172,7 +172,7 @@ describe('cmdline2', function()
t.eq(n.eval('v:errmsg'), "E1514: 'findfunc' did not return a List type") t.eq(n.eval('v:errmsg'), "E1514: 'findfunc' did not return a List type")
end) end)
it('substitution match does not clear cmdline', function() it('substitution match, empty message does not clear active cmdline', function()
exec('call setline(1, "foo")') exec('call setline(1, "foo")')
feed(':s/f') feed(':s/f')
screen:expect([[ screen:expect([[
@@ -180,6 +180,14 @@ describe('cmdline2', function()
{1:~ }|*12 {1:~ }|*12
{16::}{15:s}{16:/f^ } | {16::}{15:s}{16:/f^ } |
]]) ]])
feed('<Esc>:foo')
screen:expect([[
foo |
{1:~ }|*12
{16::}{15:foo}^ |
]])
exec('echo')
screen:expect_unchanged(true)
end) end)
it('dialog position is adjusted for toggled non-pum wildmenu', function() it('dialog position is adjusted for toggled non-pum wildmenu', function()