1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-15 10:30:25 -08:00

Fix 'display-buffer-use-least-recent-window'

* src/window.c (Fwindow_use_time): Doc fix.
(Fwindow_bump_use_time): Bump use time of the seleceted window as
well.  Doc fix.

* lisp/window.el (display-buffer-avoid-small-windows): Remove.
All users changed.
(window--display-buffer): Bump window use time when requested.
(display-buffer--lru-window): New function.
(display-buffer-use-some-window): Use it.
(display-buffer-use-least-recent-window): Rewrite and enhance doc
string.

* doc/lispref/windows.texi (Selecting Windows)
(Buffer Display Action Functions, Buffer Display Action Alists)
(The Zen of Buffer Display): Improve and update documentation of
window selection and display facilities.
This commit is contained in:
martin rudalics 2023-02-12 10:33:11 +01:00 committed by Eli Zaretskii
parent 5190ea6259
commit 9f508cef85
3 changed files with 339 additions and 96 deletions

View file

@ -2484,14 +2484,6 @@ and no others."
(defalias 'some-window 'get-window-with-predicate)
(defcustom display-buffer-avoid-small-windows nil
"If non-nil, windows that have fewer lines than this are avoided.
This is used by `get-lru-window'. The value is interpreted in units
of the frame's canonical line height, like `window-total-height' does."
:type '(choice (const nil) number)
:version "29.1"
:group 'windows)
(defun get-lru-window (&optional all-frames dedicated not-selected no-other)
"Return the least recently used window on frames specified by ALL-FRAMES.
Return a full-width window if possible. A minibuffer window is
@ -2517,11 +2509,7 @@ have special meanings:
- A frame means consider all windows on that frame only.
Any other value of ALL-FRAMES means consider all windows on the
selected frame and no others.
`display-buffer-avoid-small-windows', if non-nil, is also taken into
consideration. Windows whose height is smaller that the value of that
variable will be avoided if larger windows are available."
selected frame and no others."
(let ((windows (window-list-1 nil 'nomini all-frames))
best-window best-time second-best-window second-best-time time)
(dolist (window windows)
@ -2531,9 +2519,6 @@ variable will be avoided if larger windows are available."
(not (window-parameter window 'no-other-window))))
(setq time (window-use-time window))
(if (or (eq window (selected-window))
(and display-buffer-avoid-small-windows
(< (window-height window)
display-buffer-avoid-small-windows))
(not (window-full-width-p window)))
(when (or (not second-best-time) (< time second-best-time))
(setq second-best-time time)
@ -7274,6 +7259,11 @@ entry. Otherwise, if WINDOW is new and the value of
dedicated flag to that value. In any other case, reset WINDOW's
dedicated flag to nil.
If ALIST contains a non-nil `bump-use-time' entry, bump use time
of WINDOW so further calls of `display-buffer-use-some-window'
and `display-buffer-use-least-recent-window' will try to avoid
it.
Return WINDOW if BUFFER and WINDOW are live."
(when (and (buffer-live-p buffer) (window-live-p window))
(display-buffer-record-window type window buffer)
@ -7281,6 +7271,10 @@ Return WINDOW if BUFFER and WINDOW are live."
;; Unless WINDOW already shows BUFFER reset its dedicated flag.
(set-window-dedicated-p window nil)
(set-window-buffer window buffer))
(when (cdr (assq 'bump-use-time alist))
;; Bump WINDOW's use time so 'display-buffer--lru-window' will try
;; to avoid it.
(window-bump-use-time window))
(let ((alist-dedicated (assq 'dedicated alist)))
;; Maybe dedicate WINDOW to BUFFER if asked for.
(cond
@ -8502,15 +8496,64 @@ indirectly called by the latter."
(when (setq window (or best-window second-best-window))
(window--display-buffer buffer window 'reuse alist))))
(defun display-buffer-use-least-recent-window (buffer alist)
"Display BUFFER in an existing window, but that hasn't been used lately.
This `display-buffer' action function is like
`display-buffer-use-some-window', but will cycle through windows
when displaying buffers repeatedly, and if there's only a single
window, it will split the window."
(when-let ((window (display-buffer-use-some-window
buffer (cons (cons 'inhibit-same-window t) alist))))
(window-bump-use-time window)))
(defun display-buffer--lru-window (alist)
"Return the least recently used window according to ALIST.
Do not return a minibuffer window or a window dedicated to its
buffer. ALIST is a buffer display action alist as compiled by
`display-buffer'. The following ALIST entries are honored:
- `lru-frames' specifies the frames to investigate and has the
same meaning as the ALL-FRAMES argument of `get-lru-window'.
- `lru-time' specifies a use time. Do not return a window whose
use time is higher than this.
- `window-min-width' specifies a preferred minimum width in
canonical frame columns. If it is the constant `full-width',
prefer a full-width window.
- `window-min-height' specifies a preferred minimum height in
canonical frame lines. If it is the constant `full-height',
prefer a full-height window.
If ALIST contains a non-nil `inhibit-same--window' entry, do not
return the selected window."
(let ((windows
(window-list-1 nil 'nomini (cdr (assq 'lru-frames alist))))
(lru-time (cdr (assq 'lru-time alist)))
(min-width (cdr (assq 'window-min-width alist)))
(min-height (cdr (assq 'window-min-height alist)))
(not-this-window (cdr (assq 'inhibit-same-window alist)))
best-window best-time second-best-window second-best-time time)
(dolist (window windows)
(when (and (not (window-dedicated-p window))
(or (not not-this-window)
(not (eq window (selected-window)))))
(setq time (window-use-time window))
(unless (and (numberp lru-time) (> time lru-time))
(if (or (eq window (selected-window))
(and min-width
(or (and (numberp min-width)
(< (window-width window) min-width))
(and (eq min-width 'full-width)
(not (window-full-width-p window)))))
(and min-height
(or (and (numberp min-height)
(< (window-height window) min-height))
(and (eq min-height 'full-height)
(not (window-full-height-p window))))))
;; This window is either selected or does not meet the size
;; restrictions - so it's only a second best choice. Try to
;; find a more recently used one that fits.
(when (or (not second-best-time) (< time second-best-time))
(setq second-best-time time)
(setq second-best-window window))
;; This window is not selected and does meet the size
;; restrictions. It's the best choice so far.
(when (or (not best-time) (< time best-time))
(setq best-time time)
(setq best-window window))))))
(or best-window second-best-window)))
(defun display-buffer-use-some-window (buffer alist)
"Display BUFFER in an existing window.
@ -8534,7 +8577,11 @@ indirectly called by the latter."
(window--frame-usable-p (last-nonminibuffer-frame))))
(window
;; Reuse an existing window.
(or (get-lru-window frame nil not-this-window)
(or (display-buffer--lru-window
;; If ALIST specifies 'lru-frames' or 'window-min-width'
;; let them prevail.
(append alist `((lru-frames . ,frame)
(window-min-width . full-width))))
(let ((window (get-buffer-window buffer 'visible)))
(unless (and not-this-window
(eq window (selected-window)))
@ -8564,6 +8611,76 @@ indirectly called by the latter."
(unless (cdr (assq 'inhibit-switch-frame alist))
(window--maybe-raise-frame (window-frame window)))))))
(defun display-buffer-use-least-recent-window (buffer alist)
"Display BUFFER trying to avoid windows used recently.
This is similar to `display-buffer-use-some-window' but tries
hard to avoid using a window recently used by `display-buffer'.
Distinctive features are:
- Do not use the selected window.
- Try first to reuse a window that shows BUFFER already on a
frame specified by a `reusable-frames' ALIST entry, using the
selected frame if no such entry has been specified.
- Next try to show BUFFER in the least recently used window. The
frames to search for such a window can be specified via a
`lru-frames' ALIST entry; if no such entry exists, search the
selected frame only. In addition, try to satisfy constraints
specified by the following ALIST entries, if present:
`lru-time' specifies a use time. Do not return a window whose
use time is higher than this. When calling this action
function repeatedly (presumably to display several buffers in
a row), an application should first save the use time of the
selected window and pass that same value via such an entry in
each call of `display-buffer'. This reduces the probability
that `display-buffer' uses the same window as a previous
call.
`window-min-width' specifies a preferred minimum width in
canonical frame columns. If it is the constant `full-width',
prefer a full-width window.
`window-min-height' specifies a preferred minimum height in
canonical frame lines. If it is the constant `full-height',
prefer a full-height window.
- If the preceding steps fail, try to pop up a new window on the
selected frame.
If a window is found, bump the use time of that window to the
highest use time after the selected window. This makes it less
probable that a future invocation of this function uses that
window for another buffer."
(let* ((alist (cons (cons 'inhibit-same-window t) alist))
(window
(or (display-buffer-reuse-window buffer alist)
(let ((window (display-buffer--lru-window alist)))
(when (window-live-p window)
(let* ((quit-restore (window-parameter window 'quit-restore))
(quad (nth 1 quit-restore)))
;; If the window was used by `display-buffer' before, try to
;; resize it to its old height but don't signal an error.
(when (and (listp quad)
(integerp (nth 3 quad))
(> (nth 3 quad) (window-total-height window)))
(condition-case nil
(window-resize
window (- (nth 3 quad) (window-total-height window)))
(error nil)))
(prog1
(window--display-buffer buffer window 'reuse alist)
(window--even-window-sizes window)
(unless (cdr (assq 'inhibit-switch-frame alist))
(window--maybe-raise-frame (window-frame window)))))))
(display-buffer-pop-up-window buffer alist))))
;; Don't bump use time twice.
(when (and window (not (cdr (assq 'bump-use-time alist))))
(window-bump-use-time window))
window))
(defun display-buffer-no-window (_buffer alist)
"Display BUFFER in no window.
ALIST is an association list of action symbols and values. See