Subject: bug#71386: 29.1; Frame is auto-deleted even when it has multiple tabs

From: martin rudalics <[email-masked]>
Date: Mon, 17 Jun 2024 16:47:07 +0200

> Also if the proposed change to `window-deletable-p` is adopted, then a
 > grep on `window-deletable-p` also reveals that, for consistency,
 > `delete-windows-on` and even `calendar-exit` in `calendar.el` should
 > be modified similarly to `window--delete`.

This means that we should do the entire fix within 'window-deletable-p',
right?  That's not an internal function, so we can, in all conscience,
supply an abnormal hook as in the roughly tested patch attached.

martin

diff --git a/lisp/window.el b/lisp/window.el
index 604b9868921..04e38faa074 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -4104,6 +4104,27 @@ one-window-p
 	(next-window base-window (if nomini 'arg) all-frames))))
 
 ;;; Deleting windows.
+(defcustom window-deletable-functions nil
+   "Abnormal hook to decide whether a window may be safely deleted.
+The value should be a list of functions that take two arguments.  The
+first argument is the window about to be deleted.  The second argument
+if non-nil, means that the window is the only window on its frame and
+should be deleted together with its frame.  The window's buffer is
+current when running this hook.
+
+If any of these functions returns nil, the window will not be deleted
+and another buffer will be shown in it.  This hook is run by
+`window-deletable-p' which, in is turn, is called by `delete-windows-on'
+and `quit-restore-window'.  It is neither run by `delete-window' nor
+`delete-frame'.
+
+The purpose of this hook is to give its clients a chance to save a
+window or its frame from deletion because they might still want to use
+that window or frame for their own purposes."
+  :type 'hook
+  :version "30.1"
+  :group 'windows)
+
 (defun window-deletable-p (&optional window)
   "Return t if WINDOW can be safely deleted from its frame.
 WINDOW must be a valid window and defaults to the selected one.
@@ -4137,14 +4158,20 @@ window-deletable-p
 		    (and minibuf (eq frame (window-frame minibuf))
                          (not (eq (default-toplevel-value
                                     'minibuffer-follows-selected-frame)
-                                  t)))))
+                                  t))))
+		  (not (with-current-buffer (window-buffer window)
+			 (run-hook-with-args-until-failure
+			  'window-deletable-functions window t))))
 	'frame))
      ((window-minibuffer-p window)
       ;; If WINDOW is the minibuffer window of a non-minibuffer-only
       ;; frame, it cannot be deleted separately.
       nil)
-     ((or ignore-window-parameters
-	  (not (eq window (window-main-window frame))))
+     ((and (or ignore-window-parameters
+	       (not (eq window (window-main-window frame))))
+	   (with-current-buffer (window-buffer window)
+	     (run-hook-with-args-until-failure
+	      'window-deletable-functions window nil)))
       ;; Otherwise, WINDOW can be deleted unless it is the main window
       ;; of its frame.
       t))))