From 1685ced33551ec9c01fc0296f2f6566d14f65b26 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Tue, 31 Mar 2026 14:16:55 +0200 Subject: [PATCH] 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. --- runtime/lua/vim/_core/ui2/cmdline.lua | 9 ++++----- src/nvim/message.c | 2 ++ test/functional/ui/cmdline2_spec.lua | 10 +++++++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/runtime/lua/vim/_core/ui2/cmdline.lua b/runtime/lua/vim/_core/ui2/cmdline.lua index 0b9638275f..2260d85819 100644 --- a/runtime/lua/vim/_core/ui2/cmdline.lua +++ b/runtime/lua/vim/_core/ui2/cmdline.lua @@ -121,23 +121,22 @@ function M.cmdline_special_char(c, shift) end) end -local curpos = { 0, 0 } -- Last drawn cursor position. --- Set the cmdline cursor position. --- ---@param pos integer --@param level integer function M.cmdline_pos(pos) + local curpos = api.nvim_win_get_cursor(ui.wins.cmd) pos = #fn.strtrans(cmdbuff:sub(1, pos)) 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. 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() api.nvim_exec_autocmds('CursorMoved', {}) 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 @@ -176,7 +175,7 @@ function M.cmdline_hide(level, abort) 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) end diff --git a/src/nvim/message.c b/src/nvim/message.c index 4ceeb083cc..62a5e42cbe 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -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)) { ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false, INTEGER_OBJ(-1), (String)STRING_INIT); + cmdline_was_last_drawn = false; } return; } @@ -3311,6 +3312,7 @@ void msg_clr_eos_force(void) if (msg_row < Rows - 1 || msg_col == 0) { clear_cmdline = false; // command line has been cleared mode_displayed = false; // mode cleared or overwritten + cmdline_was_last_drawn = false; } } diff --git a/test/functional/ui/cmdline2_spec.lua b/test/functional/ui/cmdline2_spec.lua index b6879c860c..402700c88d 100644 --- a/test/functional/ui/cmdline2_spec.lua +++ b/test/functional/ui/cmdline2_spec.lua @@ -172,7 +172,7 @@ describe('cmdline2', function() t.eq(n.eval('v:errmsg'), "E1514: 'findfunc' did not return a List type") 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")') feed(':s/f') screen:expect([[ @@ -180,6 +180,14 @@ describe('cmdline2', function() {1:~ }|*12 {16::}{15:s}{16:/f^ } | ]]) + feed(':foo') + screen:expect([[ + foo | + {1:~ }|*12 + {16::}{15:foo}^ | + ]]) + exec('echo') + screen:expect_unchanged(true) end) it('dialog position is adjusted for toggled non-pum wildmenu', function()