unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: martin rudalics via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
To: Juri Linkov <juri@linkov.net>, 59862@debbugs.gnu.org
Subject: bug#59862: quit-restore per window buffer
Date: Mon, 3 Jun 2024 18:09:52 +0200	[thread overview]
Message-ID: <f9eb9bae-6275-4448-9590-e72a56064220@gmx.at> (raw)
In-Reply-To: <18173da7-32e1-4f14-bbea-9a8fa66af7dd@gmx.at>

[-- Attachment #1: Type: text/plain, Size: 172 bytes --]

I revisited it once more and attach my last patch.  It also seems to
DTRT with C-x 4 d RET C-h C-t C-h C-n q q q but would still require a
longer period of testing.

martin

[-- Attachment #2: quit-restore.diff --]
[-- Type: text/x-patch, Size: 16371 bytes --]

diff --git a/lisp/window.el b/lisp/window.el
index b014be6a7cf..dcfc578ffd8 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -4519,13 +4519,33 @@ unrecord-window-buffer
   "Unrecord BUFFER in WINDOW.
 WINDOW must be a live window and defaults to the selected one.
 BUFFER must be a live buffer and defaults to the buffer of
-WINDOW."
+WINDOW.
+
+Make BUFFER disappear from all variables mentioned by the object of
+WINDOW.  This includes the buffers previously shwon in WINDOW as well as
+any buffers mentioned by WINDOW's `quit-restore' and `quit-restore-prev'
+parameters."
   (let* ((window (window-normalize-window window t))
-	 (buffer (or buffer (window-buffer window))))
-    (set-window-prev-buffers
-     window (assq-delete-all buffer (window-prev-buffers window)))
-    (set-window-next-buffers
-     window (delq buffer (window-next-buffers window)))))
+	 (buffer (or buffer (window-buffer window)))
+	 (quit-restore (window-parameter window 'quit-restore))
+	 (quit-restore-prev (window-parameter window 'quit-restore-prev))
+	 quad)
+    (when (buffer-live-p buffer)
+      (set-window-prev-buffers
+       window (assq-delete-all buffer (window-prev-buffers window)))
+      (set-window-next-buffers
+       window (delq buffer (window-next-buffers window)))
+
+      (when (or (eq buffer (nth 3 quit-restore-prev))
+		(and (listp (setq quad (nth 1 quit-restore-prev)))
+		     (eq (car quad) buffer)))
+	(set-window-parameter window 'quit-restore-prev nil))
+
+      (when (or (eq buffer (nth 3 quit-restore))
+		(and (listp (setq quad (nth 1 quit-restore)))
+		     (eq (car quad) buffer)))
+	(set-window-parameter
+	 window 'quit-restore (window-parameter window 'quit-restore-prev))))))
 
 (defun set-window-buffer-start-and-point (window buffer &optional start point)
   "Set WINDOW's buffer to BUFFER.
@@ -5075,21 +5095,20 @@ delete-windows-on
 use \\[universal-argument] 0 to specify all windows only on
 the current terminal's frames.
 
-If a frame's root window shows the buffer specified by
-BUFFER-OR-NAME and is dedicated to that buffer and that frame
-does not host the active minibuffer window and there is at least
-one other frame on that frame's terminal, delete that frame.
-Otherwise, do not delete a frame's root window if it shows the
-buffer specified by BUFFER-OR-NAME and do not delete any frame's
-main window showing that buffer either.  Rather, in any such
-case, call `switch-to-prev-buffer' to show another buffer in that
-window and make sure the window is no more dedicated to its
-buffer.
-
-If the buffer specified by BUFFER-OR-NAME is shown in a
-minibuffer window, do nothing for that window.  For any window
-that does not show that buffer, remove the buffer from that
-window's lists of previous and next buffers."
+If a frame's root window shows the buffer specified by BUFFER-OR-NAME
+and is dedicated to that buffer and that frame does not host the active
+minibuffer window and there is at least one other frame on that frame's
+terminal, delete that frame.  Otherwise, do not delete a frame's root
+window if it shows the buffer specified by BUFFER-OR-NAME and do not
+delete any frame's main window showing that buffer either.  Rather, in
+any such case, call `quit-restore-window' to show another buffer in that
+window and make sure the window is no more dedicated to its buffer.
+
+If the buffer specified by BUFFER-OR-NAME is shown in a minibuffer
+window, do nothing for that window.  For any window that does not show
+that buffer, remove the buffer from that window's lists of previous and
+next buffers and remove any `quit-restore' and `quit-restore-prev'
+parameters naming it."
   (interactive
    (let ((frame (cond
                  ((and (numberp current-prefix-arg)
@@ -5107,9 +5126,9 @@ delete-windows-on
            frame)))
   (let ((buffer (window-normalize-buffer buffer-or-name))
 	;; Handle the "inverted" meaning of the FRAME argument wrt other
-	;; `window-list-1' based function.
-	(all-frames (cond ((not frame) t) ((eq frame t) nil) (t frame))))
-    (dolist (window (window-list-1 nil nil all-frames))
+	;; `window-list-1' based functions.
+	(frames (cond ((not frame) t) ((eq frame t) nil) (t frame))))
+    (dolist (window (window-list-1 nil 'nomini frames))
       (if (eq (window-buffer window) buffer)
 	  (let ((deletable (window-deletable-p window))
                 (dedicated (window-dedicated-p window)))
@@ -5121,19 +5140,19 @@ delete-windows-on
 	      ;; Delete window.
 	      (delete-window window))
 	     (t
-	      ;; In window switch to previous buffer.
-	      (set-window-dedicated-p window nil)
-	      (switch-to-prev-buffer window 'bury)
-              ;; Restore the dedicated 'side' flag.
-              (when (eq dedicated 'side)
-                (set-window-dedicated-p window 'side)))))
+	      (when (eq (window-buffer window) buffer)
+		(quit-restore-window window 'bury))
+	      (when (window-live-p window)
+		;; Unrecord BUFFER in this window.
+		(unrecord-window-buffer window buffer)))))
 	;; If a window doesn't show BUFFER, unrecord BUFFER in it.
 	(unrecord-window-buffer window buffer)))))
 
 (defun replace-buffer-in-windows (&optional buffer-or-name)
   "Replace BUFFER-OR-NAME with some other buffer in all windows showing it.
 BUFFER-OR-NAME may be a buffer or the name of an existing buffer
-and defaults to the current buffer.
+and defaults to the current buffer.  Minibuffer windows are not
+considered.
 
 With the exception of side windows, when a window showing BUFFER-OR-NAME
 is dedicated, that window is deleted.  If that window is the only window
@@ -5141,21 +5160,18 @@ replace-buffer-in-windows
 If there are no other frames left, some other buffer is displayed in that
 window.
 
-This function removes the buffer denoted by BUFFER-OR-NAME from
-all window-local buffer lists."
+This function removes the buffer denoted by BUFFER-OR-NAME from all
+window-local buffer lists and removes any `quit-restore' or
+`quit-restore-prev' parameters mentioning it."
   (interactive "bBuffer to replace: ")
   (let ((buffer (window-normalize-buffer buffer-or-name)))
+    ;; Scan all windows.  We have to unrecord BUFFER in those not
+    ;; showing it.
     (dolist (window (window-list-1 nil nil t))
-      (if (eq (window-buffer window) buffer)
-          ;; Delete a dedicated window unless it is a side window.
-          (let ((dedicated-side (eq (window-dedicated-p window) 'side)))
-            (when (or dedicated-side (not (window--delete window t t)))
-              ;; Switch to another buffer in that window.
-              (set-window-dedicated-p window nil)
-              (if (switch-to-prev-buffer window 'kill)
-                  (and dedicated-side (set-window-dedicated-p window 'side))
-                (window--delete window nil 'kill))))
-	;; Unrecord BUFFER in WINDOW.
+      (when (eq (window-buffer window) buffer)
+	(quit-restore-window window))
+      (when (window-live-p window)
+	;; Unrecord BUFFER in this window.
 	(unrecord-window-buffer window buffer)))))
 
 (defcustom quit-window-hook nil
@@ -5176,13 +5192,13 @@ quit-restore-window
   "Quit WINDOW and deal with its buffer.
 WINDOW must be a live window and defaults to the selected one.
 
-According to information stored in WINDOW's `quit-restore' window
-parameter either (1) delete WINDOW and its frame, (2) delete
-WINDOW but leave its frame alone, (3) restore the buffer
-previously shown in WINDOW, or (4) make WINDOW display some other
-buffer.  If WINDOW is not deleted, reset its `quit-restore'
-parameter to nil.  See Info node `(elisp) Quitting Windows' for
-more details.
+According to information stored in WINDOW's `quit-restore' and
+`quit-restore-prev' window parameters either (1) delete WINDOW and its
+frame, (2) delete WINDOW but leave its frame alone, (3) restore the
+buffer previously shown in WINDOW, or (4) make WINDOW display some other
+buffer.  In case (3) set any of these parameters to nil if it has been
+used to restore the previously shown buffer.  See Info node `(elisp)
+Quitting Windows' for more details.
 
 If WINDOW's dedicated flag is t, try to delete WINDOW.  If it
 equals the value `side', restore that value when WINDOW is not
@@ -5210,13 +5226,15 @@ quit-restore-window
   (setq window (window-normalize-window window t))
   (let* ((buffer (window-buffer window))
 	 (quit-restore (window-parameter window 'quit-restore))
+	 (quit-restore-prev (window-parameter window 'quit-restore-prev))
 	 (quit-restore-2 (nth 2 quit-restore))
+	 (quit-restore-prev-2 (nth 2 quit-restore-prev))
          (prev-buffer (catch 'prev-buffer
                         (dolist (buf (window-prev-buffers window))
                           (unless (eq (car buf) buffer)
                             (throw 'prev-buffer (car buf))))))
          (dedicated (window-dedicated-p window))
-	 quad entry)
+	 quad entry reset-prev)
     (cond
      ;; First try to delete dedicated windows that are not side windows.
      ((and dedicated (not (eq dedicated 'side))
@@ -5230,21 +5248,35 @@ quit-restore-window
       ;; 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)
-	       (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
-		    ;; frame, do not delete it (Bug#12764).
-		    (not (eq window (frame-root-window window)))))
-	   (eq (nth 3 quit-restore) buffer)
+	   (or (and (or (eq (nth 1 quit-restore) 'frame)
+			(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 frame, do not delete it
+			     ;; (Bug#12764).
+			     (not (eq window (frame-root-window window)))))
+		    (eq (nth 3 quit-restore) buffer))
+	       (and (or (eq (nth 1 quit-restore-prev) 'frame)
+			(and (eq (nth 1 quit-restore-prev) 'window)
+			     (not (eq window (frame-root-window window)))))
+		    (eq (nth 3 quit-restore-prev) buffer)
+		    ;; Use selected window from quit-restore-prev.
+		    (setq quit-restore-2 quit-restore-prev-2)))
 	   ;; Delete WINDOW if possible.
 	   (window--delete window nil (eq bury-or-kill 'kill)))
       ;; If the previously selected window is still alive, select it.
       (window--quit-restore-select-window quit-restore-2))
-     ((and (listp (setq quad (nth 1 quit-restore)))
-	   (buffer-live-p (car quad))
-	   (eq (nth 3 quit-restore) buffer))
-      ;; Show another buffer stored in quit-restore parameter.
+     ((or (and (listp (setq quad (nth 1 quit-restore-prev)))
+	       (buffer-live-p (car quad))
+	       (eq (nth 3 quit-restore-prev) buffer)
+	       ;; Use selected window from quit-restore-prev.
+	       (setq quit-restore-2 quit-restore-prev-2)
+	       ;; We want to reset quit-restore-prev only.
+	       (setq reset-prev t))
+	  (and (listp (setq quad (nth 1 quit-restore)))
+	       (buffer-live-p (car quad))
+	       (eq (nth 3 quit-restore) buffer)))
+      ;; Show another buffer stored in quit-restore(-prev) parameter.
       (when (and (integerp (nth 3 quad))
 		 (if (window-combined-p window)
                      (/= (nth 3 quad) (window-total-height window))
@@ -5281,15 +5313,18 @@ quit-restore-window
 	;; `display-buffer-in-previous-window' can nevertheless find it.
 	(set-window-prev-buffers
 	 window (append (window-prev-buffers window) (list entry))))
-      ;; Reset the quit-restore parameter.
-      (set-window-parameter window 'quit-restore nil)
+      ;; Reset the quit-restore(-prev) parameter.
+      (set-window-parameter window 'quit-restore-prev nil)
+      (unless reset-prev
+	;; If quit-restore-prev was not used, reset the quit-restore
+	;; parameter
+	(set-window-parameter window 'quit-restore nil))
       ;; Select old window.
       ;; If the previously selected window is still alive, select it.
       (window--quit-restore-select-window quit-restore-2))
      (t
-      ;; Show some other buffer in WINDOW and reset the quit-restore
-      ;; parameter.
-      (set-window-parameter window 'quit-restore nil)
+      ;; Show some other buffer in WINDOW and leave the
+      ;; quit-restore(-prev) parameters alone (Juri's idea).
       ;; Make sure that WINDOW is no more dedicated.
       (set-window-dedicated-p window nil)
       ;; Try to switch to a previous buffer.  Delete the window only if
@@ -5297,10 +5332,7 @@ quit-restore-window
       (if (switch-to-prev-buffer window bury-or-kill)
           (when (eq dedicated 'side)
             (set-window-dedicated-p window 'side))
-        (window--delete window nil (eq bury-or-kill 'kill))
-      ;; If the previously selected window is still alive, select it.
-      (window--quit-restore-select-window quit-restore-2))))
-
+        (window--delete window nil (eq bury-or-kill 'kill)))))
     ;; Deal with the buffer.
     (cond
      ((not (buffer-live-p buffer)))
@@ -5336,17 +5368,21 @@ quit-windows-on
 BUFFER-OR-NAME.  Optional argument FRAME is handled as by
 `delete-windows-on'.
 
-This function calls `quit-window' on all candidate windows
-showing BUFFER-OR-NAME."
+This function calls `quit-restore-window' on all candidate windows
+showing BUFFER-OR-NAME.  In addition, it removes the buffer denoted by
+BUFFER-OR-NAME from all window-local buffer lists and removes any
+`quit-restore' or `quit-restore-prev' parameters mentioning it."
   (interactive "bQuit windows on (buffer):\nP")
   (let ((buffer (window-normalize-buffer buffer-or-name))
 	;; Handle the "inverted" meaning of the FRAME argument wrt other
-	;; `window-list-1' based function.
-	(all-frames (cond ((not frame) t) ((eq frame t) nil) (t frame))))
-    (dolist (window (window-list-1 nil nil all-frames))
-      (if (eq (window-buffer window) buffer)
-	  (quit-window kill window)
-	;; If a window doesn't show BUFFER, unrecord BUFFER in it.
+	;; `window-list' based function.
+	(frames (cond ((not frame) t) ((eq frame t) nil) (t frame))))
+    (dolist (window (window-list-1 nil nil frames))
+      (when (eq (window-buffer window) buffer)
+	(quit-restore-window window kill))
+
+      (when (window-live-p window)
+	;; Unrecord BUFFER in this window.
 	(unrecord-window-buffer window buffer)))))
 \f
 (defun window--combination-resizable (parent &optional horizontal)
@@ -6656,7 +6692,13 @@ display-buffer-record-window
 previously shown in the window, that buffer's window start and
 window point, and the window's height.  The third element is the
 window selected at the time the parameter was created.  The
-fourth element is BUFFER."
+fourth element is BUFFER.
+
+If TYPE is `reuse', BUFFER is different from the one currently displayed
+in WINDOW, and WINDOW already has a `quit-restore' parameter, install or
+update a `quit-restore-prev' parameter for this window that allows to
+quit WINDOW in a similar fashion but remembers the very first in a
+series of buffer display operations for this window."
   (cond
    ((eq type 'reuse)
     (if (eq (window-buffer window) buffer)
@@ -6673,7 +6715,7 @@ display-buffer-record-window
       ;; WINDOW shows another buffer.
       (with-current-buffer (window-buffer window)
 	(set-window-parameter
-	 window 'quit-restore
+	 window 'quit-restore-prev
 	 (list 'other
 	       ;; A quadruple of WINDOW's buffer, start, point and height.
 	       (list (current-buffer) (window-start window)
@@ -9103,6 +9145,9 @@ switch-to-buffer
 as prescribed by the option `switch-to-buffer-preserve-window-point'.
 Otherwise, these are left alone.
 
+In either case, call `display-buffer-record-window' to avoid disrupting
+a sequence of `display-buffer' operations using this window.
+
 Return the buffer switched to."
   (interactive
    (let ((force-same-window
@@ -9158,6 +9203,11 @@ switch-to-buffer
                                     buffer))
 	       (displayed (and (eq preserve-win-point 'already-displayed)
 			       (get-buffer-window buffer 0))))
+
+	  ;; Make sure quitting the window works.
+	  (unless switch-to-buffer-obey-display-actions
+	    (display-buffer-record-window 'reuse (selected-window) buffer))
+
 	  (set-window-buffer nil buffer)
 	  (when (and entry (or (eq preserve-win-point t) displayed))
 	    ;; Try to restore start and point of buffer in the selected

  reply	other threads:[~2024-06-03 16:09 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-06 17:32 bug#59862: quit-restore per window buffer Juri Linkov
2024-06-02  6:45 ` Juri Linkov
2024-06-03  9:34   ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-03  9:53     ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-03 16:09       ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors [this message]
2024-06-04  6:53         ` Juri Linkov
2024-06-05 16:56           ` Juri Linkov
2024-06-11  6:52           ` Juri Linkov
2024-06-12  8:57             ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-13  6:47               ` Juri Linkov
2024-06-13  8:21                 ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-14 17:35                   ` Juri Linkov
2024-06-15  8:41                     ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-06-16 16:50                       ` Juri Linkov
2024-06-17 14:48                         ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-07-08 16:49                         ` martin rudalics via Bug reports for GNU Emacs, the Swiss army knife of text editors

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=f9eb9bae-6275-4448-9590-e72a56064220@gmx.at \
    --to=bug-gnu-emacs@gnu.org \
    --cc=59862@debbugs.gnu.org \
    --cc=juri@linkov.net \
    --cc=rudalics@gmx.at \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).