]> git.eshelyaron.com Git - emacs.git/commitdiff
Optionally have 'display-buffer' reuse windows of indirect buffers
authorMartin Rudalics <rudalics@gmx.at>
Mon, 24 Feb 2025 09:17:10 +0000 (10:17 +0100)
committerEshel Yaron <me@eshelyaron.com>
Wed, 26 Feb 2025 09:34:12 +0000 (10:34 +0100)
* lisp/window.el (window-indirect-buffer-p): New function.
(get-buffer-window-list): New argument INDIRECT.
(display-buffer-reuse-window): New alist entry 'reuse-indirect'
to reuse a window indirectly related to the BUFFER argument.
* doc/lispref/windows.texi (Buffers and Windows): Describe new
function 'window-indirect-buffer-p' and new argument INDIRECT of
'get-buffer-window-list'.
(Buffer Display Action Functions): Describe new action alist
entry 'reuse-indirect'.
* etc/NEWS: Announce new argument for 'get-buffer-window-list' and
new 'display-buffer' action alist entry 'reuse-indirect'.

(cherry picked from commit a205d554522340e23540bdda63c80965ddd64951)

doc/lispref/windows.texi
lisp/window.el

index 6c4e59d448f97adb35a5d01274ea0c23c3e264b7..5c0db6d48779edd4a4882c7ac89a04d98d91fd50 100644 (file)
@@ -2697,7 +2697,37 @@ Ordering}).  This function may be changed in a future version of Emacs
 to eliminate this discrepancy.
 @end defun
 
-@defun get-buffer-window-list &optional buffer-or-name minibuf all-frames
+The following function can tell for a specific window whether its buffer
+shares the text of some other buffer (@pxref{Indirect Buffers}).
+
+@defun window-indirect-buffer-p &optional window buffer-or-name
+This function returns non-@code{nil} if @var{window} is indirectly
+related to @var{buffer-or-name}.  @var{window} must be a live window and
+defaults to the selected window.  @var{buffer-or-name} may be a buffer
+or the name of an existing buffer and defaults to the current buffer.
+
+@var{window} is indirectly related to @var{buffer-or-name} if one of the
+following conditions hold:
+
+@itemize @bullet
+@item
+@var{buffer-or-name} specifies an indirect buffer and @var{window}'s
+buffer is its base buffer.
+
+@item
+@var{window}'s buffer is an indirect buffer whose base buffer is the
+buffer specified by @var{buffer-or-name}.
+
+@item
+Both, @var{window}'s buffer and the buffer specified by
+@var{buffer-or-name}, are indirect buffer's sharing the same base
+buffer.
+@end itemize
+
+It returns @code{nil} if none of the above holds.
+@end defun
+
+@defun get-buffer-window-list &optional buffer-or-name minibuf all-frames indirect
 This function returns a list of all windows currently displaying
 @var{buffer-or-name}.  @var{buffer-or-name} should be a buffer or the
 name of an existing buffer.  If omitted or @code{nil}, it defaults to
@@ -2709,6 +2739,13 @@ The arguments @var{minibuf} and @var{all-frames} have the same
 meanings as in the function @code{next-window} (@pxref{Cyclic Window
 Ordering}).  Note that the @var{all-frames} argument does @emph{not}
 behave exactly like in @code{get-buffer-window}.
+
+The optional argument @var{indirect} non-@code{nil} means to append to
+the list of windows showing @var{buffer-or-name} a list of all windows
+that are indirectly related to @var{buffer-or-name}, that is, windows
+for which @code{window-indirect-buffer-p} (see above) with the window
+and the buffer specified by @var{buffer-or-name} as arguments returns
+non-@code{nil}.
 @end defun
 
 @deffn Command replace-buffer-in-windows &optional buffer-or-name
@@ -3165,6 +3202,17 @@ searches just the selected frame.
 If this function chooses a window on another frame, it makes that
 frame visible and, unless @var{alist} contains an
 @code{inhibit-switch-frame} entry, raises that frame if necessary.
+
+If @var{alist} has a non-@code{nil} @code{reuse-indirect} entry and no
+window showing @var{buffer} has been found, this function tries to find
+a window that is indirectly related to @var{buffer}---a window for which
+@code{window-indirect-buffer-p} (@pxref{Buffers and Windows}) with the
+window and @var{buffer} as arguments returns non-@code{nil}.  If such a
+window has been found and the @sc{cdr} of the @code{reuse-indirect}
+entry equals the symbol @code{buffer}, it does not replace the buffer of
+that window with @var{buffer} but returns the window with its old buffer
+in place.  Otherwise, it puts @var{buffer} into that window and returns
+that window.
 @end defun
 
 @defun display-buffer-reuse-mode-window buffer alist
index 363cb38ea0e5788d6f5099e08aaf5c58069f680a..2176cde2cb21fb3690770bf6e80535c99d70e902 100644 (file)
@@ -2557,7 +2557,36 @@ selected frame and no others."
          (setq best-window window))))
     best-window))
 
