Patch 8.2.2972
Problem:    "%bd" tries to delete popup window buffers, which fails. (Ralf
            Schandl)
Solution:   Do not try to delete a popup window buffer. (closes #8349)
Files:      src/buffer.c, src/vim.h, src/testdir/test_popupwin.vim


*** ../vim-8.2.2971/src/buffer.c        2021-05-15 17:23:22.882858583 +0200
--- src/buffer.c        2021-06-10 21:06:17.219219954 +0200
***************
*** 1181,1302 ****
  }
  
  /*
-  * do_bufdel() - delete or unload buffer(s)
-  *
-  * addr_count == 0: ":bdel" - delete current buffer
-  * addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete
-  *                buffer "end_bnr", then any other arguments.
-  * addr_count == 2: ":N,N bdel" - delete buffers in range
-  *
-  * command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or
-  * DOBUF_DEL (":bdel")
-  *
-  * Returns error message or NULL
-  */
-     char *
- do_bufdel(
-     int               command,
-     char_u    *arg,           // pointer to extra arguments
-     int               addr_count,
-     int               start_bnr,      // first buffer number in a range
-     int               end_bnr,        // buffer nr or last buffer nr in a 
range
-     int               forceit)
- {
-     int               do_current = 0; // delete current buffer?
-     int               deleted = 0;    // number of buffers deleted
-     char      *errormsg = NULL; // return value
-     int               bnr;            // buffer number
-     char_u    *p;
- 
-     if (addr_count == 0)
-     {
-       (void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
-     }
-     else
-     {
-       if (addr_count == 2)
-       {
-           if (*arg)           // both range and argument is not allowed
-               return ex_errmsg(e_trailing_arg, arg);
-           bnr = start_bnr;
-       }
-       else    // addr_count == 1
-           bnr = end_bnr;
- 
-       for ( ;!got_int; ui_breakcheck())
-       {
-           /*
-            * delete the current buffer last, otherwise when the
-            * current buffer is deleted, the next buffer becomes
-            * the current one and will be loaded, which may then
-            * also be deleted, etc.
-            */
-           if (bnr == curbuf->b_fnum)
-               do_current = bnr;
-           else if (do_buffer(command, DOBUF_FIRST, FORWARD, (int)bnr,
-                                                              forceit) == OK)
-               ++deleted;
- 
-           /*
-            * find next buffer number to delete/unload
-            */
-           if (addr_count == 2)
-           {
-               if (++bnr > end_bnr)
-                   break;
-           }
-           else    // addr_count == 1
-           {
-               arg = skipwhite(arg);
-               if (*arg == NUL)
-                   break;
-               if (!VIM_ISDIGIT(*arg))
-               {
-                   p = skiptowhite_esc(arg);
-                   bnr = buflist_findpat(arg, p,
-                         command == DOBUF_WIPE || command == DOBUF_WIPE_REUSE,
-                                                               FALSE, FALSE);
-                   if (bnr < 0)            // failed
-                       break;
-                   arg = p;
-               }
-               else
-                   bnr = getdigits(&arg);
-           }
-       }
-       if (!got_int && do_current && do_buffer(command, DOBUF_FIRST,
-                                         FORWARD, do_current, forceit) == OK)
-           ++deleted;
- 
-       if (deleted == 0)
-       {
-           if (command == DOBUF_UNLOAD)
-               STRCPY(IObuff, _("E515: No buffers were unloaded"));
-           else if (command == DOBUF_DEL)
-               STRCPY(IObuff, _("E516: No buffers were deleted"));
-           else
-               STRCPY(IObuff, _("E517: No buffers were wiped out"));
-           errormsg = (char *)IObuff;
-       }
-       else if (deleted >= p_report)
-       {
-           if (command == DOBUF_UNLOAD)
-               smsg(NGETTEXT("%d buffer unloaded",
-                           "%d buffers unloaded", deleted), deleted);
-           else if (command == DOBUF_DEL)
-               smsg(NGETTEXT("%d buffer deleted",
-                           "%d buffers deleted", deleted), deleted);
-           else
-               smsg(NGETTEXT("%d buffer wiped out",
-                           "%d buffers wiped out", deleted), deleted);
-       }
-     }
- 
- 
-     return errormsg;
- }
- 
- /*
   * Make the current buffer empty.
   * Used when it is wiped out and it's the last buffer.
   */
--- 1181,1186 ----
***************
*** 1354,1366 ****
   *
   * Return FAIL or OK.
   */
!     int
! do_buffer(
      int               action,
      int               start,
      int               dir,            // FORWARD or BACKWARD
      int               count,          // buffer number or number of buffers
!     int               forceit)        // TRUE for :...!
  {
      buf_T     *buf;
      buf_T     *bp;
--- 1238,1250 ----
   *
   * Return FAIL or OK.
   */
!     static int
! do_buffer_ext(
      int               action,
      int               start,
      int               dir,            // FORWARD or BACKWARD
      int               count,          // buffer number or number of buffers
!     int               flags)          // DOBUF_FORCEIT etc.
  {
      buf_T     *buf;
      buf_T     *bp;
***************
*** 1446,1451 ****
--- 1330,1343 ----
            emsg(_("E88: Cannot go before first buffer"));
        return FAIL;
      }
+ #ifdef FEAT_PROP_POPUP
+     if ((flags & DOBUF_NOPOPUP) && bt_popup(buf)
+ # ifdef FEAT_TERMINAL
+                               && !bt_terminal(buf)
+ #endif
+        )
+       return OK;
+ #endif
  
  #ifdef FEAT_GUI
      need_mouse_correct = TRUE;
***************
*** 1470,1476 ****
                                   && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
            return FAIL;
  
!       if (!forceit && bufIsChanged(buf))
        {
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
            if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
--- 1362,1368 ----
                                   && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
            return FAIL;
  
!       if ((flags & DOBUF_FORCEIT) == 0 && bufIsChanged(buf))
        {
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
            if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
***************
*** 1506,1512 ****
            if (bp->b_p_bl && bp != buf)
                break;
        if (bp == NULL && buf == curbuf)
!           return empty_curbuf(TRUE, forceit, action);
  
        /*
         * If the deleted buffer is the current one, close the current window
--- 1398,1404 ----
            if (bp->b_p_bl && bp != buf)
                break;
        if (bp == NULL && buf == curbuf)
!           return empty_curbuf(TRUE, (flags & DOBUF_FORCEIT), action);
  
        /*
         * If the deleted buffer is the current one, close the current window
***************
*** 1633,1639 ****
      {
        // Autocommands must have wiped out all other buffers.  Only option
        // now is to make the current buffer empty.
!       return empty_curbuf(FALSE, forceit, action);
      }
  
      /*
--- 1525,1531 ----
      {
        // Autocommands must have wiped out all other buffers.  Only option
        // now is to make the current buffer empty.
!       return empty_curbuf(FALSE, (flags & DOBUF_FORCEIT), action);
      }
  
      /*
***************
*** 1660,1666 ****
      /*
       * Check if the current buffer may be abandoned.
       */
!     if (action == DOBUF_GOTO && !can_abandon(curbuf, forceit))
      {
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
        if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
--- 1552,1558 ----
      /*
       * Check if the current buffer may be abandoned.
       */
!     if (action == DOBUF_GOTO && !can_abandon(curbuf, (flags & DOBUF_FORCEIT)))
      {
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
        if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
***************
*** 1695,1700 ****
--- 1587,1720 ----
      return OK;
  }
  
+     int
+ do_buffer(
+     int               action,
+     int               start,
+     int               dir,            // FORWARD or BACKWARD
+     int               count,          // buffer number or number of buffers
+     int               forceit)        // TRUE when using !
+ {
+     return do_buffer_ext(action, start, dir, count,
+                                                 forceit ? DOBUF_FORCEIT : 0);
+ }
+ 
+ /*
+  * do_bufdel() - delete or unload buffer(s)
+  *
+  * addr_count == 0: ":bdel" - delete current buffer
+  * addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete
+  *                buffer "end_bnr", then any other arguments.
+  * addr_count == 2: ":N,N bdel" - delete buffers in range
+  *
+  * command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or
+  * DOBUF_DEL (":bdel")
+  *
+  * Returns error message or NULL
+  */
+     char *
+ do_bufdel(
+     int               command,
+     char_u    *arg,           // pointer to extra arguments
+     int               addr_count,
+     int               start_bnr,      // first buffer number in a range
+     int               end_bnr,        // buffer nr or last buffer nr in a 
range
+     int               forceit)
+ {
+     int               do_current = 0; // delete current buffer?
+     int               deleted = 0;    // number of buffers deleted
+     char      *errormsg = NULL; // return value
+     int               bnr;            // buffer number
+     char_u    *p;
+ 
+     if (addr_count == 0)
+     {
+       (void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
+     }
+     else
+     {
+       if (addr_count == 2)
+       {
+           if (*arg)           // both range and argument is not allowed
+               return ex_errmsg(e_trailing_arg, arg);
+           bnr = start_bnr;
+       }
+       else    // addr_count == 1
+           bnr = end_bnr;
+ 
+       for ( ;!got_int; ui_breakcheck())
+       {
+           /*
+            * Delete the current buffer last, otherwise when the
+            * current buffer is deleted, the next buffer becomes
+            * the current one and will be loaded, which may then
+            * also be deleted, etc.
+            */
+           if (bnr == curbuf->b_fnum)
+               do_current = bnr;
+           else if (do_buffer_ext(command, DOBUF_FIRST, FORWARD, (int)bnr,
+                         DOBUF_NOPOPUP | (forceit ? DOBUF_FORCEIT : 0)) == OK)
+               ++deleted;
+ 
+           /*
+            * find next buffer number to delete/unload
+            */
+           if (addr_count == 2)
+           {
+               if (++bnr > end_bnr)
+                   break;
+           }
+           else    // addr_count == 1
+           {
+               arg = skipwhite(arg);
+               if (*arg == NUL)
+                   break;
+               if (!VIM_ISDIGIT(*arg))
+               {
+                   p = skiptowhite_esc(arg);
+                   bnr = buflist_findpat(arg, p,
+                         command == DOBUF_WIPE || command == DOBUF_WIPE_REUSE,
+                                                               FALSE, FALSE);
+                   if (bnr < 0)            // failed
+                       break;
+                   arg = p;
+               }
+               else
+                   bnr = getdigits(&arg);
+           }
+       }
+       if (!got_int && do_current && do_buffer(command, DOBUF_FIRST,
+                                         FORWARD, do_current, forceit) == OK)
+           ++deleted;
+ 
+       if (deleted == 0)
+       {
+           if (command == DOBUF_UNLOAD)
+               STRCPY(IObuff, _("E515: No buffers were unloaded"));
+           else if (command == DOBUF_DEL)
+               STRCPY(IObuff, _("E516: No buffers were deleted"));
+           else
+               STRCPY(IObuff, _("E517: No buffers were wiped out"));
+           errormsg = (char *)IObuff;
+       }
+       else if (deleted >= p_report)
+       {
+           if (command == DOBUF_UNLOAD)
+               smsg(NGETTEXT("%d buffer unloaded",
+                           "%d buffers unloaded", deleted), deleted);
+           else if (command == DOBUF_DEL)
+               smsg(NGETTEXT("%d buffer deleted",
+                           "%d buffers deleted", deleted), deleted);
+           else
+               smsg(NGETTEXT("%d buffer wiped out",
+                           "%d buffers wiped out", deleted), deleted);
+       }
+     }
+ 
+ 
+     return errormsg;
+ }
+ 
  /*
   * Set current buffer to "buf".  Executes autocommands and closes current
   * buffer.  "action" tells how to close the current buffer:
*** ../vim-8.2.2971/src/vim.h   2021-06-08 20:13:27.359916592 +0200
--- src/vim.h   2021-06-10 20:56:16.236357694 +0200
***************
*** 994,999 ****
--- 994,1003 ----
  #define DOBUF_LAST    2       // "count" buffer from last buffer
  #define DOBUF_MOD     3       // "count" mod. buffer from current buffer
  
+ // Values for flags argument of do_buffer()
+ #define DOBUF_FORCEIT 1       // :cmd!
+ #define DOBUF_NOPOPUP 2       // skip popup window buffers
+ 
  // Values for sub_cmd and which_pat argument for search_regcomp()
  // Also used for which_pat argument for searchit()
  #define RE_SEARCH     0       // save/use pat in/from search_pattern
*** ../vim-8.2.2971/src/testdir/test_popupwin.vim       2021-05-28 
14:11:59.725797039 +0200
--- src/testdir/test_popupwin.vim       2021-06-10 21:05:23.419322877 +0200
***************
*** 3918,3922 ****
--- 3918,3928 ----
    call delete('XtestPropNotVisble')
  endfunction
  
+ func Test_bufdel_skips_popupwin_buffer()
+     let id = popup_create("Some text", {})
+     %bd
+     call popup_close(id)
+ endfunc
+ 
  
  " vim: shiftwidth=2 sts=2
*** ../vim-8.2.2971/src/version.c       2021-06-10 19:39:07.281697681 +0200
--- src/version.c       2021-06-10 21:06:56.383144943 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2972,
  /**/

-- 
If all you have is a hammer, everything looks like a nail.
When your hammer is C++, everything begins to look like a thumb.
                        -- Steve Hoflich, comp.lang.c++

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
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 on the web visit 
https://groups.google.com/d/msgid/vim_dev/202106101908.15AJ8Iaf1464115%40masaka.moolenaar.net.

Raspunde prin e-mail lui