From: Barrett Ruth
Date: Sun, 19 Jul 2026 14:53:55 +0000 (+0000)
Subject: patch 9.2.0806: 'showcmd' may show internal command keys
X-Git-Tag: v9.2.0806^0
X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=bd730293dccaa166cd99321be674cb059d498b73;p=thirdparty%2Fvim.git
patch 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: #20769
Co-authored-by: zeertzjq <35768171+zeertzjq@users.noreply.github.com>
Signed-off-by: Barrett Ruth
Signed-off-by: Christian Brabandt
---
diff --git a/src/drawscreen.c b/src/drawscreen.c
index 65d6a88d31..2c932d4ba7 100644
--- a/src/drawscreen.c
+++ b/src/drawscreen.c
@@ -620,6 +620,7 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED)
if (width > 0)
screen_puts_len(showcmd_buf, width, row,
wp->w_wincol + this_ru_col - width - 1, attr);
+ showcmd_update_clear_state();
}
}
diff --git a/src/normal.c b/src/normal.c
index 08b9e9f0cf..d5de6abe51 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -1739,7 +1739,7 @@ add_to_showcmd(int c)
K_RIGHTMOUSE, K_RIGHTDRAG, K_RIGHTRELEASE,
K_MOUSEDOWN, K_MOUSEUP, K_MOUSELEFT, K_MOUSERIGHT,
K_X1MOUSE, K_X1DRAG, K_X1RELEASE, K_X2MOUSE, K_X2DRAG, K_X2RELEASE,
- K_CURSORHOLD,
+ K_CURSORHOLD, K_COMMAND, K_SCRIPT_COMMAND,
0
};
@@ -1834,12 +1834,18 @@ pop_showcmd(void)
display_showcmd();
}
+ void
+showcmd_update_clear_state(void)
+{
+ showcmd_is_clear = (showcmd_buf[0] == NUL);
+}
+
static void
display_showcmd(void)
{
int len = vim_strsize(showcmd_buf);
- showcmd_is_clear = (len == 0);
+ showcmd_update_clear_state();
cursor_off();
if (*p_sloc == 's')
diff --git a/src/proto/normal.pro b/src/proto/normal.pro
index cf873f20bb..6f45b1a3ad 100644
--- a/src/proto/normal.pro
+++ b/src/proto/normal.pro
@@ -19,6 +19,7 @@ int add_to_showcmd(int c);
void add_to_showcmd_c(int c);
void push_showcmd(void);
void pop_showcmd(void);
+void showcmd_update_clear_state(void);
void do_check_scrollbind(int check);
void check_scrollbind(linenr_T topline_diff, long leftcol_diff);
int find_decl(char_u *ptr, int len, int locally, int thisblock, int flags_arg);
diff --git a/src/screen.c b/src/screen.c
index 17d4c1563c..07a237b0c6 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1676,6 +1676,9 @@ win_redr_custom(
}
ewp->w_p_crb = p_crb_save;
+ if (p_sc && STRCMP(opt_name, p_sloc) == 0)
+ showcmd_update_clear_state();
+
// Note: In the loop, build_stl_str_hl_mline() may replace stl_tmp with
// a newly allocated buffer (when "%!" evaluation occurs), freeing the
// original "stl" internally. After the loop, stl_tmp must be freed
@@ -5317,6 +5320,7 @@ draw_tabline(void)
if (width > 0)
screen_puts_len(showcmd_buf, width, 0, (int)Columns
- width - (tabcount > 1) * 2, attr_nosel);
+ showcmd_update_clear_state();
}
// Put an "X" for closing the current tab if there are several.
diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim
index c00582d5fd..69699a2d4d 100644
--- a/src/testdir/test_statusline.vim
+++ b/src/testdir/test_statusline.vim
@@ -657,6 +657,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
diff --git a/src/version.c b/src/version.c
index 4b9c6318d6..712e652ee9 100644
--- a/src/version.c
+++ b/src/version.c
@@ -759,6 +759,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 806,
/**/
805,
/**/