Patch 9.0.0708
Problem:    :confirm does not work properly for a terminal buffer.
Solution:   Handle :confirm for a terminal buffer differently.  (Yee Cheng
            Chin, closes #11312)
Files:      runtime/menu.vim, src/buffer.c, src/ex_cmds2.c, src/ex_docmd.c,
            src/proto/terminal.pro, src/terminal.c,
            src/testdir/test_terminal.vim


*** ../vim-9.0.0707/runtime/menu.vim    2022-03-02 21:25:35.000000000 +0000
--- runtime/menu.vim    2022-10-09 18:42:05.491666766 +0100
***************
*** 129,134 ****
--- 129,140 ----
        \ else <Bar>
        \   confirm close <Bar>
        \ endif<CR>
+ tln <silent> 10.330 &File.&Close<Tab>:close
+       \ <C-W>:if winheight(2) < 0 && tabpagewinnr(2) == 0 <Bar>
+       \   confirm enew <Bar>
+       \ else <Bar>
+       \   confirm close <Bar>
+       \ endif<CR>
  an 10.335 &File.-SEP1-                                <Nop>
  an <silent> 10.340 &File.&Save<Tab>:w         :if expand("%") == 
""<Bar>browse confirm w<Bar>else<Bar>confirm w<Bar>endif<CR>
  an 10.350 &File.Save\ &As\.\.\.<Tab>:sav      :browse confirm saveas<CR>
*** ../vim-9.0.0707/src/buffer.c        2022-10-01 19:43:48.602494034 +0100
--- src/buffer.c        2022-10-09 18:42:05.491666766 +0100
***************
*** 49,54 ****
--- 49,55 ----
  static void   free_buffer(buf_T *);
  static void   free_buffer_stuff(buf_T *buf, int free_options);
  static int    bt_nofileread(buf_T *buf);
+ static void   no_write_message_buf(buf_T *buf);
  
  #ifdef UNIX
  # define dev_T dev_t
***************
*** 1367,1387 ****
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
            if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
            {
!               dialog_changed(buf, FALSE);
!               if (!bufref_valid(&bufref))
!                   // Autocommand deleted buffer, oops!  It's not changed
!                   // now.
!                   return FAIL;
!               // If it's still changed fail silently, the dialog already
!               // mentioned why it fails.
!               if (bufIsChanged(buf))
!                   return FAIL;
            }
            else
  #endif
            {
!               
semsg(_(e_no_write_since_last_change_for_buffer_nr_add_bang_to_override),
!                                                                buf->b_fnum);
                return FAIL;
            }
        }
--- 1368,1397 ----
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
            if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
            {
! # ifdef FEAT_TERMINAL
!               if (term_job_running(buf->b_term))
!               {
!                   if (term_confirm_stop(buf) == FAIL)
!                       return FAIL;
!               }
!               else
! # endif
!               {
!                   dialog_changed(buf, FALSE);
!                   if (!bufref_valid(&bufref))
!                       // Autocommand deleted buffer, oops!  It's not changed
!                       // now.
!                       return FAIL;
!                   // If it's still changed fail silently, the dialog already
!                   // mentioned why it fails.
!                   if (bufIsChanged(buf))
!                       return FAIL;
!               }
            }
            else
  #endif
            {
!               no_write_message_buf(buf);
                return FAIL;
            }
        }
***************
*** 1552,1566 ****
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
        if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
        {
!           bufref_T bufref;
  
!           set_bufref(&bufref, buf);
!           dialog_changed(curbuf, FALSE);
!           if (!bufref_valid(&bufref))
!               // Autocommand deleted buffer, oops!
!               return FAIL;
        }
