From af37d459a64df0697e2a20132ec79a17718a66cd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Aug 2026 07:46:34 +0800 Subject: [PATCH] vim-patch:9.2.0985: Multiline messages not visible when mapping starts cmdline (#41405) Problem: Multiline messages exceeding 'cmdheight' not visible when a mapping starts cmdline immediately after it (after 9.2.0967). Solution: Revert patch 9.2.0967 and use a different solution (zeertzjq). fixes: vim/vim#21098 closes: vim/vim#21101 https://github.com/vim/vim/commit/fb4866a2dd58c4479418a9aea649db786e8750c6 --- src/nvim/message.c | 15 ++++++------- test/functional/legacy/messages_spec.lua | 26 ++++++++++++++++++----- test/old/testdir/test_messages.vim | 27 ++++++++++++++++++------ 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index d55b0f65ec..4394983cce 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1453,12 +1453,10 @@ void wait_return(int redraw) c = CAR; // just pretend CR was hit quit_more = false; got_int = false; - } else if (!stuff_empty() || !typebuf_typed()) { - // When there are stuffed characters or pending mapped characters, the - // next character will dismiss the hit-enter prompt immediately. A - // stuffed character then has to be put back, while a mapped character - // may even be swallowed (e.g. "g" treated as a message-scrollback key), - // so instead just don't show the hit-enter prompt at all. + } else if (!stuff_empty()) { + // When there are stuffed characters, the next stuffed character will + // dismiss the hit-enter prompt immediately and have to be put back, so + // instead just don't show the hit-enter prompt at all. c = CAR; } else { State = MODE_HITRETURN; @@ -1512,7 +1510,7 @@ void wait_return(int redraw) // Also accept scroll-down commands when messages fill the screen, // to avoid that typing one 'j' too many makes the messages // disappear. - if (p_more) { + if (KeyTyped && p_more) { if (c == 'b' || c == Ctrl_B || c == 'k' || c == 'u' || c == 'g' || c == K_UP || c == K_PAGEUP) { if (msg_scrolled > Rows) { @@ -1551,7 +1549,8 @@ void wait_return(int redraw) if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE || c == K_X1MOUSE || c == K_X2MOUSE) { jump_to_mouse(MOUSE_SETPOS, NULL, 0); - } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C && c != 'q') { + } else if (!KeyTyped + || (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C && c != 'q')) { // Put the character back in the typeahead buffer. Don't use the // stuff buffer, because lmaps wouldn't work. requeue_key(vgetc_char, vgetc_mod_mask, 0, diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua index 8a359dac76..4a125a123b 100644 --- a/test/functional/legacy/messages_spec.lua +++ b/test/functional/legacy/messages_spec.lua @@ -889,15 +889,15 @@ describe('messages', function() ]]) end) - -- oldtest: Test_hit_enter_no_eat_mapped_keys() - it('hit-enter prompt does not eat keys from a mapping', function() + -- oldtest: Test_hit_enter_during_mapping() + it('hit-enter prompt during a mapping', function() screen = Screen.new(75, 10) exec([[ - set ruler more + set ruler call setline(1, range(1, 20)) - " The 8-line :echo scrolls the screen and would raise a hit-enter prompt; - " the mapping then runs "gg" to move the cursor to line 1. + " The 8-line :echo leads to a hit-enter prompt. nnoremap X :echo "a\nb\nc\nd\ne\nf\ng\nh"gg + nnoremap \b :echo "a\nb\nc\nd\ne\nf\ng\nh":b normal! 10G ]]) t.eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) @@ -908,6 +908,22 @@ describe('messages', function() -- cursor stays put. With the fix "gg" runs and moves the cursor to line 1. t.eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) t.eq({ 1, 0 }, api.nvim_win_get_cursor(0)) + + -- If a mapping starts cmdline after multiline messages exceeding 'cmdheight', + -- the messages should still be visible. + feed('\\b') + screen:expect([[ + {3: }| + a | + b | + c | + d | + e | + f | + g | + h | + :b ^ | + ]]) end) -- oldtest: Test_fileinfo_after_last_bd() diff --git a/test/old/testdir/test_messages.vim b/test/old/testdir/test_messages.vim index bd4a6df075..dc5274d9e4 100644 --- a/test/old/testdir/test_messages.vim +++ b/test/old/testdir/test_messages.vim @@ -758,29 +758,42 @@ func Test_long_formatprg_no_hit_enter() call StopVimInTerminal(buf) endfunc -" A message shown while a mapping is still being processed must not raise a -" hit-enter prompt that eats the mapping's remaining keys. -func Test_hit_enter_no_eat_mapped_keys() +" A message shown while a mapping is still being processed must not eat the +" mapping's remaining keys. +" If a mapping starts cmdline after multiline messages exceeding 'cmdheight', +" the messages should still be visible. +func Test_hit_enter_during_mapping() CheckRunVimInTerminal let lines =<< trim END set ruler call setline(1, range(1, 20)) - " The 8-line :echo scrolls the screen and would raise a hit-enter prompt; - " the mapping then runs "gg" to move the cursor to line 1. + " The 8-line :echo leads to a hit-enter prompt. nnoremap X :echo "a\nb\nc\nd\ne\nf\ng\nh"gg + nnoremap \b :echo "a\nb\nc\nd\ne\nf\ng\nh":b normal! 10G END call writefile(lines, 'XtestHitEnterMap', 'D') let buf = RunVimInTerminal('-S XtestHitEnterMap', #{rows: 10}) - call WaitForAssert({-> assert_match('10,1', term_getline(buf, 10))}) + call WaitForAssert({-> assert_match(' 10,1 ', term_getline(buf, 10))}) call term_sendkeys(buf, "X") " Without the fix the hit-enter prompt eats the mapping's "g" keys and the " cursor stays put. With the fix "gg" runs and moves the cursor to line 1. - call WaitForAssert({-> assert_match('1,1', term_getline(buf, 10))}) + call WaitForAssert({-> assert_match(' 1,1 ', term_getline(buf, 10))}) call assert_notmatch('Press ENTER', term_getline(buf, 10)) + call term_sendkeys(buf, '\b') + call WaitForAssert({-> assert_match('^:b ', term_getline(buf, 10))}) + call assert_match('^a *$', term_getline(buf, 2)) + call assert_match('^b *$', term_getline(buf, 3)) + call assert_match('^c *$', term_getline(buf, 4)) + call assert_match('^d *$', term_getline(buf, 5)) + call assert_match('^e *$', term_getline(buf, 6)) + call assert_match('^f *$', term_getline(buf, 7)) + call assert_match('^g *$', term_getline(buf, 8)) + call assert_match('^h *$', term_getline(buf, 9)) + " clean up call StopVimInTerminal(buf) endfunc