patch 9.2.0765: popup: opacity popup over a terminal is not cleared when moved
Commit:
https://github.com/vim/vim/commit/8819c1514003dbe9488d8526d5517860f0be9a1d
Author: Hirohito Higashi <
h.eas...@gmail.com>
Date: Thu Jul 2 17:59:24 2026 +0000
patch 9.2.0765: popup: opacity popup over a terminal is not cleared when moved
Problem: A semi-transparent (opacity) popup shown over a terminal window
leaves its old cells on screen when it is moved or its text
changes, and typing in the terminal makes them accumulate. Only
a full redraw (CTRL-L) clears them.
Solution: When redrawing the background under an opacity popup, force a full
repaint of an underlying terminal window so the blend uses the
terminal's true background instead of stale, already-blended
cells.
fixes: #20679
closes: #20688
Co-Authored-By: Claude Opus 4.8 (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/popupwin.c b/src/popupwin.c
index 849849f82..96fa7895a 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -6132,7 +6132,7 @@ redraw_win_under_opacity_popup(win_T *wp)
// Check across the full width of the popup to find all underlying
// windows (e.g., when the popup spans a vertical split).
for (col = wp->w_wincol;
- col < wp->w_wincol + width && col < screen_Columns; ++col)
+ col < wp->w_wincol + width && col < screen_Columns; ++col)
{
int line_cp = r;
int col_cp = col;
@@ -6146,10 +6146,21 @@ redraw_win_under_opacity_popup(win_T *wp)
{
linenr_T lnum;
- (void)mouse_comp_pos(twp, &line_cp, &col_cp, &lnum, NULL);
- // Called from inside update_screen(); raising must_redraw
- // would loop the outer redraw indefinitely.
- redraw_win_range_now(twp, lnum, lnum);
+#ifdef FEAT_TERMINAL
+ // A terminal only repaints vterm-damaged rows; force a
+ // full repaint so the opacity blend sees the true
+ // background, not stale blended cells.
+ if (term_do_update_window(twp))
+ twp->w_redr_type = UPD_NOT_VALID;
+ else
+#endif
+ {
+ (void)mouse_comp_pos(twp, &line_cp, &col_cp, &lnum,
+ NULL);
+ // Called from inside update_screen(); raising
+ // must_redraw would loop the outer redraw indefinitely.
+ redraw_win_range_now(twp, lnum, lnum);
+ }
}
else if (line_cp == twp->w_height)
// Status bar line: mark for redraw to prevent
diff --git a/src/testdir/dumps/Test_popupwin_opacity_term_move_1.dump b/src/testdir/dumps/Test_popupwin_opacity_term_move_1.dump
new file mode 100644
index 000000000..46714d08b
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_opacity_term_move_1.dump
@@ -0,0 +1,12 @@
+|v+0&#ffffff0|i|m|>| > @69
+@75
+@75
+@75
+@9|A+0#0000001#ffffff255|B|C| +0#0000000#ffffff0@62
+@75
+@75
+@75
+@75
+@75
+@75
+@75
diff --git a/src/testdir/dumps/Test_popupwin_opacity_term_move_2.dump b/src/testdir/dumps/Test_popupwin_opacity_term_move_2.dump
new file mode 100644
index 000000000..9a9b0f5b2
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_opacity_term_move_2.dump
@@ -0,0 +1,12 @@
+|v+0&#ffffff0|i|m|>| > @69
+@75
+@75
+@75
+@12|X+0#0000001#ffffff255|Y|Z| +0#0000000#ffffff0@59
+@75
+@75
+@75
+@75
+@75
+@75
+|:|c|a|l@1| |M|o|v|e|I|t|(|)| @60
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index 79f8dac39..5e4bc5221 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -5315,6 +5315,43 @@ func Test_popup_opacity_settext_no_leftover()
call StopVimInTerminal(buf)
endfunc
+func Test_popup_opacity_terminal_move_no_leftover()
+ CheckScreendump
+ CheckFeature terminal
+ CheckUnix
+
+ " A semi-transparent popup over a terminal used to leave the old popup
+ " cells behind when it moved.
+ let lines =<< trim END
+ set shell=/bin/sh noruler
+ unlet $PROMPT_COMMAND
+ let $PS1 = 'vim> '
+ terminal ++curwin
+ call popup_create('ABC',
+ \ #{line: 5, col: 10, highlight: 'None', opacity: 30})
+ func MoveIt()
+ let id = popup_list()[0]
+ call popup_settext(id, 'XYZ')
+ call popup_setoptions(id, #{col: popup_getpos(id).col + 3})
+ endfunc
+ END
+ call writefile(lines, 'XtestPopupOpacityTermMove', 'D')
+ let buf = RunVimInTerminal('-S XtestPopupOpacityTermMove',
+ \ #{rows: 12, wait_for_ruler: 0})
+ call WaitForAssert({-> assert_match('ABC', term_getline(buf, 5))})
+ call VerifyScreenDump(buf, 'Test_popupwin_opacity_term_move_1', {})
+
+ " Move the popup and change its text: the old "ABC" cells must be cleared.
+ call term_sendkeys(buf, "\<C-W>:call MoveIt()\<CR>")
+ call WaitForAssert({-> assert_match('XYZ', term_getline(buf, 5))})
+ call VerifyScreenDump(buf, 'Test_popupwin_opacity_term_move_2', {})
+
+ " clean up
+ call term_sendkeys(buf, "\<C-W>:qa!\<CR>")
+ call WaitForAssert({-> assert_equal("finished", term_getstatus(buf))})
+ exe buf .. 'bwipe!'
+endfunc
+
func Test_popup_opacity_terminal_no_freeze()
CheckFeature terminal
CheckUnix
diff --git a/src/version.c b/src/version.c
index 09f4dc2c4..dd14dedba 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 */
+/**/
+ 765,
/**/
764,
/**/