!       if (bufIsChanged(curbuf))
  #endif
        {
            no_write_message();
--- 1562,1595 ----
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
        if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
        {
! # ifdef FEAT_TERMINAL
!           if (term_job_running(curbuf->b_term))
!           {
!               if (term_confirm_stop(curbuf) == FAIL)
!                   return FAIL;
!               // Manually kill the terminal here because this command will
!               // hide it otherwise.
!               free_terminal(curbuf);
!           }
!           else
! # endif
!           {
!               bufref_T bufref;
  
!               set_bufref(&bufref, buf);
!               dialog_changed(curbuf, FALSE);
!               if (!bufref_valid(&bufref))
!                   // Autocommand deleted buffer, oops!
!                   return FAIL;
! 
!               if (bufIsChanged(curbuf))
!               {
!                   no_write_message();
!                   return FAIL;
!               }
!           }
        }
!       else
  #endif
        {
            no_write_message();
***************
*** 1940,1945 ****
--- 1969,1986 ----
  }
  #endif
  
+     static void
+ no_write_message_buf(buf_T *buf UNUSED)
+ {
+ #ifdef FEAT_TERMINAL
+     if (term_job_running(buf->b_term))
+       emsg(_(e_job_still_running_add_bang_to_end_the_job));
+     else
+ #endif
+       
semsg(_(e_no_write_since_last_change_for_buffer_nr_add_bang_to_override),
+               buf->b_fnum);
+ }
+ 
      void
  no_write_message(void)
  {
***************
*** 5751,5758 ****
  #endif
  
  /*
!  * Return TRUE if "buf" is a "nowrite", "nofile", "terminal" or "prompt"
!  * buffer.
   */
      int
  bt_dontwrite(buf_T *buf)
--- 5792,5799 ----
  #endif
  
  /*
!  * Return TRUE if "buf" is a "nowrite", "nofile", "terminal", "prompt", or
!  * "popup" buffer.
   */
      int
  bt_dontwrite(buf_T *buf)
*** ../vim-9.0.0707/src/ex_cmds2.c      2022-08-29 15:06:46.716715543 +0100
--- src/ex_cmds2.c      2022-10-09 18:42:05.491666766 +0100
***************
*** 86,91 ****
--- 86,98 ----
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
        if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
        {
+ # ifdef FEAT_TERMINAL
+           if (term_job_running(buf->b_term))
+           {
+               return term_confirm_stop(buf) == FAIL;
+           }
+ # endif
+ 
            buf_T       *buf2;
            int         count = 0;
  
***************
*** 198,203 ****
--- 205,211 ----
                        || (cmdmod.cmod_flags & CMOD_BROWSE)
  #endif
                        )
+                   && !bt_dontwrite(buf2)
                    && !buf2->b_p_ro)
            {
                bufref_T bufref;
*** ../vim-9.0.0707/src/ex_docmd.c      2022-10-07 18:51:20.139679475 +0100
--- src/ex_docmd.c      2022-10-09 18:42:05.491666766 +0100
***************
*** 6061,6073 ****
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
        if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
        {
!           bufref_T bufref;
  
!           set_bufref(&bufref, buf);
!           dialog_changed(buf, FALSE);
!           if (bufref_valid(&bufref) && bufIsChanged(buf))
!               return;
!           need_hide = FALSE;
        }
        else
  #endif
--- 6061,6087 ----
  #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
        if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
        {
! # ifdef FEAT_TERMINAL
!           if (term_job_running(buf->b_term))
!           {
!               if (term_confirm_stop(buf) == FAIL)
!                   return;
!               // Manually kill the terminal here because this command will
!               // hide it otherwise.
!               free_terminal(buf);
!               need_hide = FALSE;
!           }
!           else
! # endif
!           {
!               bufref_T bufref;
  
!               set_bufref(&bufref, buf);
!               dialog_changed(buf, FALSE);
!               if (bufref_valid(&bufref) && bufIsChanged(buf))
!                   return;
!               need_hide = FALSE;
!           }
        }
        else
  #endif
*** ../vim-9.0.0707/src/proto/terminal.pro      2022-06-27 23:15:25.000000000 
+0100
--- src/proto/terminal.pro      2022-10-09 18:42:05.491666766 +0100
***************
*** 10,15 ****
--- 10,16 ----
  int term_job_running(term_T *term);
  int term_job_running_not_none(term_T *term);
  int term_none_open(term_T *term);
+ int term_confirm_stop(buf_T *buf);
  int term_try_stop_job(buf_T *buf);
  int term_check_timers(int next_due_arg, proftime_T *now);
  int term_in_normal_mode(void);
*** ../vim-9.0.0707/src/terminal.c      2022-10-04 16:23:39.018042176 +0100
--- src/terminal.c      2022-10-09 18:42:05.495666761 +0100
***************
*** 1651,1656 ****
--- 1651,1675 ----
        && term->tl_job->jv_channel->ch_keep_open;
  }
  
+ //
+ // Used to confirm whether we would like to kill a terminal.
+ // Return OK when the user confirms to kill it.
+ // Return FAIL if the user selects otherwise.
+ //
+     int
+ term_confirm_stop(buf_T *buf)
+ {
+     char_u    buff[DIALOG_MSG_SIZE];
+     int       ret;
+ 
+     dialog_msg(buff, _("Kill job in \"%s\"?"), buf_get_fname(buf));
+     ret = vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1);
+     if (ret == VIM_YES)
+       return OK;
+     else
+       return FAIL;
+ }
+ 
  /*
   * Used when exiting: kill the job in "buf" if so desired.
   * Return OK when the job finished.
***************
*** 1666,1679 ****
      if ((how == NULL || *how == NUL)
                          && (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)))
      {
!       char_u  buff[DIALOG_MSG_SIZE];
!       int     ret;
! 
!       dialog_msg(buff, _("Kill job in \"%s\"?"), buf_get_fname(buf));
!       ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
!       if (ret == VIM_YES)
            how = "kill";
!       else if (ret == VIM_CANCEL)
            return FAIL;
      }
  #endif
--- 1685,1693 ----
      if ((how == NULL || *how == NUL)
                          && (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)))
      {
!       if (term_confirm_stop(buf) == OK)
            how = "kill";
!       else
            return FAIL;
      }
  #endif
*** ../vim-9.0.0707/src/testdir/test_terminal.vim       2022-09-22 
12:57:02.288750572 +0100
--- src/testdir/test_terminal.vim       2022-10-09 18:42:05.495666761 +0100
***************
*** 98,104 ****
  
  func Test_terminal_wipe_buffer()
    let buf = Run_shell_in_terminal({})
!   call assert_fails(buf . 'bwipe', 'E89:')
    exe buf . 'bwipe!'
    call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
    call assert_equal("", bufname(buf))
--- 98,104 ----
  
  func Test_terminal_wipe_buffer()
    let buf = Run_shell_in_terminal({})
!   call assert_fails(buf . 'bwipe', 'E948:')
    exe buf . 'bwipe!'
    call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
    call assert_equal("", bufname(buf))
***************
*** 106,111 ****
--- 106,231 ----
    unlet g:job
  endfunc
  
+ " Test that using ':confirm bwipe' on terminal works
+ func Test_terminal_confirm_wipe_buffer()
+   CheckUnix
+   CheckNotGui
+   CheckFeature dialog_con
+   let buf = Run_shell_in_terminal({})
+   call assert_fails(buf . 'bwipe', 'E948:')
+   call feedkeys('n', 'L')
+   call assert_fails('confirm ' .. buf .. 'bwipe', 'E517:')
+   call assert_equal(buf, bufnr())
+   call assert_equal(1, &modified)
+   call feedkeys('y', 'L')
+   exe 'confirm ' .. buf .. 'bwipe'
+   call assert_notequal(buf, bufnr())
+   call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+   call assert_equal("", bufname(buf))
+ 
+   unlet g:job
+ endfunc
+ 
+ " Test that using :b! will hide the terminal
+ func Test_terminal_goto_buffer()
+   let buf_mod = bufnr()
+   let buf_term = Run_shell_in_terminal({})
+   call assert_equal(buf_term, bufnr())
+   call assert_fails(buf_mod . 'b', 'E948:')
+   exe buf_mod . 'b!'
+   call assert_equal(buf_mod, bufnr())
+   call assert_equal('run', job_status(g:job))
+   call assert_notequal('', bufname(buf_term))
+   exec buf_mod .. 'bwipe!'
+   exec buf_term .. 'bwipe!'
+ 
+   unlet g:job
+ endfunc
+ 
+ " Test that using ':confirm :b' will kill terminal
+ func Test_terminal_confirm_goto_buffer()
+   CheckUnix
+   CheckNotGui
+   CheckFeature dialog_con
+   let buf_mod = bufnr()
+   let buf_term = Run_shell_in_terminal({})
+   call feedkeys('n', 'L')
+   exe 'confirm ' .. buf_mod .. 'b'
+   call assert_equal(buf_term, bufnr())
+   call feedkeys('y', 'L')
+   exec 'confirm ' .. buf_mod .. 'b'
+   call assert_equal(buf_mod, bufnr())
+   call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+   call assert_equal("", bufname(buf_term))
+   exec buf_mod .. 'bwipe!'
+ 
+   unlet g:job
+ endfunc
+ 
+ " Test that using :close! will hide the terminal
+ func Test_terminal_close_win()
+   let buf = Run_shell_in_terminal({})
+   call assert_equal(buf, bufnr())
+   call assert_fails('close', 'E948:')
+   close!
+   call assert_notequal(buf, bufnr())
+   call assert_equal('run', job_status(g:job))
+   call assert_notequal('', bufname(buf))
+   exec buf .. 'bwipe!'
+ 
+   unlet g:job
+ endfunc
+ 
+ " Test that using ':confirm close' will kill terminal
+ func Test_terminal_confirm_close_win()
+   CheckUnix
+   CheckNotGui
+   CheckFeature dialog_con
+   let buf = Run_shell_in_terminal({})
+   call feedkeys('n', 'L')
+   confirm close
+   call assert_equal(buf, bufnr())
+   call feedkeys('y', 'L')
+   confirm close
+   call assert_notequal(buf, bufnr())
+   call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+   call assert_equal("", bufname(buf))
+ 
+   unlet g:job
+ endfunc
+ 
+ " Test that using :quit! will kill the terminal
+ func Test_terminal_quit()
+   let buf = Run_shell_in_terminal({})
+   call assert_equal(buf, bufnr())
+   call assert_fails('quit', 'E948:')
+   quit!
+   call assert_notequal(buf, bufnr())
+   call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+   exec buf .. 'bwipe!'
+ 
+   unlet g:job
+ endfunc
+ 
+ " Test that using ':confirm quit' will kill terminal
+ func Test_terminal_confirm_quit()
+   CheckUnix
+   CheckNotGui
+   CheckFeature dialog_con
+   let buf = Run_shell_in_terminal({})
+   call feedkeys('n', 'L')
+   confirm quit
+   call assert_equal(buf, bufnr())
+   call feedkeys('y', 'L')
+   confirm quit
+   call assert_notequal(buf, bufnr())
+   call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ 
+   unlet g:job
+ endfunc
+ 
+ " Test :q or :next
+ 
  func Test_terminal_split_quit()
    let buf = Run_shell_in_terminal({})
    split
***************
*** 707,713 ****
  
  func Test_terminal_list_args()
    let buf = term_start([&shell, &shellcmdflag, 'echo "123"'])
!   call assert_fails(buf . 'bwipe', 'E89:')
    exe buf . 'bwipe!'
    call assert_equal("", bufname(buf))
  endfunction
--- 827,833 ----
  
  func Test_terminal_list_args()
    let buf = term_start([&shell, &shellcmdflag, 'echo "123"'])
!   call assert_fails(buf . 'bwipe', 'E948:')
    exe buf . 'bwipe!'
    call assert_equal("", bufname(buf))
  endfunction
***************
*** 1235,1241 ****
  
    " make Vim exit, it will prompt to kill the shell
    call term_sendkeys(buf, "\<C-W>:confirm qall\<CR>")
!   call WaitForAssert({-> assert_match('ancel:', term_getline(buf, 20))})
    call term_sendkeys(buf, "y")
    call WaitForAssert({-> assert_equal('finished', term_getstatus(buf))})
  
--- 1355,1361 ----
  
    " make Vim exit, it will prompt to kill the shell
    call term_sendkeys(buf, "\<C-W>:confirm qall\<CR>")
!   call WaitForAssert({-> assert_match('\[Y\]es, (N)o:', term_getline(buf, 
20))})
    call term_sendkeys(buf, "y")
    call WaitForAssert({-> assert_equal('finished', term_getstatus(buf))})
  
*** ../vim-9.0.0707/src/version.c       2022-10-09 17:19:04.445451418 +0100
--- src/version.c       2022-10-09 18:49:46.120290363 +0100
***************
*** 701,702 ****
--- 701,704 ----
  {   /* Add new patch number below this line */
+ /**/
+     708,
  /**/

-- 
I still remember when I gave up Smoking, Drinking and Sex.  It was the
most *horrifying* hour of my life!

 /// 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/20221009175404.E53911C065C%40moolenaar.net.

Raspunde prin e-mail lui