patch 9.2.1028: redraw: cursor is left in the wrong place with an operator pending
Commit:
https://github.com/vim/vim/commit/260712df339175f9ee8d3804a9c8058af93ece1b
Author: Hirohito Higashi <
h.eas...@gmail.com>
Date: Tue Sep 1 19:19:10 2026 +0000
patch 9.2.1028: redraw: cursor is left in the wrong place with an operator pending
Problem: With an operator pending, a :redraw from an autocommand or a
callback leaves the cursor where the drawing ended, so it is
seen in the wrong place until the operator is finished. A
popup closed from such an autocommand shows this.
Solution: Put the cursor back after the screen is updated. This has to
happen before RedrawingDisabled is restored, which an
autocommand may have set, since setcursor() does nothing then.
closes: #21199
Co-Authored-By: Claude Opus 5 (1M context) <
nor...@anthropic.com>
Signed-off-by: Hirohito Higashi <
h.eas...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index c7343d290..962065bed 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -9043,6 +9043,8 @@ redraw_cmd(int clear)
validate_cursor();
update_topline();
update_screen(clear ? UPD_CLEAR : VIsual_active ? UPD_INVERTED : 0);
+ if ((State & MODE_CMDLINE) == 0)
+ setcursor(); // put cursor back where it belongs
if (need_maketitle)
maketitle();
#if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index 160542352..b43bbae66 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -6376,4 +6376,30 @@ func Test_popup_no_filter_at_hit_enter()
call StopVimInTerminal(buf)
endfunc
+func Test_popupwin_close_and_redraw_keeps_cursor()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ call setline(1, repeat(['some text'], 8))
+ call cursor(3, 2)
+ let g:id = popup_atcursor(['a popup'], #{moved: 'any'})
+ func CloseIt()
+ call popup_close(g:id)
+ redraw
+ endfunc
+ autocmd ModeChanged * ++once call CloseIt()
+ END
+ call writefile(lines, 'XtestPopupCursor', 'D')
+ let buf = RunVimInTerminal('-S XtestPopupCursor', #{rows: 10})
+ call WaitForAssert({-> assert_equal([3, 2], term_getcursor(buf)[0:1])})
+
+ " With the operator waiting, nothing after the redraw puts the cursor back.
+ call term_sendkeys(buf, "c")
+ call TermWait(buf, 100)
+ call assert_equal([3, 2], term_getcursor(buf)[0:1])
+
+ call term_sendkeys(buf, "\<Esc>")
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2
diff --git a/src/version.c b/src/version.c
index 5b0cc9e0c..b4438d279 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1028,
/**/
1027,
/**/