diff options
| author | Martin Rudalics | 2019-05-19 11:03:05 +0200 |
|---|---|---|
| committer | Martin Rudalics | 2019-05-19 11:03:05 +0200 |
| commit | 8783becbba410581c6384ee021e7e83ad5236a29 (patch) | |
| tree | 96cb1f85d83a836f9823acac0e16a89b7a0d9769 | |
| parent | b87e5eea1dd7c7345d0a9f82759eedfd7c9a8099 (diff) | |
| download | emacs-8783becbba410581c6384ee021e7e83ad5236a29.tar.gz emacs-8783becbba410581c6384ee021e7e83ad5236a29.zip | |
New buffer display action function 'display-buffer-in-direction'
* lisp/window.el (windows-sharing-edge)
(window--try-to-split-window-in-direction)
(display-buffer-in-direction): New functions.
* doc/lispref/windows.texi (Buffer Display Action Functions):
Describe new action function 'display-buffer-in-direction'.
(Buffer Display Action Alists): Describe new entry 'direction'.
Amend description of 'window' entry.
* etc/NEWS: Mention 'display-buffer-in-direction' and 'direction'
and 'window' action alist entries.
| -rw-r--r-- | doc/lispref/windows.texi | 67 | ||||
| -rw-r--r-- | etc/NEWS | 15 | ||||
| -rw-r--r-- | lisp/window.el | 148 |
3 files changed, 229 insertions, 1 deletions
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 32e8c2afa31..96e42a148c5 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi | |||
| @@ -2601,6 +2601,63 @@ window and displaying the buffer in that window. It can fail if all | |||
| 2601 | windows are dedicated to other buffers (@pxref{Dedicated Windows}). | 2601 | windows are dedicated to other buffers (@pxref{Dedicated Windows}). |
| 2602 | @end defun | 2602 | @end defun |
| 2603 | 2603 | ||
| 2604 | @defun display-buffer-in-direction buffer alist | ||
| 2605 | This function tries to display @var{buffer} at a location specified by | ||
| 2606 | @var{alist}. For this purpose, @var{alist} should contain a | ||
| 2607 | @code{direction} entry whose value is one of @code{left}, @code{above} | ||
| 2608 | (or @code{up}), @code{right} and @code{below} (or @code{down}). Other | ||
| 2609 | values are usually interpreted as @code{below}. | ||
| 2610 | |||
| 2611 | If @var{alist} also contains a @code{window} entry, its value | ||
| 2612 | specifies a reference window. That value can be a special symbol like | ||
| 2613 | @code{main} which stands for the selected frame's main window | ||
| 2614 | (@pxref{Side Window Options and Functions}) or @code{root} standing | ||
| 2615 | for the selected frame's root window (@pxref{Windows and Frames}). It | ||
| 2616 | can also specify an arbitrary valid window. Any other value (or | ||
| 2617 | omitting the @code{window} entry entirely) means to use the selected | ||
| 2618 | window as reference window. | ||
| 2619 | |||
| 2620 | This function first tries to reuse a window in the specified direction | ||
| 2621 | that already shows @var{buffer}. If no such window exists, it tries | ||
| 2622 | to split the reference window in order to produce a new window in the | ||
| 2623 | specified direction. If this fails as well, it will try to display | ||
| 2624 | @var{buffer} in an existing window in the specified direction. In | ||
| 2625 | either case, the window chosen will appear on the side of the | ||
| 2626 | reference window specified by the @code{direction} entry, sharing at | ||
| 2627 | least one edge with the reference window. | ||
| 2628 | |||
| 2629 | If the reference window is live, the edge the chosen window will share | ||
| 2630 | with it is always the opposite of the one specified by the | ||
| 2631 | @code{direction} entry. For example, if the value of the | ||
| 2632 | @code{direction} entry is @code{left}, the chosen window's right edge | ||
| 2633 | coordinate (@pxref{Coordinates and Windows}) will equal the reference | ||
| 2634 | window's left edge coordinate. | ||
| 2635 | |||
| 2636 | If the reference window is internal, a reused window must share with | ||
| 2637 | it the edge specified by the @code{direction} entry. Hence if, for | ||
| 2638 | example, the reference window is the frame's root window and the value | ||
| 2639 | of the @code{direction} entry is @code{left}, a reused window must be | ||
| 2640 | on the left of the frame. This means that the left edge coordinate of | ||
| 2641 | the chosen window and that of the reference window are the same. | ||
| 2642 | |||
| 2643 | A new window, however, will be created by splitting the reference | ||
| 2644 | window such that the chosen window will share the opposite edge with | ||
| 2645 | the reference window. In our example, a new root window would be | ||
| 2646 | created with a new live window and the reference window as its | ||
| 2647 | children. The chosen window's right edge coordinate would then equal | ||
| 2648 | the left edge coordinate of the reference window. Its left edge | ||
| 2649 | coordinate would equal the left edge coordinate of the frame's new | ||
| 2650 | root window. | ||
| 2651 | |||
| 2652 | Four special values for @code{direction} entries allow to implicitly | ||
| 2653 | specify the selected frame's main window as the reference window: | ||
| 2654 | @code{leftmost}, @code{top}, @code{rightmost} and @code{bottom}. This | ||
| 2655 | means that instead of, for example, @w{@code{(direction . left) | ||
| 2656 | (window . main)}} one can just specify @w{@code{(direction | ||
| 2657 | . leftmost)}}. An existing @code{window} @var{alist} entry is ignored | ||
| 2658 | in such cases. | ||
| 2659 | @end defun | ||
| 2660 | |||
| 2604 | @defun display-buffer-below-selected buffer alist | 2661 | @defun display-buffer-below-selected buffer alist |
| 2605 | This function tries to display @var{buffer} in a window below the | 2662 | This function tries to display @var{buffer} in a window below the |
| 2606 | selected window. If there is a window below the selected one and that | 2663 | selected window. If there is a window below the selected one and that |
| @@ -2934,12 +2991,20 @@ If non-@code{nil}, the value specifies the slot of the side window | |||
| 2934 | supposed to display the buffer. This entry is used only by | 2991 | supposed to display the buffer. This entry is used only by |
| 2935 | @code{display-buffer-in-side-window}. | 2992 | @code{display-buffer-in-side-window}. |
| 2936 | 2993 | ||
| 2994 | @vindex direction@r{, a buffer display action alist entry} | ||
| 2995 | @item direction | ||
| 2996 | The value specifies a direction which, together with a @code{window} | ||
| 2997 | entry, allows @code{display-buffer-in-direction} to determine the | ||
| 2998 | location of the window to display the buffer. | ||
| 2999 | |||
| 2937 | @vindex window@r{, a buffer display action alist entry} | 3000 | @vindex window@r{, a buffer display action alist entry} |
| 2938 | @item window | 3001 | @item window |
| 2939 | The value specifies a window that is in some way related to the window | 3002 | The value specifies a window that is in some way related to the window |
| 2940 | chosen by @code{display-buffer}. This entry is currently used by | 3003 | chosen by @code{display-buffer}. This entry is currently used by |
| 2941 | @code{display-buffer-in-atom-window} to indicate the window on whose | 3004 | @code{display-buffer-in-atom-window} to indicate the window on whose |
| 2942 | side the new window shall be created. | 3005 | side the new window shall be created. It is also used by |
| 3006 | @code{display-buffer-in-direction} to specify the reference window on | ||
| 3007 | whose side the resulting window shall appear. | ||
| 2943 | 3008 | ||
| 2944 | @vindex allow-no-window@r{, a buffer display action alist entry} | 3009 | @vindex allow-no-window@r{, a buffer display action alist entry} |
| 2945 | @item allow-no-window | 3010 | @item allow-no-window |
| @@ -1779,6 +1779,11 @@ This option allows to automatically resize minibuffer-only frames | |||
| 1779 | similarly to how minibuffer windows are resized on "normal" frames. | 1779 | similarly to how minibuffer windows are resized on "normal" frames. |
| 1780 | 1780 | ||
| 1781 | +++ | 1781 | +++ |
| 1782 | ** New buffer display action function 'display-buffer-in-direction'. | ||
| 1783 | This function allows to specify the location of the window chosen by | ||
| 1784 | 'display-buffer' in various ways. | ||
| 1785 | |||
| 1786 | +++ | ||
| 1782 | ** New buffer display action alist entry 'dedicated'. | 1787 | ** New buffer display action alist entry 'dedicated'. |
| 1783 | Such an entry allows to specify the dedicated status of a window | 1788 | Such an entry allows to specify the dedicated status of a window |
| 1784 | created by 'display-buffer'. | 1789 | created by 'display-buffer'. |
| @@ -1790,6 +1795,16 @@ for displaying a buffer. 'display-buffer-below-selected' is the only | |||
| 1790 | action function to respect it at the moment. | 1795 | action function to respect it at the moment. |
| 1791 | 1796 | ||
| 1792 | +++ | 1797 | +++ |
| 1798 | ** New buffer display action alist entry 'direction'. | ||
| 1799 | This entry is used to specify the location of the window chosen by | ||
| 1800 | 'display-buffer-in-direction'. | ||
| 1801 | |||
| 1802 | +++ | ||
| 1803 | ** Additional meaning of display action alist entry 'window'. | ||
| 1804 | A 'window' entry can now also specify a reference window for | ||
| 1805 | 'display-buffer-in-direction'. | ||
| 1806 | |||
| 1807 | +++ | ||
| 1793 | ** The function 'assoc-delete-all' now takes an optional predicate argument. | 1808 | ** The function 'assoc-delete-all' now takes an optional predicate argument. |
| 1794 | 1809 | ||
| 1795 | +++ | 1810 | +++ |
diff --git a/lisp/window.el b/lisp/window.el index b4f5ac5cc44..2c9d177d0a2 100644 --- a/lisp/window.el +++ b/lisp/window.el | |||
| @@ -7534,6 +7534,152 @@ be added to ALIST." | |||
| 7534 | (unless (cdr (assq 'inhibit-switch-frame alist)) | 7534 | (unless (cdr (assq 'inhibit-switch-frame alist)) |
| 7535 | (window--maybe-raise-frame frame))))) | 7535 | (window--maybe-raise-frame frame))))) |
| 7536 | 7536 | ||
| 7537 | (defun windows-sharing-edge (&optional window edge within) | ||
| 7538 | "Return list of live windows sharing the same edge with WINDOW. | ||
| 7539 | WINDOW must be a valid window and defaults to the selected one. | ||
| 7540 | EDGE stands for the edge to share and must be either 'left', | ||
| 7541 | 'above', 'right' or 'below'. Omitted or nil, EDGE defaults to | ||
| 7542 | 'left'. | ||
| 7543 | |||
| 7544 | WITHIN nil means to find a live window that shares the opposite | ||
| 7545 | EDGE with WINDOW. For example, if EDGE equals 'left', WINDOW has | ||
| 7546 | to share (part of) the right edge of any window returned. WITHIN | ||
| 7547 | non-nil means to find all live windows that share the same EDGE | ||
| 7548 | with WINDOW (Window must be internal in this case). So if EDGE | ||
| 7549 | equals 'left', WINDOW's left edge has to fully encompass the left | ||
| 7550 | edge of any window returned." | ||
| 7551 | (setq window (window-normalize-window window)) | ||
| 7552 | (setq edge (or edge 'left)) | ||
| 7553 | (when (and within (window-live-p window)) | ||
| 7554 | (error "Cannot share edge from within live window %s" window)) | ||
| 7555 | (let ((window-edges (window-edges window nil nil t)) | ||
| 7556 | (horizontal (memq edge '(left right))) | ||
| 7557 | (n (pcase edge | ||
| 7558 | ('left 0) ('above 1) ('right 2) ('below 3)))) | ||
| 7559 | (unless (numberp n) | ||
| 7560 | (error "Invalid EDGE %s" edge)) | ||
| 7561 | (let ((o (mod (+ 2 n) 4)) | ||
| 7562 | (p (if horizontal 1 0)) | ||
| 7563 | (q (if horizontal 3 2)) | ||
| 7564 | windows) | ||
| 7565 | (walk-window-tree | ||
| 7566 | (lambda (other) | ||
| 7567 | (let ((other-edges (window-edges other nil nil t))) | ||
| 7568 | (when (and (not (eq window other)) | ||
| 7569 | (= (nth n window-edges) | ||
| 7570 | (nth (if within n o) other-edges)) | ||
| 7571 | (cond | ||
| 7572 | ((= (nth p window-edges) (nth p other-edges))) | ||
| 7573 | ((< (nth p window-edges) (nth p other-edges)) | ||
| 7574 | (< (nth p other-edges) (nth q window-edges))) | ||
| 7575 | (t | ||
| 7576 | (< (nth p window-edges) (nth q other-edges))))) | ||
| 7577 | (setq windows (cons other windows))))) | ||
| 7578 | (window-frame window) nil 'nomini) | ||
| 7579 | (reverse windows)))) | ||
| 7580 | |||
| 7581 | (defun window--try-to-split-window-in-direction (window direction alist) | ||
| 7582 | "Try to split WINDOW in DIRECTION. | ||
| 7583 | DIRECTION is passed as SIDE argument to `split-window-no-error'. | ||
| 7584 | ALIST is a buffer display alist." | ||
| 7585 | (and (not (frame-parameter (window-frame window) 'unsplittable)) | ||
| 7586 | (let* ((window-combination-limit | ||
| 7587 | ;; When `window-combination-limit' equals | ||
| 7588 | ;; `display-buffer' or equals `resize-window' and a | ||
| 7589 | ;; `window-height' or `window-width' alist entry are | ||
| 7590 | ;; present, bind it to t so resizing steals space | ||
| 7591 | ;; preferably from the window that was split. | ||
| 7592 | (if (or (eq window-combination-limit 'display-buffer) | ||
| 7593 | (and (eq window-combination-limit 'window-size) | ||
| 7594 | (or (cdr (assq 'window-height alist)) | ||
| 7595 | (cdr (assq 'window-width alist))))) | ||
| 7596 | t | ||
| 7597 | window-combination-limit)) | ||
| 7598 | (new-window (split-window-no-error window nil direction))) | ||
| 7599 | (and (window-live-p new-window) new-window)))) | ||
| 7600 | |||
| 7601 | (defun display-buffer-in-direction (buffer alist) | ||
| 7602 | "Try to display BUFFER in a direction specified by ALIST. | ||
| 7603 | ALIST has to contain a 'direction' entry whose value should be | ||
| 7604 | one of 'left', 'above' (or 'up'), 'right', and 'below' (or | ||
| 7605 | 'down'). Other values are usually interpreted as 'below'. | ||
| 7606 | |||
| 7607 | If ALIST also contains a 'window' entry, its value specifies a | ||
| 7608 | reference window. That value can be a special symbol like | ||
| 7609 | 'main' (which stands for the selected frame's main window) or | ||
| 7610 | 'root' (standings for the selected frame's root window) or an | ||
| 7611 | arbitrary valid window. Any other value (or omitting the | ||
| 7612 | 'window' entry) means to use the selected window as reference | ||
| 7613 | window. | ||
| 7614 | |||
| 7615 | This function tries to reuse or split a window such that the | ||
| 7616 | window produced this way is on the side of the reference window | ||
| 7617 | specified by the 'direction' entry. | ||
| 7618 | |||
| 7619 | Four special values for 'direction' entries allow to implicitly | ||
| 7620 | specify the selected frame's main window as reference window: | ||
| 7621 | 'leftmost', 'top', 'rightmost' and 'bottom'. Hence, instead of | ||
| 7622 | '(direction . left) (window . main)' one can simply write | ||
| 7623 | '(direction . leftmost)'." | ||
| 7624 | (let ((direction (cdr (assq 'direction alist)))) | ||
| 7625 | (when direction | ||
| 7626 | (let ((window (cdr (assq 'window alist))) | ||
| 7627 | within windows other-window-shows-buffer other-window) | ||
| 7628 | ;; Sanitize WINDOW. | ||
| 7629 | (cond | ||
| 7630 | ((or (eq window 'main) | ||
| 7631 | (memq direction '(top bottom leftmost rightmost))) | ||
| 7632 | (setq window (window-main-window))) | ||
| 7633 | ((eq window 'root) | ||
| 7634 | (setq window (frame-root-window))) | ||
| 7635 | ((window-valid-p window)) | ||
| 7636 | (t | ||
| 7637 | (setq window (selected-window)))) | ||
| 7638 | (setq within (not (window-live-p window))) | ||
| 7639 | ;; Sanitize DIRECTION | ||
| 7640 | (cond | ||
| 7641 | ((memq direction '(left above right below))) | ||
| 7642 | ((eq direction 'leftmost) | ||
| 7643 | (setq direction 'left)) | ||
| 7644 | ((memq direction '(top up)) | ||
| 7645 | (setq direction 'above)) | ||
| 7646 | ((eq direction 'rightmost) | ||
| 7647 | (setq direction 'right)) | ||
| 7648 | ((memq direction '(bottom down)) | ||
| 7649 | (setq direction 'below)) | ||
| 7650 | (t | ||
| 7651 | (setq direction 'below))) | ||
| 7652 | |||
| 7653 | (setq alist | ||
| 7654 | (append alist | ||
| 7655 | `(,(if temp-buffer-resize-mode | ||
| 7656 | '(window-height . resize-temp-buffer-window) | ||
| 7657 | '(window-height . fit-window-to-buffer)) | ||
| 7658 | ,(when temp-buffer-resize-mode | ||
| 7659 | '(preserve-size . (nil . t)))))) | ||
| 7660 | |||
| 7661 | (setq windows (windows-sharing-edge window direction within)) | ||
| 7662 | (dolist (other windows) | ||
| 7663 | (cond | ||
| 7664 | ((and (not other-window-shows-buffer) | ||
| 7665 | (eq buffer (window-buffer other))) | ||
| 7666 | (setq other-window-shows-buffer t) | ||
| 7667 | (setq other-window other)) | ||
| 7668 | ((not other-window) | ||
| 7669 | (setq other-window other)))) | ||
| 7670 | (or (and other-window-shows-buffer | ||
| 7671 | (window--display-buffer buffer other-window 'reuse alist)) | ||
| 7672 | (and (setq other-window | ||
| 7673 | (window--try-to-split-window-in-direction | ||
| 7674 | window direction alist)) | ||
| 7675 | (window--display-buffer buffer other-window 'window alist)) | ||
| 7676 | (and (setq window other-window) | ||
| 7677 | (not (window-dedicated-p other-window)) | ||
| 7678 | (not (window-minibuffer-p other-window)) | ||
| 7679 | (window--display-buffer buffer other-window 'reuse alist))))))) | ||
| 7680 | |||
| 7681 | ;; This should be rewritten as | ||
| 7682 | ;; (display-buffer-in-direction buffer (cons '(direction . below) alist)) | ||
| 7537 | (defun display-buffer-below-selected (buffer alist) | 7683 | (defun display-buffer-below-selected (buffer alist) |
| 7538 | "Try displaying BUFFER in a window below the selected window. | 7684 | "Try displaying BUFFER in a window below the selected window. |
| 7539 | If there is a window below the selected one and that window | 7685 | If there is a window below the selected one and that window |
| @@ -7589,6 +7735,8 @@ must also contain a 'window-height' entry with the same value." | |||
| 7589 | (display-buffer--maybe-pop-up-frame buffer alist) | 7735 | (display-buffer--maybe-pop-up-frame buffer alist) |
| 7590 | (display-buffer-at-bottom buffer alist)))) | 7736 | (display-buffer-at-bottom buffer alist)))) |
| 7591 | 7737 | ||
| 7738 | ;; This should be rewritten as | ||
| 7739 | ;; (display-buffer-in-direction buffer (cons '(direction . bottom) alist)) | ||
| 7592 | (defun display-buffer-at-bottom (buffer alist) | 7740 | (defun display-buffer-at-bottom (buffer alist) |
| 7593 | "Try displaying BUFFER in a window at the bottom of the selected frame. | 7741 | "Try displaying BUFFER in a window at the bottom of the selected frame. |
| 7594 | This either reuses such a window provided it shows BUFFER | 7742 | This either reuses such a window provided it shows BUFFER |