diff options
Diffstat (limited to 'lisp/window.el')
-rw-r--r-- | lisp/window.el | 981 |
1 files changed, 677 insertions, 304 deletions
diff --git a/lisp/window.el b/lisp/window.el index a86c2f96bdc..8b12c4381f4 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -181,7 +181,7 @@ This construct is similar to `with-output-to-temp-buffer' but, neither runs `temp-buffer-setup-hook' which usually puts the buffer in Help mode, nor `temp-buffer-show-function' (the ACTION argument replaces this)." - (declare (debug t)) + (declare (debug t) (indent 3)) (let ((buffer (make-symbol "buffer")) (window (make-symbol "window")) (value (make-symbol "value"))) @@ -204,7 +204,7 @@ argument replaces this)." This construct is like `with-temp-buffer-window' but unlike that, makes the buffer specified by BUFFER-OR-NAME current for running BODY." - (declare (debug t)) + (declare (debug t) (indent 3)) (let ((buffer (make-symbol "buffer")) (window (make-symbol "window")) (value (make-symbol "value"))) @@ -226,7 +226,7 @@ BODY." "Show a buffer BUFFER-OR-NAME and evaluate BODY in that buffer. This construct is like `with-current-buffer-window' but unlike that, displays the buffer specified by BUFFER-OR-NAME before running BODY." - (declare (debug t)) + (declare (debug t) (indent 3)) (let ((buffer (make-symbol "buffer")) (window (make-symbol "window")) (value (make-symbol "value"))) @@ -434,7 +434,8 @@ shorter, explicitly specify the SIZE argument of that function." (defun window-min-pixel-height (&optional window) "Return the minimum pixel height of window WINDOW." - (* (max window-min-height window-safe-min-height) + (* (max (if (window-minibuffer-p window) 1 window-min-height) + window-safe-min-height) (frame-char-size window))) ;; This must go to C, finally (or get removed). @@ -509,11 +510,14 @@ child if WINDOW is a horizontal combination." (window-left-child window) (window-top-child window))) -(defun window-combinations (window &optional horizontal) +(defun window-combinations (window &optional horizontal ignore-fixed) "Return largest number of windows vertically arranged within WINDOW. WINDOW must be a valid window and defaults to the selected one. If HORIZONTAL is non-nil, return the largest number of -windows horizontally arranged within WINDOW." +windows horizontally arranged within WINDOW. + +Optional argument IGNORE-FIXED, if non-nil, means to ignore +fixed-size windows in the calculation." (setq window (window-normalize-window window)) (cond ((window-live-p window) @@ -527,9 +531,10 @@ windows horizontally arranged within WINDOW." (let ((child (window-child window)) (count 0)) (while child - (setq count - (+ (window-combinations child horizontal) - count)) + (unless (and ignore-fixed (window-size-fixed-p child horizontal)) + (setq count + (+ (window-combinations child horizontal ignore-fixed) + count))) (setq child (window-right child))) count)) (t @@ -538,9 +543,10 @@ windows horizontally arranged within WINDOW." (let ((child (window-child window)) (count 1)) (while child - (setq count - (max (window-combinations child horizontal) - count)) + (unless (and ignore-fixed (window-size-fixed-p child horizontal)) + (setq count + (max (window-combinations child horizontal ignore-fixed) + count))) (setq child (window-right child))) count)))) @@ -571,23 +577,25 @@ FRAME. Optional argument MINIBUF t means run FUN on FRAME's minibuffer window even if it isn't active. MINIBUF nil or omitted means run -FUN on FRAME's minibuffer window only if it's active. In both -cases the minibuffer window must be part of FRAME. MINIBUF +FUN on FRAME's minibuffer window only if it's active. In either +case the minibuffer window must be part of FRAME. MINIBUF neither nil nor t means never run FUN on the minibuffer window. This function performs a pre-order, depth-first traversal of the window tree. If FUN changes the window tree, the result is unpredictable." - (setq frame (window-normalize-frame frame)) - (walk-window-tree-1 fun (frame-root-window frame) any) - (when (memq minibuf '(nil t)) + (let ((root (frame-root-window frame)) + (mini (minibuffer-window frame))) + (setq frame (window-normalize-frame frame)) + (unless (eq root mini) + (walk-window-tree-1 fun root any)) ;; Run FUN on FRAME's minibuffer window if requested. - (let ((minibuffer-window (minibuffer-window frame))) - (when (and (window-live-p minibuffer-window) - (eq (window-frame minibuffer-window) frame) - (or (eq minibuf t) - (minibuffer-window-active-p minibuffer-window))) - (funcall fun minibuffer-window))))) + (when (and (window-live-p mini) + (eq (window-frame mini) frame) + (or (eq minibuf t) + (and (not minibuf) + (minibuffer-window-active-p mini)))) + (funcall fun mini)))) (defun walk-window-subtree (fun &optional window any) "Run function FUN on the subtree of windows rooted at WINDOW. @@ -700,8 +708,7 @@ failed." (set-window-parameter window 'window-atom 'main)) (set-window-parameter new 'window-atom side) ;; Display BUFFER in NEW and return NEW. - (window--display-buffer - buffer new 'window alist display-buffer-mark-dedicated)))) + (window--display-buffer buffer new 'window alist)))) (defun window--atom-check-1 (window) "Subroutine of `window--atom-check'." @@ -958,7 +965,11 @@ and may be called only if no window on SIDE exists yet." ;; window and not make a new parent window unless needed. (window-combination-resize 'side) (window-combination-limit nil) - (window (split-window-no-error next-to nil on-side))) + (window (split-window-no-error next-to nil on-side)) + (alist (if (assq 'dedicated alist) + alist + (cons `(dedicated . ,(or display-buffer-mark-dedicated 'side)) + alist)))) (when window ;; Initialize `window-side' parameter of new window to SIDE and ;; make that parameter persistent. @@ -985,7 +996,7 @@ and may be called only if no window on SIDE exists yet." (with-current-buffer buffer (setq window--sides-shown t)) ;; Install BUFFER in new window and return WINDOW. - (window--display-buffer buffer window 'window alist 'side)))) + (window--display-buffer buffer window 'window alist)))) (defun display-buffer-in-side-window (buffer alist) "Display BUFFER in a side window of the selected frame. @@ -1019,10 +1030,7 @@ nor installs any other window parameters unless they have been explicitly provided via a `window-parameters' entry in ALIST." (let* ((side (or (cdr (assq 'side alist)) 'bottom)) (slot (or (cdr (assq 'slot alist)) 0)) - (left-or-right (memq side '(left right))) - ;; Softly dedicate window to BUFFER unless - ;; `display-buffer-mark-dedicated' already asks for it. - (dedicated (or display-buffer-mark-dedicated 'side))) + (left-or-right (memq side '(left right)))) (cond ((not (memq side '(top bottom left right))) (error "Invalid side %s specified" side)) @@ -1055,7 +1063,11 @@ explicitly provided via a `window-parameters' entry in ALIST." ((eq side 'bottom) 3)) window-sides-slots)) (window--sides-inhibit-check t) - window this-window this-slot prev-window next-window + (alist (if (assq 'dedicated alist) + alist + (cons `(dedicated . ,(or display-buffer-mark-dedicated 'side)) + alist))) + window this-window this-slot prev-window next-window best-window best-slot abs-slot) (cond @@ -1113,8 +1125,7 @@ explicitly provided via a `window-parameters' entry in ALIST." ;; Reuse `this-window'. (with-current-buffer buffer (setq window--sides-shown t)) - (window--display-buffer - buffer this-window 'reuse alist dedicated)) + (window--display-buffer buffer this-window 'reuse alist)) (and (or (not max-slots) (< slots max-slots)) (or (and next-window ;; Make new window before `next-window'. @@ -1131,8 +1142,7 @@ explicitly provided via a `window-parameters' entry in ALIST." (set-window-parameter window 'window-slot slot) (with-current-buffer buffer (setq window--sides-shown t)) - (window--display-buffer - buffer window 'window alist dedicated)) + (window--display-buffer buffer window 'window alist)) (and best-window ;; Reuse `best-window'. (progn @@ -1141,7 +1151,7 @@ explicitly provided via a `window-parameters' entry in ALIST." (with-current-buffer buffer (setq window--sides-shown t)) (window--display-buffer - buffer best-window 'reuse alist dedicated))))))))) + buffer best-window 'reuse alist))))))))) (defun window-toggle-side-windows (&optional frame) "Toggle display of side windows on specified FRAME. @@ -1594,8 +1604,6 @@ return the minimum pixel-size of WINDOW." value) (with-current-buffer (window-buffer window) (cond - ((window-minibuffer-p window) - (if pixelwise (frame-char-height (window-frame window)) 1)) ((window-size-fixed-p window horizontal ignore) ;; The minimum size of a fixed size window is its size. (window-size window horizontal pixelwise)) @@ -2041,6 +2049,8 @@ doc-string of `window-resizable'." ;; Aliases of functions defined in window.c. (defalias 'window-height 'window-total-height) (defalias 'window-width 'window-body-width) +(defalias 'window-pixel-width-before-size-change 'window-old-pixel-width) +(defalias 'window-pixel-height-before-size-change 'window-old-pixel-height) (defun window-full-height-p (&optional window) "Return t if WINDOW is as high as its containing frame. @@ -2262,14 +2272,14 @@ SIDE can be any of the symbols `left', `top', `right' or "Return window in DIRECTION as seen from WINDOW. More precisely, return the nearest window in direction DIRECTION as seen from the position of `window-point' in window WINDOW. -DIRECTION must be one of `above', `below', `left' or `right'. +DIRECTION should be one of 'above', 'below', 'left' or 'right'. WINDOW must be a live window and defaults to the selected one. -Do not return a window whose `no-other-window' parameter is -non-nil. If the nearest window's `no-other-window' parameter is +Do not return a window whose 'no-other-window' parameter is +non-nil. If the nearest window's 'no-other-window' parameter is non-nil, try to find another window in the indicated direction. If, however, the optional argument IGNORE is non-nil, return that -window even if its `no-other-window' parameter is non-nil. +window even if its 'no-other-window' parameter is non-nil. Optional argument SIGN a negative number means to use the right or bottom edge of WINDOW as reference position instead of @@ -2278,7 +2288,7 @@ top edge of WINDOW as reference position. Optional argument WRAP non-nil means to wrap DIRECTION around frame borders. This means to return for WINDOW at the top of the -frame and DIRECTION `above' the minibuffer window if the frame +frame and DIRECTION 'above' the minibuffer window if the frame has one, and a window at the bottom of the frame otherwise. Optional argument MINI nil means to return the minibuffer window @@ -2288,8 +2298,13 @@ if WRAP is non-nil, always act as if MINI were nil. Return nil if no suitable window can be found." (setq window (window-normalize-window window t)) - (unless (memq direction '(above below left right)) - (error "Wrong direction %s" direction)) + (cond + ((eq direction 'up) + (setq direction 'above)) + ((eq direction 'down) + (setq direction 'below)) + ((not (memq direction '(above below left right))) + (error "Wrong direction %s" direction))) (let* ((frame (window-frame window)) (hor (memq direction '(left right))) (first (if hor @@ -2723,49 +2738,50 @@ windows." (when (window-right window) (window--resize-reset-1 (window-right window) horizontal))) +;; The following is the internal function used when resizing mini +;; windows "manually", for example, when dragging a divider between +;; root and mini window. The routines for automatic minibuffer window +;; resizing call `window--resize-root-window-vertically' instead. (defun window--resize-mini-window (window delta) - "Resize minibuffer window WINDOW by DELTA pixels. + "Change height of mini window WINDOW by DELTA pixels. If WINDOW cannot be resized by DELTA pixels make it as large (or as small) as possible, but don't signal an error." (when (window-minibuffer-p window) (let* ((frame (window-frame window)) (root (frame-root-window frame)) (height (window-pixel-height window)) - (min-delta - (- (window-pixel-height root) - (window-min-size root nil nil t)))) - ;; Sanitize DELTA. - (cond - ((<= (+ height delta) 0) - (setq delta (- (frame-char-height (window-frame window)) height))) - ((> delta min-delta) - (setq delta min-delta))) + (min-height (+ (frame-char-height frame) + (- (window-pixel-height window) + (window-body-height window t)))) + (max-delta (- (window-pixel-height root) + (window-min-size root nil nil t)))) + ;; Don't make mini window too small. + (when (< (+ height delta) min-height) + (setq delta (- min-height height))) + ;; Don't make root window too small. + (when (> delta max-delta) + (setq delta max-delta)) (unless (zerop delta) - ;; Resize now. (window--resize-reset frame) - ;; Ideally we should be able to resize just the last child of root - ;; here. See the comment in `resize-root-window-vertically' for - ;; why we do not do that. (window--resize-this-window root (- delta) nil nil t) (set-window-new-pixel window (+ height delta)) ;; The following routine catches the case where we want to resize ;; a minibuffer-only frame. (when (resize-mini-window-internal window) - (window--pixel-to-total frame) - (run-window-configuration-change-hook frame)))))) + (window--pixel-to-total frame)))))) (defun window--resize-apply-p (frame &optional horizontal) "Return t when a window on FRAME shall be resized vertically. Optional argument HORIZONTAL non-nil means return t when a window shall be resized horizontally." -(catch 'apply + (catch 'apply (walk-window-tree (lambda (window) (unless (= (window-new-pixel window) (window-size window horizontal t)) (throw 'apply t))) - frame t) + frame t t) nil)) (defun window-resize (window delta &optional horizontal ignore pixelwise) @@ -2851,9 +2867,7 @@ instead." (window--resize-siblings window delta horizontal ignore)) (when (window--resize-apply-p frame horizontal) (if (window-resize-apply frame horizontal) - (progn - (window--pixel-to-total frame horizontal) - (run-window-configuration-change-hook frame)) + (window--pixel-to-total frame horizontal) (error "Failed to apply resizing %s" window)))) (t (error "Cannot resize window %s" window))))) @@ -3084,11 +3098,12 @@ already set by this routine." (while (and best-window (not (zerop delta))) (setq sub last) (setq best-window nil) - (setq best-value most-negative-fixnum) + (setq best-value nil) (while sub (when (and (consp (window-new-normal sub)) (not (<= (car (window-new-normal sub)) 0)) - (> (cdr (window-new-normal sub)) best-value)) + (or (not best-value) + (> (cdr (window-new-normal sub)) best-value))) (setq best-window sub) (setq best-value (cdr (window-new-normal sub)))) @@ -3113,10 +3128,11 @@ already set by this routine." (while (and best-window (not (zerop delta))) (setq sub last) (setq best-window nil) - (setq best-value most-positive-fixnum) + (setq best-value nil) (while sub (when (and (numberp (window-new-normal sub)) - (< (window-new-normal sub) best-value)) + (or (not best-value) + (< (window-new-normal sub) best-value))) (setq best-window sub) (setq best-value (window-new-normal sub))) @@ -3366,6 +3382,12 @@ routines." pixel-delta (/ pixel-delta (frame-char-height frame))))) +(defun window--resize-mini-frame (frame) + "Resize minibuffer-only frame FRAME." + (if (functionp resize-mini-frames) + (funcall resize-mini-frames frame) + (fit-frame-to-buffer frame))) + (defun window--sanitize-window-sizes (horizontal) "Assert that all windows on selected frame are large enough. If necessary and possible, make sure that every window on frame @@ -3385,7 +3407,8 @@ may happen when the FRAME is not large enough to accommodate it." (when (> delta 0) (if (window-resizable-p window delta horizontal nil t) (window-resize window delta horizontal nil t) - (setq value nil)))))) + (setq value nil))))) + nil nil 'nomini) value)) (defun adjust-window-trailing-edge (window delta &optional horizontal pixelwise) @@ -3570,9 +3593,7 @@ move it as far as possible in the desired direction." ;; Don't report an error in the standard case. (when (window--resize-apply-p frame horizontal) (if (window-resize-apply frame horizontal) - (progn - (window--pixel-to-total frame horizontal) - (run-window-configuration-change-hook frame)) + (window--pixel-to-total frame horizontal) ;; But do report an error if applying the changes fails. (error "Failed adjusting window %s" window)))))))) @@ -3686,6 +3707,8 @@ WINDOW must be a valid window and defaults to the selected one. If the option `window-resize-pixelwise' is non-nil minimize WINDOW pixelwise." (interactive) + (when switch-to-buffer-preserve-window-point + (window--before-delete-windows window)) (setq window (window-normalize-window window)) (window-resize window @@ -4023,6 +4046,41 @@ frame can be safely deleted." (throw 'done t) (setq parent (window-parent parent)))))))) +;; This function is called by `delete-window' and +;; `delete-other-windows' when `switch-to-buffer-preserve-window-point' +;; evaluates non-nil: it allows `winner-undo' to restore the +;; buffer point from deleted windows (Bug#23621). +(defun window--before-delete-windows (&optional window) + "Update `window-prev-buffers' before delete a window. +Optional arg WINDOW, if non-nil, update WINDOW-START and POS +in `window-prev-buffers' for all windows displaying same +buffer as WINDOW. Otherwise, update `window-prev-buffers' for +all windows. + +The new values for WINDOW-START and POS are those +returned by `window-start' and `window-point' respectively. + +This function is called only if `switch-to-buffer-preserve-window-point' +evaluates non-nil." + (dolist (win (window-list)) + (let* ((buf (window-buffer (or window win))) + (start (window-start win)) + (pos (window-point win)) + (entry (assq buf (window-prev-buffers win)))) + (cond (entry + (let ((marker (nth 2 entry))) + (unless (= pos marker) + (set-marker (nth 1 entry) start buf) + (set-marker marker pos buf)))) + (t + (let ((prev-buf (window-prev-buffers win)) + (start-m (make-marker)) + (pos-m (make-marker))) + (set-marker start-m start buf) + (set-marker pos-m pos buf) + (push (list buf start-m pos-m) prev-buf) + (set-window-prev-buffers win prev-buf))))))) + (defun delete-window (&optional window) "Delete WINDOW. WINDOW must be a valid window and defaults to the selected one. @@ -4041,6 +4099,8 @@ argument. Signal an error if WINDOW is either the only window on its frame, the last non-side window, or part of an atomic window that is its frame's root window." (interactive) + (when switch-to-buffer-preserve-window-point + (window--before-delete-windows)) (setq window (window-normalize-window window)) (let* ((frame (window-frame window)) (function (window-parameter window 'delete-window)) @@ -4103,7 +4163,6 @@ that is its frame's root window." ;; `delete-window-internal' has selected a window that should ;; not be selected, fix this here. (other-window -1 frame)) - (run-window-configuration-change-hook frame) (window--check frame) ;; Always return nil. nil)))) @@ -4166,7 +4225,8 @@ any window whose `no-delete-other-windows' parameter is non-nil." (and (not (window-parameter other 'window-side)) (window-parameter other 'no-delete-other-windows))) - (throw 'tag nil)))) + (throw 'tag nil))) + nil nil 'nomini) t) (setq main (window-main-window frame))) (t @@ -4189,7 +4249,6 @@ any window whose `no-delete-other-windows' parameter is non-nil." ;; If WINDOW is the main window of its frame do nothing. (unless (eq window main) (delete-other-windows-internal window main) - (run-window-configuration-change-hook frame) (window--check frame)) ;; Always return nil. nil))) @@ -4271,7 +4330,7 @@ WINDOW must be a live window and defaults to the selected one." (list (copy-marker start) (copy-marker ;; Preserve window-point-insertion-type - ;; (Bug#12588). + ;; (Bug#12855). point window-point-insertion-type))))) (set-window-prev-buffers window (cons entry (window-prev-buffers window))))) @@ -4721,7 +4780,7 @@ 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." - (interactive "BDelete windows on (buffer):\nP") + (interactive "bDelete 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. @@ -4904,7 +4963,7 @@ BUFFER-OR-NAME. Optional argument FRAME is handled as by This function calls `quit-window' on all candidate windows showing BUFFER-OR-NAME." - (interactive "BQuit windows on (buffer):\nP") + (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. @@ -4915,6 +4974,24 @@ showing BUFFER-OR-NAME." ;; If a window doesn't show BUFFER, unrecord BUFFER in it. (unrecord-window-buffer window buffer))))) +(defun window--combination-resizable (parent &optional horizontal) + "Return number of pixels recoverable from height of window PARENT. +PARENT must be a vertical (horizontal if HORIZONTAL is non-nil) +window combination. The return value is the sum of the pixel +heights of all non-fixed height child windows of PARENT divided +by their number plus 1. If HORIZONTAL is non-nil, return the sum +of the pixel widths of all non-fixed width child windows of +PARENT divided by their number plus 1." + (let ((sibling (window-child parent)) + (number 0) + (size 0)) + (while sibling + (unless (window-size-fixed-p sibling horizontal) + (setq number (1+ number)) + (setq size (+ (window-size sibling horizontal t) size))) + (setq sibling (window-next-sibling sibling))) + (/ size (1+ number)))) + (defun split-window (&optional window size side pixelwise) "Make a new window adjacent to WINDOW. WINDOW must be a valid window and defaults to the selected one. @@ -4928,26 +5005,29 @@ absolute value can be less than `window-min-height' or small as one line or two columns. SIZE defaults to half of WINDOW's size. -Optional third argument SIDE nil (or `below') specifies that the -new window shall be located below WINDOW. SIDE `above' means the +Optional third argument SIDE nil (or 'below') specifies that the +new window shall be located below WINDOW. SIDE 'above' means the new window shall be located above WINDOW. In both cases SIZE specifies the new number of lines for WINDOW (or the new window if SIZE is negative) including space reserved for the mode and/or header line. -SIDE t (or `right') specifies that the new window shall be -located on the right side of WINDOW. SIDE `left' means the new +SIDE t (or 'right') specifies that the new window shall be +located on the right side of WINDOW. SIDE 'left' means the new window shall be located on the left of WINDOW. In both cases SIZE specifies the new number of columns for WINDOW (or the new window provided SIZE is negative) including space reserved for -fringes and the scrollbar or a divider column. Any other non-nil -value for SIDE is currently handled like t (or `right'). +fringes and the scrollbar or a divider column. + +For compatibility reasons, SIDE 'up' and 'down' are interpreted +as 'above' and 'below'. Any other non-nil value for SIDE is +currently handled like t (or 'right'). PIXELWISE, if non-nil, means to interpret SIZE pixelwise. If the variable `ignore-window-parameters' is non-nil or the -`split-window' parameter of WINDOW equals t, do not process any -parameters of WINDOW. Otherwise, if the `split-window' parameter +'split-window' parameter of WINDOW equals t, do not process any +parameters of WINDOW. Otherwise, if the 'split-window' parameter of WINDOW specifies a function, call that function with all three arguments and return the value returned by that function. @@ -4963,6 +5043,8 @@ frame. The selected window is not changed by this function." (setq window (window-normalize-window window)) (let* ((side (cond ((not side) 'below) + ((eq side 'up) 'above) + ((eq side 'down) 'below) ((memq side '(below above right left)) side) (t 'right))) (horizontal (not (memq side '(below above)))) @@ -4986,10 +5068,10 @@ frame. The selected window is not changed by this function." (catch 'done (cond ;; Ignore window parameters if either `ignore-window-parameters' - ;; is t or the `split-window' parameter equals t. + ;; is t or the 'split-window' parameter equals t. ((or ignore-window-parameters (eq function t))) ((functionp function) - ;; The `split-window' parameter specifies the function to call. + ;; The 'split-window' parameter specifies the function to call. ;; If that function is `ignore', do nothing. (throw 'done (funcall function window size side))) ;; If WINDOW is part of an atomic window, split the root window @@ -5022,10 +5104,10 @@ frame. The selected window is not changed by this function." (setq window-combination-limit t)) (let* ((parent-pixel-size - ;; `parent-pixel-size' is the pixel size of WINDOW's + ;; 'parent-pixel-size' is the pixel size of WINDOW's ;; parent, provided it has one. (when parent (window-size parent horizontal t))) - ;; `resize' non-nil means we are supposed to resize other + ;; 'resize' non-nil means we are supposed to resize other ;; windows in WINDOW's combination. (resize (and window-combination-resize @@ -5034,9 +5116,9 @@ frame. The selected window is not changed by this function." (not (eq window-combination-limit t)) ;; Resize makes sense in iso-combinations only. (window-combined-p window horizontal))) - ;; `old-pixel-size' is the current pixel size of WINDOW. + ;; 'old-pixel-size' is the current pixel size of WINDOW. (old-pixel-size (window-size window horizontal t)) - ;; `new-size' is the specified or calculated size of the + ;; 'new-size' is the specified or calculated size of the ;; new window. new-pixel-size new-parent new-normal) (cond @@ -5047,8 +5129,7 @@ frame. The selected window is not changed by this function." ;; average size of a window in its combination. (max (min (- parent-pixel-size (window-min-size parent horizontal nil t)) - (/ parent-pixel-size - (1+ (window-combinations parent horizontal)))) + (window--combination-resizable parent horizontal)) (window-min-pixel-size)) ;; Else try to give the new window half the size ;; of WINDOW (plus an eventual odd pixel). @@ -5133,7 +5214,7 @@ frame. The selected window is not changed by this function." (pixel-size (/ (float new-pixel-size) (if new-parent old-pixel-size parent-pixel-size))) (new-parent 0.5) - (resize (/ 1.0 (1+ (window-combinations parent horizontal)))) + (resize (/ 1.0 (1+ (window-combinations parent horizontal t)))) (t (/ (window-normal-size window horizontal) 2.0)))) (if resize @@ -5190,7 +5271,6 @@ frame. The selected window is not changed by this function." (unless size (window--sanitize-window-sizes horizontal)) - (run-window-configuration-change-hook frame) (run-window-scroll-functions new) (window--check frame) ;; Always return the new window. @@ -5323,11 +5403,12 @@ is non-nil)." (total-sum parent-size) failed size sub-total sub-delta sub-amount rest) (while sub - (setq number-of-children (1+ number-of-children)) - (when (window-size-fixed-p sub horizontal) - (setq total-sum - (- total-sum (window-size sub horizontal t))) - (set-window-new-normal sub 'ignore)) + (if (window-size-fixed-p sub horizontal) + (progn + (setq total-sum + (- total-sum (window-size sub horizontal t))) + (set-window-new-normal sub 'ignore)) + (setq number-of-children (1+ number-of-children))) (setq sub (window-right sub))) (setq failed t) @@ -5352,16 +5433,16 @@ is non-nil)." (set-window-new-normal sub 'skip))) (setq sub (window-right sub)))) - ;; How can we be sure that `number-of-children' is NOT zero here ? - (setq rest (% total-sum number-of-children)) - ;; Fix rounding by trying to enlarge non-stuck windows by one line - ;; (column) until `rest' is zero. - (setq sub first) - (while (and sub (> rest 0)) - (unless (window--resize-child-windows-skip-p window) - (set-window-new-pixel sub (min rest char-size) t) - (setq rest (- rest char-size))) - (setq sub (window-right sub))) + (when (> number-of-children 0) + (setq rest (% total-sum number-of-children)) + ;; Fix rounding by trying to enlarge non-stuck windows by one line + ;; (column) until `rest' is zero. + (setq sub first) + (while (and sub (> rest 0)) + (unless (window--resize-child-windows-skip-p window) + (set-window-new-pixel sub (min rest char-size) t) + (setq rest (- rest char-size))) + (setq sub (window-right sub)))) ;; Fix rounding by trying to enlarge stuck windows by one line ;; (column) until `rest' equals zero. @@ -5420,15 +5501,13 @@ window." (balance-windows-1 window) (when (window--resize-apply-p frame) (window-resize-apply frame) - (window--pixel-to-total frame) - (run-window-configuration-change-hook frame)) + (window--pixel-to-total frame)) ;; Balance horizontally. (window--resize-reset (window-frame window) t) (balance-windows-1 window t) (when (window--resize-apply-p frame t) (window-resize-apply frame t) - (window--pixel-to-total frame t) - (run-window-configuration-change-hook frame)))) + (window--pixel-to-total frame t)))) (defun window-fixed-size-p (&optional window direction) "Return t if WINDOW cannot be resized in DIRECTION. @@ -5557,9 +5636,18 @@ specific buffers." (t 'leaf))) (buffer (window-buffer window)) (selected (eq window (selected-window))) + (next-buffers (when (window-live-p window) + (delq nil (mapcar (lambda (buffer) + (and (buffer-live-p buffer) buffer)) + (window-next-buffers window))))) + (prev-buffers (when (window-live-p window) + (delq nil (mapcar (lambda (entry) + (and (buffer-live-p (nth 0 entry)) + entry)) + (window-prev-buffers window))))) (head `(,type - ,@(unless (window-next-sibling window) `((last . t))) + ,@(unless (window-next-sibling window) '((last . t))) (pixel-width . ,(window-pixel-width window)) (pixel-height . ,(window-pixel-height window)) (total-width . ,(window-total-width window)) @@ -5591,7 +5679,7 @@ specific buffers." (let ((point (window-point window)) (start (window-start window))) `((buffer - ,(buffer-name buffer) + ,(if writable (buffer-name buffer) buffer) (selected . ,selected) (hscroll . ,(window-hscroll window)) (fringes . ,(window-fringes window)) @@ -5609,7 +5697,22 @@ specific buffers." (start . ,(if writable start (with-current-buffer buffer - (copy-marker start)))))))))) + (copy-marker start)))))))) + ,@(when next-buffers + `((next-buffers + . ,(if writable + (mapcar (lambda (buffer) (buffer-name buffer)) + next-buffers) + next-buffers)))) + ,@(when prev-buffers + `((prev-buffers + . ,(if writable + (mapcar (lambda (entry) + (list (buffer-name (nth 0 entry)) + (marker-position (nth 1 entry)) + (marker-position (nth 2 entry)))) + prev-buffers) + prev-buffers)))))) (tail (when (memq type '(vc hc)) (let (list) @@ -5752,7 +5855,9 @@ value can be also stored on disk and read back in a new session." (let ((window (car item)) (combination-limit (cdr (assq 'combination-limit item))) (parameters (cdr (assq 'parameters item))) - (state (cdr (assq 'buffer item)))) + (state (cdr (assq 'buffer item))) + (next-buffers (cdr (assq 'next-buffers item))) + (prev-buffers (cdr (assq 'prev-buffers item)))) (when combination-limit (set-window-combination-limit window combination-limit)) ;; Reset window's parameters and assign saved ones (we might want @@ -5764,7 +5869,8 @@ value can be also stored on disk and read back in a new session." (set-window-parameter window (car parameter) (cdr parameter)))) ;; Process buffer related state. (when state - (let ((buffer (get-buffer (car state)))) + (let ((buffer (get-buffer (car state))) + (state (cdr state))) (if buffer (with-current-buffer buffer (set-window-buffer window buffer) @@ -5776,7 +5882,7 @@ value can be also stored on disk and read back in a new session." (let ((scroll-bars (cdr (assq 'scroll-bars state)))) (set-window-scroll-bars window (car scroll-bars) (nth 2 scroll-bars) - (nth 3 scroll-bars) (nth 5 scroll-bars))) + (nth 3 scroll-bars) (nth 5 scroll-bars) (nth 6 scroll-bars))) (set-window-vscroll window (cdr (assq 'vscroll state))) ;; Adjust vertically. (if (or (memq window-size-fixed '(t height)) @@ -5833,7 +5939,30 @@ value can be also stored on disk and read back in a new session." (set-window-point window (cdr (assq 'point state)))) ;; Select window if it's the selected one. (when (cdr (assq 'selected state)) - (select-window window))) + (select-window window)) + (when next-buffers + (set-window-next-buffers + window + (delq nil (mapcar (lambda (buffer) + (setq buffer (get-buffer buffer)) + (when (buffer-live-p buffer) buffer)) + next-buffers)))) + (when prev-buffers + (set-window-prev-buffers + window + (delq nil (mapcar (lambda (entry) + (let ((buffer (get-buffer (nth 0 entry))) + (m1 (nth 1 entry)) + (m2 (nth 2 entry))) + (when (buffer-live-p buffer) + (list buffer + (if (markerp m1) m1 + (set-marker (make-marker) m1 + buffer)) + (if (markerp m2) m2 + (set-marker (make-marker) m2 + buffer)))))) + prev-buffers))))) ;; We don't want to raise an error in case the buffer does ;; not exist anymore, so we switch to a previous one and ;; save the window with the intention of deleting it later @@ -5845,29 +5974,34 @@ value can be also stored on disk and read back in a new session." "Put window state STATE into WINDOW. STATE should be the state of a window returned by an earlier invocation of `window-state-get'. Optional argument WINDOW must -specify a valid window and defaults to the selected one. If -WINDOW is not live, replace WINDOW by a live one before putting -STATE into it. +specify a valid window. If WINDOW is not a live window, +replace WINDOW by a new live window created on the same frame. +If WINDOW is nil, create a new window before putting STATE into it. Optional argument IGNORE non-nil means ignore minimum window sizes and fixed size restrictions. IGNORE equal `safe' means windows can get as small as `window-safe-min-height' and `window-safe-min-width'." (setq window-state-put-stale-windows nil) - (setq window (window-normalize-window window)) - ;; When WINDOW is internal, reduce it to a live one to put STATE into, - ;; see Bug#16793. + ;; When WINDOW is internal or nil, reduce it to a live one, + ;; then create a new window on the same frame to put STATE into. (unless (window-live-p window) (let ((root window)) - (setq window (catch 'live - (walk-window-subtree - (lambda (window) - (when (and (window-live-p window) - (not (window-parameter window 'window-side))) - (throw 'live window))) - root))) - (delete-other-windows-internal window root))) + (setq window (if root + (catch 'live + (walk-window-subtree + (lambda (window) + (when (and (window-live-p window) + (not (window-parameter + window 'window-side))) + (throw 'live window))) + root)) + (selected-window))) + (delete-other-windows-internal window root) + ;; Create a new window to replace the existing one. + (setq window (prog1 (split-window window) + (delete-window window))))) (set-window-dedicated-p window nil) @@ -6023,23 +6157,26 @@ not resized by this function." (defun display-buffer-record-window (type window buffer) "Record information for window used by `display-buffer'. +WINDOW is the window used for or created by a buffer display +action function. BUFFER is the buffer to display. Note that +this function must be called before BUFFER is explicitly made +WINDOW's buffer (although WINDOW may show BUFFER already). + TYPE specifies the type of the calling operation and must be one -of the symbols `reuse' (when WINDOW existed already and was -reused for displaying BUFFER), `window' (when WINDOW was created -on an already existing frame), or `frame' (when WINDOW was -created on a new frame). WINDOW is the window used for or created -by the `display-buffer' routines. BUFFER is the buffer that -shall be displayed. - -This function installs or updates the quit-restore parameter of -WINDOW. The quit-restore parameter is a list of four elements: -The first element is one of the symbols `window', `frame', `same' or -`other'. The second element is either one of the symbols `window' -or `frame' or a list whose elements are the buffer 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." +of the symbols 'reuse' (meaning that WINDOW exists already and +will be used for displaying BUFFER), 'window' (WINDOW was created +on an already existing frame) or 'frame' (WINDOW was created on a +new frame). + +This function installs or updates the 'quit-restore' parameter of +WINDOW. The 'quit-restore' parameter is a list of four elements: +The first element is one of the symbols 'window', 'frame', 'same' +or 'other'. The second element is either one of the symbols +'window' or 'frame' or a list whose elements are the buffer +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." (cond ((eq type 'reuse) (if (eq (window-buffer window) buffer) @@ -6060,7 +6197,7 @@ element is BUFFER." (list 'other ;; A quadruple of WINDOW's buffer, start, point and height. (list (current-buffer) (window-start window) - ;; Preserve window-point-insertion-type (Bug#12588). + ;; Preserve window-point-insertion-type (Bug#12855). (copy-marker (window-point window) window-point-insertion-type) (if (window-combined-p window) @@ -6608,7 +6745,7 @@ split." (unless (or (eq w window) (window-dedicated-p w)) (throw 'done nil))) - frame) + frame nil 'nomini) t))) (not (window-minibuffer-p window)) (let ((split-height-threshold 0)) @@ -6660,6 +6797,7 @@ represents a live window, nil otherwise." )) frame)))) +(defvaralias 'even-window-heights 'even-window-sizes) (defcustom even-window-sizes t "If non-nil `display-buffer' will try to even window sizes. Otherwise `display-buffer' will leave the window configuration @@ -6673,7 +6811,6 @@ any of them." (const :tag "Always" t)) :version "25.1" :group 'windows) -(defvaralias 'even-window-heights 'even-window-sizes) (defun window--even-window-sizes (window) "Even sizes of WINDOW and selected window. @@ -6698,20 +6835,51 @@ window is larger than WINDOW." (/ (- (window-total-height window) (window-total-height)) 2)) (error nil)))))) -(defun window--display-buffer (buffer window type &optional alist dedicated) +(defun window--display-buffer (buffer window type &optional alist) "Display BUFFER in WINDOW. -TYPE must be one of the symbols `reuse', `window' or `frame' and -is passed unaltered to `display-buffer-record-window'. ALIST is -the alist argument of `display-buffer'. Set `window-dedicated-p' -to DEDICATED if non-nil. Return WINDOW if BUFFER and WINDOW are -live." +WINDOW must be a live window chosen by a buffer display action +function for showing BUFFER. TYPE tells whether WINDOW existed +already before that action function was called or is a new window +created by that function. ALIST is a buffer display action alist +as compiled by `display-buffer'. + +TYPE must be one of the following symbols: 'reuse' (which means +WINDOW existed before the call of `display-buffer' and may +already show BUFFER or not), 'window' (WINDOW was created on an +existing frame) or 'frame' (WINDOW was created on a new frame). +TYPE is passed unaltered to `display-buffer-record-window'. + +Handle WINDOW's dedicated flag as follows: If WINDOW already +shows BUFFER, leave it alone. Otherwise, if ALIST contains a +'dedicated' entry and WINDOW is either new or that entry's value +equals 'side', set WINDOW's dedicated flag to the value of that +entry. Otherwise, if WINDOW is new and the value of +'display-buffer-mark-dedicated' is non-nil, set WINDOW's +dedicated flag to that value. In any other case, reset WINDOW's +dedicated flag to nil. + +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) (unless (eq buffer (window-buffer window)) + ;; Unless WINDOW already shows BUFFER reset its dedicated flag. (set-window-dedicated-p window nil) (set-window-buffer window buffer)) - (when dedicated - (set-window-dedicated-p window dedicated)) + (let ((alist-dedicated (assq 'dedicated alist))) + ;; Maybe dedicate WINDOW to BUFFER if asked for. + (cond + ;; Don't dedicate WINDOW if it is dedicated because it shows + ;; BUFFER already or it is reused and is not a side window. + ((or (window-dedicated-p window) + (and (eq type 'reuse) (not (eq (cdr alist-dedicated) 'side))))) + ;; Otherwise, if ALIST contains a 'dedicated' entry, use that + ;; entry's value (which may be nil). + (alist-dedicated + (set-window-dedicated-p window (cdr alist-dedicated))) + ;; Otherwise, if 'display-buffer-mark-dedicated' is non-nil, + ;; use that. + (display-buffer-mark-dedicated + (set-window-dedicated-p window display-buffer-mark-dedicated)))) (when (memq type '(window frame)) (set-window-prev-buffers window nil)) (let ((quit-restore (window-parameter window 'quit-restore)) @@ -7106,7 +7274,7 @@ on all the frames on the current terminal, skipping the selected window; if that fails, it pops up a new frame. This uses the function `display-buffer' as a subroutine; see its documentation for additional customization information." - (interactive "BDisplay buffer in other frame: ") + (interactive "bDisplay buffer in other frame: ") (display-buffer buffer display-buffer--other-frame-action t)) ;;; `display-buffer' action functions: @@ -7140,8 +7308,7 @@ that allows the selected frame)." frame nil (cdr (assq 'inhibit-same-window alist)))))) (when window (prog1 - (window--display-buffer - buffer window 'reuse alist display-buffer-mark-dedicated) + (window--display-buffer buffer window 'reuse alist) (unless (cdr (assq 'inhibit-switch-frame alist)) (window--maybe-raise-frame frame)))))) @@ -7166,25 +7333,26 @@ return nil." (defun display-buffer-reuse-window (buffer alist) "Return a window that is already displaying BUFFER. -Return nil if no usable window is found. +Preferably use a window on the selected frame if such a window +exists. Return nil if no usable window is found. -If ALIST has a non-nil `inhibit-same-window' entry, the selected +If ALIST has a non-nil 'inhibit-same-window' entry, the selected window is not eligible for reuse. -If ALIST contains a `reusable-frames' entry, its value determines +If ALIST contains a 'reusable-frames' entry, its value determines which frames to search for a reusable window: nil -- the selected frame (actually the last non-minibuffer frame) A frame -- just that frame - `visible' -- all visible frames + 'visible' -- all visible frames 0 -- all frames on the current terminal t -- all frames. -If ALIST contains no `reusable-frames' entry, search just the +If ALIST contains no 'reusable-frames' entry, search just the selected frame if `display-buffer-reuse-frames' and `pop-up-frames' are both nil; search all frames on the current terminal if either of those variables is non-nil. -If ALIST has a non-nil `inhibit-switch-frame' entry, then in the +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." (let* ((alist-entry (assq 'reusable-frames alist)) @@ -7198,9 +7366,21 @@ that frame." (window (if (and (eq buffer (window-buffer)) (not (cdr (assq 'inhibit-same-window alist)))) (selected-window) - (car (delq (selected-window) - (get-buffer-window-list buffer 'nomini - frames)))))) + ;; 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)))))) (when (window-live-p window) (prog1 (window--display-buffer buffer window 'reuse alist) (unless (cdr (assq 'inhibit-switch-frame alist)) @@ -7306,8 +7486,7 @@ new frame." (with-current-buffer buffer (setq frame (funcall fun))) (setq window (frame-selected-window frame))) - (prog1 (window--display-buffer - buffer window 'frame alist display-buffer-mark-dedicated) + (prog1 (window--display-buffer buffer window 'frame alist) (unless (cdr (assq 'inhibit-switch-frame alist)) (window--maybe-raise-frame frame)))))) @@ -7336,8 +7515,7 @@ raising the frame." (window--try-to-split-window (get-lru-window frame t) alist)))) - (prog1 (window--display-buffer - buffer window 'window alist display-buffer-mark-dedicated) + (prog1 (window--display-buffer buffer window 'window alist) (unless (cdr (assq 'inhibit-switch-frame alist)) (window--maybe-raise-frame (window-frame window))))))) @@ -7348,12 +7526,23 @@ text-only terminal), try with `display-buffer-pop-up-frame'. If that cannot be done, and `pop-up-windows' is non-nil, try again with `display-buffer-pop-up-window'." - (or (and (if (eq pop-up-frames 'graphic-only) - (display-graphic-p) - pop-up-frames) - (display-buffer-pop-up-frame buffer alist)) - (and pop-up-windows - (display-buffer-pop-up-window buffer alist)))) + (or (display-buffer--maybe-pop-up-frame buffer alist) + (display-buffer--maybe-pop-up-window buffer alist))) + +(defun display-buffer--maybe-pop-up-frame (buffer alist) + "Try displaying BUFFER based on `pop-up-frames'. +If `pop-up-frames' is non-nil (and not `graphic-only' on a +text-only terminal), try with `display-buffer-pop-up-frame'." + (and (if (eq pop-up-frames 'graphic-only) + (display-graphic-p) + pop-up-frames) + (display-buffer-pop-up-frame buffer alist))) + +(defun display-buffer--maybe-pop-up-window (buffer alist) + "Try displaying BUFFER based on `pop-up-windows'. +If `pop-up-windows' is non-nil, try with `display-buffer-pop-up-window'." + (and pop-up-windows + (display-buffer-pop-up-window buffer alist))) (defun display-buffer-in-child-frame (buffer alist) "Display BUFFER in a child frame. @@ -7374,7 +7563,7 @@ be added to ALIST." (parent (or (assq 'parent-frame parameters) (selected-frame))) (share (assq 'share-child-frame parameters)) - share1 frame window) + share1 frame window type) (with-current-buffer buffer (when (frame-live-p parent) (catch 'frame @@ -7387,38 +7576,220 @@ be added to ALIST." (throw 'frame t)))))) (if frame - (setq window (frame-selected-window frame)) + (progn + (setq window (frame-selected-window frame)) + (setq type 'reuse)) (setq frame (make-frame parameters)) - (setq window (frame-selected-window frame)))) + (setq window (frame-selected-window frame)) + (setq type 'frame))) - (prog1 (window--display-buffer - buffer window 'frame alist display-buffer-mark-dedicated) + (prog1 (window--display-buffer buffer window type alist) (unless (cdr (assq 'inhibit-switch-frame alist)) (window--maybe-raise-frame frame))))) +(defun windows-sharing-edge (&optional window edge within) + "Return list of live windows sharing the same edge with WINDOW. +WINDOW must be a valid window and defaults to the selected one. +EDGE stands for the edge to share and must be either 'left', +'above', 'right' or 'below'. Omitted or nil, EDGE defaults to +'left'. + +WITHIN nil means to find a live window that shares the opposite +EDGE with WINDOW. For example, if EDGE equals 'left', WINDOW has +to share (part of) the right edge of any window returned. WITHIN +non-nil means to find all live windows that share the same EDGE +with WINDOW (Window must be internal in this case). So if EDGE +equals 'left', WINDOW's left edge has to fully encompass the left +edge of any window returned." + (setq window (window-normalize-window window)) + (setq edge (or edge 'left)) + (when (and within (window-live-p window)) + (error "Cannot share edge from within live window %s" window)) + (let ((window-edges (window-edges window nil nil t)) + (horizontal (memq edge '(left right))) + (n (pcase edge + ('left 0) ('above 1) ('right 2) ('below 3)))) + (unless (numberp n) + (error "Invalid EDGE %s" edge)) + (let ((o (mod (+ 2 n) 4)) + (p (if horizontal 1 0)) + (q (if horizontal 3 2)) + windows) + (walk-window-tree + (lambda (other) + (let ((other-edges (window-edges other nil nil t))) + (when (and (not (eq window other)) + (= (nth n window-edges) + (nth (if within n o) other-edges)) + (cond + ((= (nth p window-edges) (nth p other-edges))) + ((< (nth p window-edges) (nth p other-edges)) + (< (nth p other-edges) (nth q window-edges))) + (t + (< (nth p window-edges) (nth q other-edges))))) + (setq windows (cons other windows))))) + (window-frame window) nil 'nomini) + (reverse windows)))) + +(defun window--try-to-split-window-in-direction (window direction alist) + "Try to split WINDOW in DIRECTION. +DIRECTION is passed as SIDE argument to `split-window-no-error'. +ALIST is a buffer display alist." + (and (not (frame-parameter (window-frame window) 'unsplittable)) + (let* ((window-combination-limit + ;; When `window-combination-limit' equals + ;; `display-buffer' or equals `resize-window' and a + ;; `window-height' or `window-width' alist entry are + ;; present, bind it to t so resizing steals space + ;; preferably from the window that was split. + (if (or (eq window-combination-limit 'display-buffer) + (and (eq window-combination-limit 'window-size) + (or (cdr (assq 'window-height alist)) + (cdr (assq 'window-width alist))))) + t + window-combination-limit)) + (new-window (split-window-no-error window nil direction))) + (and (window-live-p new-window) new-window)))) + +(defun display-buffer-in-direction (buffer alist) + "Try to display BUFFER in a direction specified by ALIST. +ALIST has to contain a 'direction' entry whose value should be +one of 'left', 'above' (or 'up'), 'right', and 'below' (or +'down'). Other values are usually interpreted as 'below'. + +If ALIST also contains a 'window' entry, its value specifies a +reference window. That value can be a special symbol like +'main' (which stands for the selected frame's main window) or +'root' (standings for the selected frame's root window) or an +arbitrary valid window. Any other value (or omitting the +'window' entry) means to use the selected window as reference +window. + +This function tries to reuse or split a window such that the +window produced this way is on the side of the reference window +specified by the 'direction' entry. + +Four special values for 'direction' entries allow to implicitly +specify the selected frame's main window as reference window: +'leftmost', 'top', 'rightmost' and 'bottom'. Hence, instead of +'(direction . left) (window . main)' one can simply write +'(direction . leftmost)'." + (let ((direction (cdr (assq 'direction alist)))) + (when direction + (let ((window (cdr (assq 'window alist))) + within windows other-window-shows-buffer other-window) + ;; Sanitize WINDOW. + (cond + ((or (eq window 'main) + (memq direction '(top bottom leftmost rightmost))) + (setq window (window-main-window))) + ((eq window 'root) + (setq window (frame-root-window))) + ((window-valid-p window)) + (t + (setq window (selected-window)))) + (setq within (not (window-live-p window))) + ;; Sanitize DIRECTION + (cond + ((memq direction '(left above right below))) + ((eq direction 'leftmost) + (setq direction 'left)) + ((memq direction '(top up)) + (setq direction 'above)) + ((eq direction 'rightmost) + (setq direction 'right)) + ((memq direction '(bottom down)) + (setq direction 'below)) + (t + (setq direction 'below))) + + (setq alist + (append alist + `(,(if temp-buffer-resize-mode + '(window-height . resize-temp-buffer-window) + '(window-height . fit-window-to-buffer)) + ,(when temp-buffer-resize-mode + '(preserve-size . (nil . t)))))) + + (setq windows (windows-sharing-edge window direction within)) + (dolist (other windows) + (cond + ((and (not other-window-shows-buffer) + (eq buffer (window-buffer other))) + (setq other-window-shows-buffer t) + (setq other-window other)) + ((not other-window) + (setq other-window other)))) + (or (and other-window-shows-buffer + (window--display-buffer buffer other-window 'reuse alist)) + (and (setq other-window + (window--try-to-split-window-in-direction + window direction alist)) + (window--display-buffer buffer other-window 'window alist)) + (and (setq window other-window) + (not (window-dedicated-p other-window)) + (not (window-minibuffer-p other-window)) + (window--display-buffer buffer other-window 'reuse alist))))))) + +;; This should be rewritten as +;; (display-buffer-in-direction buffer (cons '(direction . below) alist)) (defun display-buffer-below-selected (buffer alist) "Try displaying BUFFER in a window below the selected window. If there is a window below the selected one and that window already displays BUFFER, use that window. Otherwise, try to create a new window below the selected one and show BUFFER there. If that attempt fails as well and there is a non-dedicated window -below the selected one, use that window." - (let (window) +below the selected one, use that window. + +If ALIST contains a 'window-min-height' entry, this function +ensures that the window used is or can become at least as high as +specified by that entry's value. Note that such an entry alone +will not resize the window per se. In order to do that, ALIST +must also contain a 'window-height' entry with the same value." + (let ((min-height (cdr (assq 'window-min-height alist))) + window) (or (and (setq window (window-in-direction 'below)) - (eq buffer (window-buffer window)) + (eq buffer (window-buffer window)) + (or (not (numberp min-height)) + (>= (window-height window) min-height) + ;; 'window--display-buffer' can resize this window if + ;; and only if it has a 'quit-restore' parameter + ;; certifying that it always showed BUFFER before. + (let ((height (window-height window)) + (quit-restore (window-parameter window 'quit-restore))) + (and quit-restore + (eq (nth 1 quit-restore) 'window) + (window-resizable-p window (- min-height height))))) (window--display-buffer buffer window 'reuse alist)) (and (not (frame-parameter nil 'unsplittable)) - (let ((split-height-threshold 0) + (or (not (numberp min-height)) + (window-sizable-p nil (- min-height))) + (let ((split-height-threshold 0) split-width-threshold) - (setq window (window--try-to-split-window + (setq window (window--try-to-split-window (selected-window) alist))) - (window--display-buffer - buffer window 'window alist display-buffer-mark-dedicated)) + (window--display-buffer buffer window 'window alist)) (and (setq window (window-in-direction 'below)) - (not (window-dedicated-p window)) - (window--display-buffer - buffer window 'reuse alist display-buffer-mark-dedicated))))) - + (not (window-dedicated-p window)) + (or (not (numberp min-height)) + ;; A window that showed another buffer before cannot + ;; be resized. + (>= (window-height window) min-height)) + (window--display-buffer buffer window 'reuse alist))))) + +(defun display-buffer--maybe-at-bottom (buffer alist) + (let ((alist (append alist `(,(if temp-buffer-resize-mode + '(window-height . resize-temp-buffer-window) + '(window-height . fit-window-to-buffer)) + ,(when temp-buffer-resize-mode + '(preserve-size . (nil . t))))))) + (or (display-buffer--maybe-same-window buffer alist) + (display-buffer-reuse-window buffer alist) + (display-buffer--maybe-pop-up-frame buffer alist) + (display-buffer-at-bottom buffer alist)))) + +;; This should be rewritten as +;; (display-buffer-in-direction buffer (cons '(direction . bottom) alist)) (defun display-buffer-at-bottom (buffer alist) "Try displaying BUFFER in a window at the bottom of the selected frame. This either reuses such a window provided it shows BUFFER @@ -7435,24 +7806,16 @@ selected frame." (setq bottom-window-shows-buffer t) (setq bottom-window window)) ((not bottom-window) - (setq bottom-window window))) - nil nil 'nomini)) + (setq bottom-window window)))) + nil nil 'nomini) (or (and bottom-window-shows-buffer - (window--display-buffer - buffer bottom-window 'reuse alist display-buffer-mark-dedicated)) - (and (not (frame-parameter nil 'unsplittable)) - (let (split-width-threshold) - (setq window (window--try-to-split-window bottom-window alist))) - (window--display-buffer - buffer window 'window alist display-buffer-mark-dedicated)) + (window--display-buffer buffer bottom-window 'reuse alist)) (and (not (frame-parameter nil 'unsplittable)) (setq window (split-window-no-error (window-main-window))) - (window--display-buffer - buffer window 'window alist display-buffer-mark-dedicated)) + (window--display-buffer buffer window 'window alist)) (and (setq window bottom-window) (not (window-dedicated-p window)) - (window--display-buffer - buffer window 'reuse alist display-buffer-mark-dedicated))))) + (window--display-buffer buffer window 'reuse alist))))) (defun display-buffer-in-previous-window (buffer alist) "Display BUFFER in a window previously showing it. @@ -7512,7 +7875,8 @@ apply the following order of preference: ;; anything we found so far. (when (and (setq window (cdr (assq 'previous-window alist))) (window-live-p window) - (not (window-dedicated-p window))) + (or (eq buffer (window-buffer window)) + (not (window-dedicated-p window)))) (if (eq window (selected-window)) (unless inhibit-same-window (setq second-best-window window)) @@ -7682,7 +8046,9 @@ position in the selected window. This variable is ignored if the buffer is already displayed in the selected window or never appeared in it before, or if -`switch-to-buffer' calls `pop-to-buffer' to display the buffer." +`switch-to-buffer' calls `pop-to-buffer' to display the buffer, +or non-nil `switch-to-buffer-obey-display-actions' displays it +in another window." :type '(choice (const :tag "Never" nil) (const :tag "If already displayed elsewhere" already-displayed) @@ -7717,6 +8083,16 @@ FORCE-SAME-WINDOW is non-nil." :group 'windows :version "25.1") +(defcustom switch-to-buffer-obey-display-actions nil + "If non-nil, `switch-to-buffer' runs `pop-to-buffer-same-window' instead. +This means that when switching the buffer it respects display actions +specified by `display-buffer-overriding-action', `display-buffer-alist' +and other display related variables. So `switch-to-buffer' will display +the buffer in the window specified by the rules from these variables." + :type 'boolean + :group 'windows + :version "27.1") + (defun switch-to-buffer (buffer-or-name &optional norecord force-same-window) "Display buffer BUFFER-OR-NAME in the selected window. @@ -7749,59 +8125,83 @@ displaying it the most recently selected one. If optional argument FORCE-SAME-WINDOW is non-nil, the buffer must be displayed in the selected window when called non-interactively; if that is impossible, signal an error rather -than calling `pop-to-buffer'. +than calling `pop-to-buffer'. It has no effect when the option +`switch-to-buffer-obey-display-actions' is non-nil. The option `switch-to-buffer-preserve-window-point' can be used to make the buffer appear at its last position in the selected window. +If the option `switch-to-buffer-obey-display-actions' is non-nil, +run the function `pop-to-buffer-same-window' instead. +This may display the buffer in another window as specified by +`display-buffer-overriding-action', `display-buffer-alist' and +other display related variables. If this results in displaying +the buffer in the selected window, window start and point are adjusted +as prescribed by the option `switch-to-buffer-preserve-window-point'. +Otherwise, these are left alone. + Return the buffer switched to." (interactive (let ((force-same-window - (cond - ((window-minibuffer-p) nil) - ((not (eq (window-dedicated-p) t)) 'force-same-window) - ((pcase switch-to-buffer-in-dedicated-window - (`nil (user-error - "Cannot switch buffers in a dedicated window")) - (`prompt - (if (y-or-n-p - (format "Window is dedicated to %s; undedicate it" - (window-buffer))) - (progn - (set-window-dedicated-p nil nil) - 'force-same-window) - (user-error - "Cannot switch buffers in a dedicated window"))) - (`pop nil) - (_ (set-window-dedicated-p nil nil) 'force-same-window)))))) + (unless switch-to-buffer-obey-display-actions + (cond + ((window-minibuffer-p) nil) + ((not (eq (window-dedicated-p) t)) 'force-same-window) + ((pcase switch-to-buffer-in-dedicated-window + ('nil (user-error + "Cannot switch buffers in a dedicated window")) + ('prompt + (if (y-or-n-p + (format "Window is dedicated to %s; undedicate it" + (window-buffer))) + (progn + (set-window-dedicated-p nil nil) + 'force-same-window) + (user-error + "Cannot switch buffers in a dedicated window"))) + ('pop nil) + (_ (set-window-dedicated-p nil nil) 'force-same-window))))))) (list (read-buffer-to-switch "Switch to buffer: ") nil force-same-window))) - (let ((buffer (window-normalize-buffer-to-switch-to buffer-or-name))) + (let ((buffer (window-normalize-buffer-to-switch-to buffer-or-name)) + (set-window-start-and-point (not switch-to-buffer-obey-display-actions))) (cond ;; Don't call set-window-buffer if it's not needed since it ;; might signal an error (e.g. if the window is dedicated). - ((eq buffer (window-buffer))) - ((window-minibuffer-p) + ((and (eq buffer (window-buffer)) + ;; pop-to-buffer-same-window might decide to display + ;; the same buffer in another window + (not switch-to-buffer-obey-display-actions))) + ((and (window-minibuffer-p) + (not switch-to-buffer-obey-display-actions)) (if force-same-window (user-error "Cannot switch buffers in minibuffer window") (pop-to-buffer buffer norecord))) - ((eq (window-dedicated-p) t) + ((and (eq (window-dedicated-p) t) + (not switch-to-buffer-obey-display-actions)) (if force-same-window (user-error "Cannot switch buffers in a dedicated window") (pop-to-buffer buffer norecord))) (t - (let* ((entry (assq buffer (window-prev-buffers))) - (displayed (and (eq switch-to-buffer-preserve-window-point - 'already-displayed) - (get-buffer-window buffer 0)))) - (set-window-buffer nil buffer) - (when (and entry - (or (eq switch-to-buffer-preserve-window-point t) - displayed)) - ;; Try to restore start and point of buffer in the selected - ;; window (Bug#4041). - (set-window-start (selected-window) (nth 1 entry) t) - (set-window-point nil (nth 2 entry)))))) + (when switch-to-buffer-obey-display-actions + (let ((selected-window (selected-window))) + (pop-to-buffer-same-window buffer norecord) + (when (eq (selected-window) selected-window) + (setq set-window-start-and-point t)))) + + (when set-window-start-and-point + (let* ((entry (assq buffer (window-prev-buffers))) + (displayed (and (eq switch-to-buffer-preserve-window-point + 'already-displayed) + (get-buffer-window buffer 0)))) + (set-window-buffer nil buffer) + (when (and entry + (or (eq switch-to-buffer-preserve-window-point t) + displayed)) + ;; Try to restore start and point of buffer in the selected + ;; window (Bug#4041). + (set-window-start (selected-window) (nth 1 entry) t) + (set-window-point nil (nth 2 entry))))))) (unless norecord (select-window (selected-window))) @@ -8098,7 +8498,7 @@ parameters of FRAME." (if parent (frame-native-height parent) (- (nth 3 geometry) (nth 1 geometry)))) - ;; FRAME'S parent or workarea sizes. Used when no margins + ;; FRAME's parent or workarea sizes. Used when no margins ;; are specified. (parent-or-workarea (if parent @@ -8822,7 +9222,7 @@ A prefix argument is handled like `recenter': With plain `C-u', move current line to window center." (interactive "P") (cond - (arg (recenter arg)) ; Always respect ARG. + (arg (recenter arg t)) ; Always respect ARG. (t (setq recenter-last-op (if (eq this-command last-command) @@ -8833,15 +9233,15 @@ A prefix argument is handled like `recenter': (min (max 0 scroll-margin) (truncate (/ (window-body-height) 4.0))))) (cond ((eq recenter-last-op 'middle) - (recenter)) + (recenter nil t)) ((eq recenter-last-op 'top) - (recenter this-scroll-margin)) + (recenter this-scroll-margin t)) ((eq recenter-last-op 'bottom) - (recenter (- -1 this-scroll-margin))) + (recenter (- -1 this-scroll-margin) t)) ((integerp recenter-last-op) - (recenter recenter-last-op)) + (recenter recenter-last-op t)) ((floatp recenter-last-op) - (recenter (round (* recenter-last-op (window-height)))))))))) + (recenter (round (* recenter-last-op (window-height))) t))))))) (define-key global-map [?\C-l] 'recenter-top-bottom) @@ -8979,35 +9379,17 @@ This is different from `scroll-down-command' that scrolls a full screen." (put 'scroll-down-line 'scroll-command t) -(defun scroll-other-window-down (&optional lines) - "Scroll the \"other window\" down. -For more details, see the documentation for `scroll-other-window'." - (interactive "P") - (scroll-other-window - ;; Just invert the argument's meaning. - ;; We can do that without knowing which window it will be. - (if (eq lines '-) nil - (if (null lines) '- - (- (prefix-numeric-value lines)))))) - (defun beginning-of-buffer-other-window (arg) "Move point to the beginning of the buffer in the other window. Leave mark at previous position. With arg N, put point N/10 of the way from the true beginning." (interactive "P") - (let ((orig-window (selected-window)) - (window (other-window-for-scrolling))) - ;; We use unwind-protect rather than save-window-excursion - ;; because the latter would preserve the things we want to change. - (unwind-protect - (progn - (select-window window) - ;; Set point and mark in that window's buffer. - (with-no-warnings - (beginning-of-buffer arg)) - ;; Set point accordingly. - (recenter '(t))) - (select-window orig-window)))) + (with-selected-window (other-window-for-scrolling) + ;; Set point and mark in that window's buffer. + (with-no-warnings + (beginning-of-buffer arg)) + ;; Set point accordingly. + (recenter '(t)))) (defun end-of-buffer-other-window (arg) "Move point to the end of the buffer in the other window. @@ -9015,15 +9397,10 @@ Leave mark at previous position. With arg N, put point N/10 of the way from the true end." (interactive "P") ;; See beginning-of-buffer-other-window for comments. - (let ((orig-window (selected-window)) - (window (other-window-for-scrolling))) - (unwind-protect - (progn - (select-window window) - (with-no-warnings - (end-of-buffer arg)) - (recenter '(t))) - (select-window orig-window)))) + (with-selected-window (other-window-for-scrolling) + (with-no-warnings + (end-of-buffer arg)) + (recenter '(t)))) (defvar mouse-autoselect-window-timer nil "Timer used by delayed window autoselection.") @@ -9149,6 +9526,8 @@ is active. This function is run by `mouse-autoselect-window-timer'." ;; autoselection. (mouse-autoselect-window-start mouse-position window))))) +(declare-function display-multi-frame-p "frame" (&optional display)) + (defun handle-select-window (event) "Handle select-window events." (interactive "^e") @@ -9186,7 +9565,7 @@ is active. This function is run by `mouse-autoselect-window-timer'." ;; we might get two windows with an active cursor. (select-window window) (cond - ((or (not (memq (window-system frame) '(x w32 ns))) + ((or (not (display-multi-frame-p)) (not focus-follows-mouse) ;; Focus FRAME if it's either a child frame or an ancestor ;; of the frame switched from. @@ -9258,10 +9637,12 @@ a two-argument function used to combine the widths and heights of the given windows." (when windows (let ((width (window-max-chars-per-line (car windows))) - (height (window-body-height (car windows)))) + (height (with-selected-window (car windows) + (floor (window-screen-lines))))) (dolist (window (cdr windows)) (setf width (funcall reducer width (window-max-chars-per-line window))) - (setf height (funcall reducer height (window-body-height window)))) + (setf height (funcall reducer height (with-selected-window window + (floor (window-screen-lines)))))) (cons width height)))) (defun window-adjust-process-window-size-smallest (_process windows) @@ -9317,15 +9698,7 @@ displaying that processes's buffer." (when size (set-process-window-size process (cdr size) (car size)))))))))) -;; Remove the following call in Emacs 27, running -;; 'window-size-change-functions' should suffice. (add-hook 'window-configuration-change-hook 'window--adjust-process-windows) - -;; Catch any size changes not handled by -;; 'window-configuration-change-hook' (Bug#32720, "another issue" in -;; Bug#33230). -(add-hook 'window-size-change-functions (lambda (_frame) - (window--adjust-process-windows))) ;; Some of these are in tutorial--default-keys, so update that if you ;; change these. |