patch 9.2.0477: popup: leftover content after popup_free under layout change

Commit: 
https://github.com/vim/vim/commit/3a9e1bb7e245f862f506b02845fc6132013ef6e7
Author: Yasuhiro Matsumoto <[email protected]>
Date:   Tue May 12 17:47:41 2026 +0000

    patch 9.2.0477: popup: leftover content after popup_free under layout change
    
    Problem:  popup_mask still marks the freed popup's cells as covered
              until may_update_popup_mask() runs inside the next
              update_screen.  Any screen_fill / screen_puts called in
              between (for example msg_clr_eos triggered by a status message
              from :copen) hits skip_for_popup() and silently drops writes
              to those cells, so the popup's chars survive on screen until
              those cells happen to be redrawn for another reason.
    Solution: Add popup_clear_mask_for() and call it from popup_hide() and
              popup_free() when the popup was visible, so the upcoming
              writes take effect immediately (Yasuhiro Matsumoto)
    
    Note: The test is limited to MS-Windows because the original report
          (#20178) was reproduced there and the redraw timing required to
          surface the bug differs on other platforms.
    
    fixes:  #20178
    closes: #20188
    
    Signed-off-by: Yasuhiro Matsumoto <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/src/popupwin.c b/src/popupwin.c
index 685c3cae7..0767fa913 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -3522,10 +3522,38 @@ f_popup_close(typval_T *argvars, typval_T *rettv UNUSED)
        popup_close_and_callback(wp, &argvars[1]);
 }
 
+/*
+ * Clear popup_mask entries for the cells covered by "wp" so that
+ * screen_fill / screen_puts calls made before the next update_screen()
+ * (e.g. msg_clr_eos triggered by a status message) are not silently
+ * dropped by skip_for_popup().  Without this the popup's chars survive
+ * on screen until may_update_popup_mask() runs and the affected cells
+ * happen to be redrawn.
+ */
+    static void
+popup_clear_mask_for(win_T *wp)
+{
+    int r, c;
+    int row_start, col_start, row_end, col_end;
+
+    if (popup_mask == NULL || !popup_visible)
+       return;
+
+    row_start = MAX(wp->w_winrow, 0);
+    col_start = MAX(wp->w_wincol, 0);
+    row_end = MIN(wp->w_winrow + popup_height(wp), (int)screen_Rows);
+    col_end = MIN(wp->w_wincol + popup_width(wp), (int)screen_Columns);
+
+    for (r = row_start; r < row_end; ++r)
+       for (c = col_start; c < col_end; ++c)
+           popup_mask[r * screen_Columns + c] = 0;
+}
+
     void
 popup_hide(win_T *wp)
 {
     popup_area_T       old_area;
+    int                        was_visible = (wp->w_popup_flags & POPF_HIDDEN) 
== 0;
 
 #ifdef FEAT_TERMINAL
     if (error_if_term_popup_window())
@@ -3541,6 +3569,9 @@ popup_hide(win_T *wp)
     if (wp->w_winrow + popup_height(wp) >= cmdline_row)
        clear_cmdline = TRUE;
 
+    if (was_visible)
+       popup_clear_mask_for(wp);
+
     if (old_area.active)
        popup_redraw_exposed_area(&old_area);
     else
@@ -3725,6 +3756,7 @@ f_popup_setbuf(typval_T *argvars, typval_T *rettv UNUSED)
 popup_free(win_T *wp)
 {
     popup_area_T       old_area;
+    int                        was_visible = (wp->w_popup_flags & POPF_HIDDEN) 
== 0;
 
     popup_save_area(wp, &old_area);
 
@@ -3733,6 +3765,9 @@ popup_free(win_T *wp)
     if (wp->w_winrow + popup_height(wp) >= cmdline_row)
        clear_cmdline = TRUE;
 
+    if (was_visible)
+       popup_clear_mask_for(wp);
+
     popup_redraw_exposed_area(&old_area);
 
     win_free_popup(wp);
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index 873840d8e..56cce4fb4 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -5408,4 +5408,52 @@ func Test_popupwin_close_status_redraw()
   call StopVimInTerminal(buf)
 endfunc
 
+func Test_popupwin_close_copen_redraw()
+  CheckMSWindows
+  CheckFeature quickfix
+
+  func! s:OpenPopup()
+    call popup_create(repeat(['ZZZZZZZZZ'], 10), #{
+          \ pos: 'botright',
+          \ col: &columns,
+          \ line: &lines,
+          \ filter: function('s:PopupFilter'),
+          \ })
+  endfunc
+  func! s:PopupFilter(winid, key)
+    if a:key ==# 'q'
+      call popup_close(a:winid)
+      copen
+    endif
+    return 1
+  endfunc
+
+  enew!
+  call setline(1, range(1, 30))
+  call setqflist(map(range(1, 20),
+        \ {_, v -> {'bufnr': bufnr('%'), 'lnum': v, 'col': 1, 'text': 'item ' 
.. v}}))
+
+  call s:OpenPopup()
+  redraw
+  call feedkeys('q', 'xt')
+  redraw
+  cclose
+  redraw
+
+  call s:OpenPopup()
+  redraw
+  call feedkeys('q', 'xt')
+  redraw
+
+  for row in range(&lines - 9, &lines)
+    let line = join(map(range(1, &columns), 'screenstring(row, v:val)'), '')
+    call assert_notmatch('Z', line)
+  endfor
+
+  cclose
+  bwipe!
+  delfunc s:OpenPopup
+  delfunc s:PopupFilter
+endfunc
+
 " vim: shiftwidth=2 sts=2
diff --git a/src/version.c b/src/version.c
index 6a8735c6b..d52a2c468 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    477,
 /**/
     476,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1wMrOp-001Sza-6m%40256bit.org.

Raspunde prin e-mail lui