From cee4d03535e5f63054a12925096e9c98a6b4af6f Mon Sep 17 00:00:00 2001 From: Martin Rudalics Date: Mon, 24 Feb 2025 10:17:10 +0100 Subject: [PATCH] Optionally have 'display-buffer' reuse windows of indirect buffers * 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 | 50 +++++++++++++++++- lisp/window.el | 107 ++++++++++++++++++++++++++++++--------- 2 files changed, 133 insertions(+), 24 deletions(-) diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 6c4e59d448f..5c0db6d4877 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -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 diff --git a/lisp/window.el b/lisp/window.el index 363cb38ea0e..2176cde2cb2 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -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))))))) -- 2.39.5