]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve logic of tab handling when quitting windows (bug#71386)
authorJuri Linkov <juri@linkov.net>
Mon, 31 Mar 2025 17:40:17 +0000 (20:40 +0300)
committerEshel Yaron <me@eshelyaron.com>
Mon, 31 Mar 2025 18:40:16 +0000 (20:40 +0200)
* lisp/window.el (window-deletable-p): Add tab logic that returns
the symbol 'tab' for a set of predefined conditions.
(window--delete): Call 'tab-bar-close-tab' when 'window-deletable-p'
returns the symbol 'tab'.
(quit-restore-window): Remove tab logic and merge it with frame logic.

* test/lisp/tab-bar-tests.el (tab-bar-tests-close-other-tabs-default)
(tab-bar-tests-close-other-tabs-with-arg): Clean up tabs afterwards.
(tab-bar-tests-quit-restore-window): New test.

(cherry picked from commit a97a61b630624f5a6ec917db92e2985c56b20aa0)

lisp/window.el
test/lisp/tab-bar-tests.el

index f4981be41041cbd37439ddb175b5695850539d07..50f7c90a080eac3435e59a73a8e8e1d494d218b9 100644 (file)
@@ -4128,6 +4128,21 @@ returns nil."
 
   (let ((frame (window-frame window)))
     (cond
+     ((and tab-bar-mode
+           ;; Fall back to frame handling in case of less than 2 tabs
+           (> (length (funcall tab-bar-tabs-function frame)) 1)
+           ;; Close the tab with the initial window (bug#59862)
+           (or (eq (nth 1 (window-parameter window 'quit-restore)) 'tab)
+               ;; or with the dedicated window (bug#71386)
+               (and (window-dedicated-p window)
+                    (frame-root-window-p window)))
+           ;; Don't close the tab if more windows were created explicitly
+           (< (seq-count (lambda (w)
+                           (memq (car (window-parameter w 'quit-restore))
+                                 '(window tab frame same)))
+                         (window-list-1 nil 'nomini))
+              2))
+      'tab)
      ((frame-root-window-p window)
       ;; WINDOW's frame can be deleted only if there are other frames
       ;; on the same terminal, and it does not contain the active
@@ -4963,6 +4978,10 @@ if WINDOW gets deleted or its frame is auto-hidden."
   (unless (and dedicated-only (not (window-dedicated-p window)))
     (let ((deletable (window-deletable-p window)))
       (cond
+       ((eq deletable 'tab)
+        (tab-bar-close-tab)
+        (message "Tab closed after deleting the last window")
+        'tab)
        ((eq deletable 'frame)
        (let ((frame (window-frame window)))
          (cond
@@ -5331,13 +5350,7 @@ elsewhere.  This value is used by `quit-windows-on'."
       ;; If the previously selected window is still alive, select it.
       (window--quit-restore-select-window quit-restore-2))
      ((and (not prev-buffer)
-          (eq (nth 1 quit-restore) 'tab)
-          (eq (nth 3 quit-restore) buffer))
-      (tab-bar-close-tab)
-      ;; If the previously selected window is still alive, select it.
-      (window--quit-restore-select-window quit-restore-2))
-     ((and (not prev-buffer)
-          (or (eq (nth 1 quit-restore) 'frame)
+          (or (memq (nth 1 quit-restore) '(frame tab))
               (and (eq (nth 1 quit-restore) 'window)
                    ;; If the window has been created on an existing
                    ;; frame and ended up as the sole window on that
index a749db1c512a348c9183f58a7e43d13087350942..c0e12cf159c26a9a5b95bb6f84b138c0e3c65dfb 100644 (file)
   (should (eq (length tab-bar-closed-tabs) 0)))
 
 (ert-deftest tab-bar-tests-close-other-tabs-default ()
-  (tab-bar-tests-close-other-tabs nil))
+  (tab-bar-tests-close-other-tabs nil)
+  ;; Clean up tabs afterwards
+  (tab-bar-tabs-set nil))
 
 (ert-deftest tab-bar-tests-close-other-tabs-with-arg ()
-  (dotimes (i 5) (tab-bar-tests-close-other-tabs i)))
+  (dotimes (i 5) (tab-bar-tests-close-other-tabs i))
+  ;; Clean up tabs afterwards
+  (tab-bar-tabs-set nil))
+
+(ert-deftest tab-bar-tests-quit-restore-window ()
+  (let* ((frame-params (when noninteractive
+                         '((window-system . nil)
+                           (tty-type . "linux"))))
+         (pop-up-frame-alist frame-params)
+         (frame-auto-hide-function 'delete-frame))
+
+    ;; 1.1. 'quit-restore-window' should delete the frame
+    ;; from initial window (bug#59862)
+    (progn
+      (should (eq (length (frame-list)) 1))
+      (other-frame-prefix)
+      (info)
+      (should (eq (length (frame-list)) 2))
+      (should (equal (buffer-name) "*info*"))
+      (view-echo-area-messages)
+      (other-window 1)
+      (should (eq (length (window-list)) 2))
+      (should (equal (buffer-name) "*Messages*"))
+      (quit-window)
+      (should (eq (length (window-list)) 1))
+      (should (equal (buffer-name) "*info*"))
+      (quit-window)
+      (should (eq (length (frame-list)) 1)))
+
+    ;; 1.2. 'quit-restore-window' should not delete the frame
+    ;; from non-initial window (bug#59862)
+    (progn
+      (should (eq (length (frame-list)) 1))
+      (other-frame-prefix)
+      (info)
+      (should (eq (length (frame-list)) 2))
+      (should (equal (buffer-name) "*info*"))
+      (view-echo-area-messages)
+      (should (eq (length (window-list)) 2))
+      (should (equal (buffer-name) "*info*"))
+      (quit-window)
+      (should (eq (length (window-list)) 1))
+      (should (eq (length (frame-list)) 2))
+      ;; FIXME: uncomment (should (equal (buffer-name) "*Messages*"))
+      (quit-window)
+      (should (eq (length (frame-list)) 2))
+      ;; Clean up the frame afterwards
+      (delete-frame))
+
+    ;; 2.1. 'quit-restore-window' should close the tab
+    ;; from initial window (bug#59862)
+    (progn
+      (should (eq (length (tab-bar-tabs)) 1))
+      (other-tab-prefix)
+      (info)
+      (should (eq (length (tab-bar-tabs)) 2))
+      (should (equal (buffer-name) "*info*"))
+      (view-echo-area-messages)
+      (other-window 1)
+      (should (eq (length (window-list)) 2))
+      (should (equal (buffer-name) "*Messages*"))
+      (quit-window)
+      (should (eq (length (window-list)) 1))
+      (should (equal (buffer-name) "*info*"))
+      (quit-window)
+      (should (eq (length (tab-bar-tabs)) 1)))
+
+    ;; 2.2. 'quit-restore-window' should not close the tab
+    ;; from non-initial window (bug#59862)
+    (progn
+      (should (eq (length (tab-bar-tabs)) 1))
+      (other-tab-prefix)
+      (info)
+      (should (eq (length (tab-bar-tabs)) 2))
+      (should (equal (buffer-name) "*info*"))
+      (view-echo-area-messages)
+      (should (eq (length (window-list)) 2))
+      (should (equal (buffer-name) "*info*"))
+      (quit-window)
+      (should (eq (length (window-list)) 1))
+      (should (eq (length (tab-bar-tabs)) 2))
+      (should (equal (buffer-name) "*Messages*"))
+      (quit-window)
+      (should (eq (length (tab-bar-tabs)) 2))
+      ;; Clean up the tab afterwards
+      (tab-close))
+
+    ;; 3. Don't delete the frame with dedicated window
+    ;; from the second tab (bug#71386)
+    (with-selected-frame (make-frame frame-params)
+      (switch-to-buffer (generate-new-buffer "test1"))
+      (tab-new)
+      (switch-to-buffer (generate-new-buffer "test2"))
+      (set-window-dedicated-p (selected-window) t)
+      (kill-buffer)
+      (should (eq (length (frame-list)) 2))
+      (should (eq (length (tab-bar-tabs)) 1))
+      ;; But now should delete the frame with dedicated window
+      ;; from the last tab
+      (set-window-dedicated-p (selected-window) t)
+      (kill-buffer)
+      (should (eq (length (frame-list)) 1)))
+
+    ;; Clean up tabs afterwards
+    (tab-bar-tabs-set nil)))
 
 (provide 'tab-bar-tests)
 ;;; tab-bar-tests.el ends here