From a56f4d221e64c2f2e46b52df6f7a1652f6b01f0f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 26 Jul 2026 06:50:25 +0800 Subject: [PATCH] vim-patch:partial:9.2.0806: 'showcmd' may show internal command keys Problem: The `showcmd` statusline item may show internal command keys when a `` or `` mapping redraws the statusline, and may leave stale text behind when `%S` is rendered directly. Solution: Do not add these internal mapping dispatch keys to the `showcmd` buffer, and keep the clear state in sync when `%S` renders it (Barrett Ruth) closes: vim/vim#20769 https://github.com/vim/vim/commit/bd730293dccaa166cd99321be674cb059d498b73 Co-authored-by: Barrett Ruth --- src/nvim/normal.c | 8 +++++- src/nvim/normal.h | 1 - src/nvim/statusline.c | 4 +-- test/old/testdir/test_statusline.vim | 41 ++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index ffdaa8166d..ef3d25a7ce 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1851,6 +1851,7 @@ void may_clear_cmdline(void) // Routines for displaying a partly typed command static char old_showcmd_buf[SHOWCMD_BUFLEN]; // For push_showcmd() +static bool showcmd_is_clear = true; static bool showcmd_visual = false; void clear_showcmd(void) @@ -2043,9 +2044,14 @@ void pop_showcmd(void) display_showcmd(); } -static void display_showcmd(void) +void showcmd_update_clear_state(void) { showcmd_is_clear = (showcmd_buf[0] == NUL); +} + +static void display_showcmd(void) +{ + showcmd_update_clear_state(); if (*p_sloc == 's') { if (showcmd_is_clear) { diff --git a/src/nvim/normal.h b/src/nvim/normal.h index c4881d906a..125bc7ad62 100644 --- a/src/nvim/normal.h +++ b/src/nvim/normal.h @@ -16,6 +16,5 @@ enum { /// 'showcmd' buffer shared between normal.c and statusline.c EXTERN char showcmd_buf[SHOWCMD_BUFLEN]; -EXTERN bool showcmd_is_clear INIT( = true); #include "normal.h.generated.h" diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 2d822c258d..b6f3553857 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -427,7 +427,7 @@ static void win_redr_stl_expr(win_T *wp, bool draw_winbar, bool draw_ruler, bool } if (p_sc && find_option(p_sloc) == opt_idx) { - showcmd_is_clear = (showcmd_buf[0] == NUL); + showcmd_update_clear_state(); } if (ui_event) { @@ -821,7 +821,7 @@ void draw_tabline(void) grid_line_puts(Columns - sc_width - (tabcount > 1) * 2, showcmd_buf, sc_width, attr_nosel); } - showcmd_is_clear = (showcmd_buf[0] == NUL); + showcmd_update_clear_state(); } // Put an "X" for closing the current tab if there are several. diff --git a/test/old/testdir/test_statusline.vim b/test/old/testdir/test_statusline.vim index 6d6b8818f7..e53f281131 100644 --- a/test/old/testdir/test_statusline.vim +++ b/test/old/testdir/test_statusline.vim @@ -667,6 +667,47 @@ func Test_statusline_showcmd() call StopVimInTerminal(buf) endfunc +func Test_statusline_showcmd_redraw_tabline() + CheckRunVimInTerminal + + let lines =<< trim END + set showcmd showcmdloc=tabline showtabline=2 tabline=%S timeoutlen=0 + nnoremap g :redraw + nnoremap gc + END + call writefile(lines, 'XTest_statusline_showcmd_redraw', 'D') + + let buf = RunVimInTerminal('-S XTest_statusline_showcmd_redraw', #{rows: 6, cols: 40}) + call term_sendkeys(buf, 'g') + call WaitForAssert({-> assert_match(':redraw', term_getline(buf, 6))}) + call WaitForAssert({-> assert_notmatch('^:', term_getline(buf, 1))}) + call StopVimInTerminal(buf) +endfunc + +func Test_statusline_showcmd_cmd_mapping() + set showcmd + set statusline=AAA%SBBB + set showcmdloc=statusline + try + let g:showcmd_statusline = '' + nnoremap let g:showcmd_statusline = get_statusline() + call feedkeys("\", 'xt') + call assert_equal('AAABBB', trim(g:showcmd_statusline)) + let g:showcmd_statusline = '' + nunmap + + "nnoremap let g:showcmd_statusline = get_statusline() + "call feedkeys("\", 'xt') + "call assert_equal('AAABBB', trim(g:showcmd_statusline)) + finally + silent! nunmap + unlet! g:showcmd_statusline + set showcmd& + set statusline& + set showcmdloc& + endtry +endfunc + func Test_statusline_highlight_group_cleared() CheckScreendump