-(defun get-buffer-window-list (&optional buffer-or-name minibuf all-frames)
+(defun window-indirect-buffer-p (&optional window buffer-or-name)
+  "Return non-nil if specified WINDOW is indirectly related to BUFFER-OR-NAME.
+WINDOW must be a live window and defaults to the selected window.
+BUFFER-OR-NAME may be a buffer or the name of an existing buffer and
+defaults to the current buffer.
+
+WINODW is indirectly related to BUFFER-OR-NAME if one of the following
+conditions hold:
+
+- BUFFER-OR-NAME specifies an indirect buffer and WINDOW's buffer is its
+  base buffer.
+
+- WINDOW's buffer is an indirect buffer whose base buffer is the buffer
+  specified by BUFFER-OR-NAME.
+
+- Both, WINDOW's buffer and the buffer specified by BUFFER-OR-NAME, are
+  indirect buffer's sharing the same base buffer.
+
+Return nil if none of the above holds."
+  (let* ((window (window-normalize-window window t))
+        (window-buffer (window-buffer window))
+        (window-base-buffer (buffer-base-buffer window-buffer))
+        (buffer (window-normalize-buffer buffer-or-name))
+        (buffer-base-buffer (buffer-base-buffer buffer)))
+    (or (eq buffer-base-buffer window-buffer)
+       (eq window-base-buffer buffer)
+       (and buffer-base-buffer
+            (eq buffer-base-buffer window-base-buffer)))))
+
+(defun get-buffer-window-list (&optional buffer-or-name minibuf all-frames indirect)
   "Return list of all windows displaying BUFFER-OR-NAME, or nil if none.
 BUFFER-OR-NAME may be a buffer or the name of an existing buffer
 and defaults to the current buffer.  If the selected window displays
@@ -2586,12 +2615,23 @@ non-nil values of ALL-FRAMES have special meanings:
 - A frame means consider all windows on that frame only.
 
 Anything else means consider all windows on the selected frame
-and no others."
+and no others.
+
+INDIRECT non-nil means to append to the list of windows showing
+BUFFER-OR-NAME a list of all windows that are indirectly related to
+BUFFER-OR-NAME, that is, windows for which `window-indirect-buffer-p'
+with the window and the buffer specified by BUFFER-OR-NAME as arguments
+returns non-nil."
   (let ((buffer (window-normalize-buffer buffer-or-name))
+       (window-list (window-list-1 (selected-window) minibuf all-frames))
        windows)
-    (dolist (window (window-list-1 (selected-window) minibuf all-frames))
+    (dolist (window window-list)
       (when (eq (window-buffer window) buffer)
        (setq windows (cons window windows))))
+    (when indirect
+      (dolist (window window-list)
+       (when (window-indirect-buffer-p window buffer)
+         (setq windows (cons window windows)))))
     (nreverse windows)))
 
 (defun minibuffer-window-active-p (window)
@@ -8300,35 +8340,56 @@ If ALIST has a non-nil `inhibit-switch-frame' entry, then in the
 event that a window on another frame is chosen, avoid raising
 that frame.
 
+If ALIST has a non-nil `reuse-indirect' entry and no window showing
+BUFFER has been found, try to find a window that is indirectly related
+to BUFFER and return that window.  This would be a window for which
+`window-indirect-buffer-p' with the window and BUFFER as arguments
+returns non-nil.  If a suitable window has been found and the cdr of the
+entry equals the symbol `buffer', do not replace the buffer of that
+window with BUFFER but return the window with its old buffer in place.
+Otherwise, put BUFFER into that window and return the window.
+
 This is an action function for buffer display, see Info
 node `(elisp) Buffer Display Action Functions'.  It should be
 called only by `display-buffer' or a function directly or
 indirectly called by the latter."
-  (let* ((alist-entry (assq 'reusable-frames alist))
-        (frames (cond (alist-entry (cdr alist-entry))
+  (let* ((reusable-frames (assq 'reusable-frames alist))
+        (reuse-indirect (assq 'reuse-indirect alist))
+        (frames (cond (reusable-frames (cdr reusable-frames))
                       ((window--pop-up-frames alist)
                        0)
                       (display-buffer-reuse-frames 0)
                       (t (last-nonminibuffer-frame))))
-        (window (if (and (eq buffer (window-buffer))
-                         (not (cdr (assq 'inhibit-same-window alist))))
-                    (selected-window)
-                   ;; Preferably use a window on the selected frame,
-                   ;; if such a window exists (Bug#36680).
-                   (let* ((windows (delq (selected-window)
-                                         (get-buffer-window-list
-                                          buffer 'nomini frames)))
-                          (first (car windows))
-                          (this-frame (selected-frame)))
-                     (cond
-                      ((eq (window-frame first) this-frame)
-                       first)
-                      ((catch 'found
-                         (dolist (next (cdr windows))
-                           (when (eq (window-frame next) this-frame)
-                             (throw 'found next)))))
-                      (t first))))))
+        (inhibit-same (cdr (assq 'inhibit-same-window alist)))
+        (window
+         ;; Avoid calling 'get-buffer-window-list' if the selected
+         ;; window already shows BUFFER and can be used.
+         (if (and (eq buffer (window-buffer)) (not inhibit-same))
+             (selected-window)
+            ;; Preferably use a window on the selected frame,
+            ;; if such a window exists (Bug#36680).
+            (let* ((windows-raw
+                   (get-buffer-window-list
+                     buffer 'nomini frames reuse-indirect))
+                  (windows (if inhibit-same
+                               (delq (selected-window) windows-raw)
+                             windows-raw))
+                   (first (car windows))
+                   (this-frame (selected-frame)))
+              (cond
+               ((eq (window-frame first) this-frame)
+                first)
+               ((catch 'found
+                  (dolist (next (cdr windows))
+                    (when (eq (window-frame next) this-frame)
+                      (throw 'found next)))))
+               (t first))))))
     (when (window-live-p window)
+      (when (and (eq (cdr reuse-indirect) 'buffer)
+                (not (eq (window-buffer window) buffer)))
+       ;; Pretend we were asking for a window showing the buffer of
+       ;; that window.
+       (setq buffer (window-buffer window)))
       (prog1 (window--display-buffer buffer window 'reuse alist)
        (unless (cdr (assq 'inhibit-switch-frame alist))
          (window--maybe-raise-frame (window-frame window)))))))