X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/cf20330bba87d1f2bb622d2403fcce4c9830dfee..9478502212bb8eea71a5d96805fe40f3c9d29561:/lisp/window.el diff --git a/lisp/window.el b/lisp/window.el index c9e5d79365..41a5d17321 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -1,7 +1,8 @@ ;;; window.el --- GNU Emacs window commands aside from those written in C ;; Copyright (C) 1985, 1989, 1992, 1993, 1994, 2000, 2001, 2002, -;; 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +;; 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +;; Free Software Foundation, Inc. ;; Maintainer: FSF ;; Keywords: internal @@ -39,19 +40,20 @@ unless you explicitly change the size, or Emacs has no other choice.") (make-variable-buffer-local 'window-size-fixed) (defmacro save-selected-window (&rest body) - "Execute BODY, then select the window that was selected before BODY. + "Execute BODY, then select the previously selected window. The value returned is the value of the last form in BODY. +This macro saves and restores the selected window, as well as the +selected window in each frame. If the previously selected window +is no longer live, then whatever window is selected at the end of +BODY remains selected. If the previously selected window of some +frame is no longer live at the end of BODY, that frame's selected +window is left alone. + This macro saves and restores the current buffer, since otherwise -its normal operation could potentially make a different -buffer current. It does not alter the buffer list ordering. - -This macro saves and restores the selected window, as well as -the selected window in each frame. If the previously selected -window of some frame is no longer live at the end of BODY, that -frame's selected window is left alone. If the selected window is -no longer live, then whatever window is selected at the end of -BODY remains selected." +its normal operation could make a different buffer current. The +order of recently selected windows and the buffer list ordering +are not altered by this macro (unless they are altered in BODY)." `(let ((save-selected-window-window (selected-window)) ;; It is necessary to save all of these, because calling ;; select-window changes frame-selected-window for whatever @@ -65,13 +67,19 @@ BODY remains selected." (dolist (elt save-selected-window-alist) (and (frame-live-p (car elt)) (window-live-p (cdr elt)) - (set-frame-selected-window (car elt) (cdr elt)))) - (if (window-live-p save-selected-window-window) - (select-window save-selected-window-window)))))) + (set-frame-selected-window (car elt) (cdr elt) 'norecord))) + (when (window-live-p save-selected-window-window) + (select-window save-selected-window-window 'norecord)))))) (defun window-body-height (&optional window) - "Return number of lines in window WINDOW for actual buffer text. -This does not include the mode line (if any) or the header line (if any)." + "Return number of lines in WINDOW available for actual buffer text. +WINDOW defaults to the selected window. + +The return value does not include the mode line or the header +line, if any. If a line at the bottom of the window is only +partially visible, that line is included in the return value. +If you do not want to include a partially visible bottom line +in the return value, use `window-text-height' instead." (or window (setq window (selected-window))) (if (window-minibuffer-p window) (window-height window) @@ -80,6 +88,16 @@ This does not include the mode line (if any) or the header line (if any)." (if mode-line-format 1 0) (if header-line-format 1 0)))))) +;; See discussion in bug#4543. +(defun window-full-height-p (&optional window) + "Return non-nil if WINDOW is not the result of a vertical split. +WINDOW defaults to the selected window. (This function is not +appropriate for minibuffers.)" + (unless window + (setq window (selected-window))) + (= (window-height window) + (window-height (frame-root-window (window-frame window))))) + (defun one-window-p (&optional nomini all-frames) "Return non-nil if the selected window is the only window. Optional arg NOMINI non-nil means don't count the minibuffer @@ -99,11 +117,18 @@ If ALL-FRAMES is anything else, count only the selected frame." (next-window base-window (if nomini 'arg) all-frames)))) (defun window-current-scroll-bars (&optional window) - "Return the current scroll-bar settings in window WINDOW. -Value is a cons (VERTICAL . HORIZONTAL) where VERTICAL specifies the -current location of the vertical scroll-bars (left, right, or nil), -and HORIZONTAL specifies the current location of the horizontal scroll -bars (top, bottom, or nil)." + "Return the current scroll bar settings for WINDOW. +WINDOW defaults to the selected window. + +The return value is a cons cell (VERTICAL . HORIZONTAL) where +VERTICAL specifies the current location of the vertical scroll +bars (`left', `right', or nil), and HORIZONTAL specifies the +current location of the horizontal scroll bars (`top', `bottom', +or nil). + +Unlike `window-scroll-bars', this function reports the scroll bar +type actually used, once frame defaults and `scroll-bar-mode' are +taken into account." (let ((vert (nth 2 (window-scroll-bars window))) (hor nil)) (when (or (eq vert t) (eq hor t)) @@ -116,34 +141,53 @@ bars (top, bottom, or nil)." (cons vert hor))) (defun walk-windows (proc &optional minibuf all-frames) - "Cycle through all visible windows, calling PROC for each one. -PROC is called with a window as argument. + "Cycle through all windows, calling PROC for each one. +PROC must specify a function with a window as its sole argument. +The optional arguments MINIBUF and ALL-FRAMES specify the set of +windows to include in the walk, see also `next-window'. -Optional second arg MINIBUF t means count the minibuffer window even -if not active. MINIBUF nil or omitted means count the minibuffer only if -it is active. MINIBUF neither t nor nil means not to count the -minibuffer even if it is active. +MINIBUF t means include the minibuffer window even if the +minibuffer is not active. MINIBUF nil or omitted means include +the minibuffer window only if the minibuffer is active. Any +other value means do not include the minibuffer window even if +the minibuffer is active. Several frames may share a single minibuffer; if the minibuffer -counts, all windows on all frames that share that minibuffer count -too. Therefore, if you are using a separate minibuffer frame -and the minibuffer is active and MINIBUF says it counts, -`walk-windows' includes the windows in the frame from which you -entered the minibuffer, as well as the minibuffer window. - -ALL-FRAMES is the optional third argument. -ALL-FRAMES nil or omitted means cycle within the frames as specified above. -ALL-FRAMES = `visible' means include windows on all visible frames. -ALL-FRAMES = 0 means include windows on all visible and iconified frames. -ALL-FRAMES = t means include windows on all frames including invisible frames. -If ALL-FRAMES is a frame, it means include windows on that frame. -Anything else means restrict to the selected frame." - ;; If we start from the minibuffer window, don't fail to come back to it. - (if (window-minibuffer-p (selected-window)) - (setq minibuf t)) +is active, all windows on all frames that share that minibuffer +are included too. Therefore, if you are using a separate +minibuffer frame and the minibuffer is active and MINIBUF says it +counts, `walk-windows' includes the windows in the frame from +which you entered the minibuffer, as well as the minibuffer +window. + +ALL-FRAMES nil or omitted means cycle through all windows on + WINDOW's frame, plus the minibuffer window if specified by the + MINIBUF argument, see above. If the minibuffer counts, cycle + through all windows on all frames that share that minibuffer + too. +ALL-FRAMES t means cycle through all windows on all existing + frames. +ALL-FRAMES `visible' means cycle through all windows on all + visible frames. +ALL-FRAMES 0 means cycle through all windows on all visible and + iconified frames. +ALL-FRAMES a frame means cycle through all windows on that frame + only. +Anything else means cycle through all windows on WINDOW's frame + and no others. + +This function changes neither the order of recently selected +windows nor the buffer list." + ;; If we start from the minibuffer window, don't fail to come + ;; back to it. + (when (window-minibuffer-p (selected-window)) + (setq minibuf t)) + ;; Make sure to not mess up the order of recently selected + ;; windows. Use `save-selected-window' and `select-window' + ;; with second argument non-nil for this purpose. (save-selected-window - (if (framep all-frames) - (select-window (frame-first-window all-frames))) + (when (framep all-frames) + (select-window (frame-first-window all-frames) 'norecord)) (let* (walk-windows-already-seen (walk-windows-current (selected-window))) (while (progn @@ -157,32 +201,15 @@ Anything else means restrict to the selected frame." (defun get-window-with-predicate (predicate &optional minibuf all-frames default) "Return a window satisfying PREDICATE. - -This function cycles through all visible windows using `walk-windows', -calling PREDICATE on each one. PREDICATE is called with a window as -argument. The first window for which PREDICATE returns a non-nil -value is returned. If no window satisfies PREDICATE, DEFAULT is -returned. - -Optional second arg MINIBUF t means count the minibuffer window even -if not active. MINIBUF nil or omitted means count the minibuffer only if -it is active. MINIBUF neither t nor nil means not to count the -minibuffer even if it is active. - -Several frames may share a single minibuffer; if the minibuffer -counts, all windows on all frames that share that minibuffer count -too. Therefore, if you are using a separate minibuffer frame -and the minibuffer is active and MINIBUF says it counts, -`walk-windows' includes the windows in the frame from which you -entered the minibuffer, as well as the minibuffer window. - -ALL-FRAMES is the optional third argument. -ALL-FRAMES nil or omitted means cycle within the frames as specified above. -ALL-FRAMES = `visible' means include windows on all visible frames. -ALL-FRAMES = 0 means include windows on all visible and iconified frames. -ALL-FRAMES = t means include windows on all frames including invisible frames. -If ALL-FRAMES is a frame, it means include windows on that frame. -Anything else means restrict to the selected frame." +More precisely, cycle through all windows using `walk-windows', +calling the function PREDICATE on each one of them with the +window as its sole argument. Return the first window for which +PREDICATE returns non-nil. If no window satisfies PREDICATE, +return DEFAULT. + +The optional arguments MINIBUF and ALL-FRAMES specify the set of +windows to include. See `walk-windows' for the meaning of these +arguments." (catch 'found (walk-windows #'(lambda (window) (when (funcall predicate window) @@ -196,8 +223,11 @@ Anything else means restrict to the selected frame." (defun get-buffer-window-list (&optional buffer-or-name minibuf all-frames) "Return list of all windows displaying BUFFER-OR-NAME, or nil if none. BUFFER-OR-NAME may be a buffer or the name of an existing buffer -and defaults to nil. -See `walk-windows' for the meaning of MINIBUF and ALL-FRAMES." +and defaults to the current buffer. + +The optional arguments MINIBUF and ALL-FRAMES specify the set of +windows to consider. See `walk-windows' for the precise meaning +of these arguments." (let ((buffer (cond ((not buffer-or-name) (current-buffer)) ((bufferp buffer-or-name) buffer-or-name) @@ -210,28 +240,18 @@ See `walk-windows' for the meaning of MINIBUF and ALL-FRAMES." windows)) (defun minibuffer-window-active-p (window) - "Return t if WINDOW (a minibuffer window) is now active." + "Return t if WINDOW is the currently active minibuffer window." (eq window (active-minibuffer-window))) (defun count-windows (&optional minibuf) "Return the number of visible windows. -This counts the windows in the selected frame and (if the minibuffer is -to be counted) its minibuffer frame (if that's not the same frame). -The optional arg MINIBUF non-nil means count the minibuffer -even if it is inactive." +The optional argument MINIBUF specifies whether the minibuffer +window shall be counted. See `walk-windows' for the precise +meaning of this argument." (let ((count 0)) (walk-windows (lambda (w) (setq count (+ count 1))) minibuf) count)) - -(defun window-safely-shrinkable-p (&optional window) - "Non-nil if the WINDOW can be shrunk without shrinking other windows. -If WINDOW is nil or omitted, it defaults to the currently selected window." - (with-selected-window (or window (selected-window)) - (let ((edges (window-edges))) - (or (= (nth 2 edges) (nth 2 (window-edges (previous-window)))) - (= (nth 0 edges) (nth 0 (window-edges (next-window)))))))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; `balance-windows' subroutines using `window-tree' @@ -429,17 +449,17 @@ Arguments WINDOW, DELTA and HORIZONTAL are passed on to that function." (dolist (c childs) (bw-balance-sub c cw ch))))) -;;; A different solution to balance-windows - (defun window-fixed-size-p (&optional window direction) - "Non-nil if WINDOW cannot be resized in DIRECTION. -DIRECTION can be nil (i.e. any), `height' or `width'." + "Return t if WINDOW cannot be resized in DIRECTION. +WINDOW defaults to the selected window. DIRECTION can be +nil (i.e. any), `height' or `width'." (with-current-buffer (window-buffer window) - (let ((fixed (and (boundp 'window-size-fixed) window-size-fixed))) - (when fixed - (not (and direction - (member (cons direction window-size-fixed) - '((height . width) (width . height))))))))) + (when (and (boundp 'window-size-fixed) window-size-fixed) + (not (and direction + (member (cons direction window-size-fixed) + '((height . width) (width . height)))))))) + +;;; A different solution to balance-windows. (defvar window-area-factor 1 "Factor by which the window area should be over-estimated. @@ -449,7 +469,8 @@ Changing this globally has no effect.") (defun balance-windows-area () "Make all visible windows the same area (approximately). -See also `window-area-factor' to change the relative size of specific buffers." +See also `window-area-factor' to change the relative size of +specific buffers." (interactive) (let* ((unchanged 0) (carry 0) (round 0) ;; Remove fixed-size windows. @@ -538,159 +559,186 @@ Commands such as `switch-to-buffer-other-window' and (function :tag "function")) :group 'windows) -(defun special-display-p (buffer-name) - "Return non-nil if a buffer named BUFFER-NAME gets a special frame. -If the value is t, `display-buffer' or `pop-to-buffer' would -create a special frame for that buffer using the default frame -parameters. - -If the value is a list, it is a list of frame parameters that -would be used to make a frame for that buffer. The variables -`special-display-buffer-names' and `special-display-regexps' -control this." - (let (tmp) - (cond - ((not (stringp buffer-name))) - ;; Make sure to return t in the following two cases. - ((member buffer-name special-display-buffer-names) t) - ((setq tmp (assoc buffer-name special-display-buffer-names)) (cdr tmp)) - ((catch 'found - (dolist (regexp special-display-regexps) - (cond - ((stringp regexp) - (when (string-match-p regexp buffer-name) - (throw 'found t))) - ((and (consp regexp) (stringp (car regexp)) - (string-match-p (car regexp) buffer-name)) - (throw 'found (cdr regexp)))))))))) - (defcustom special-display-buffer-names nil - "List of buffer names that should have their own special frames. + "List of names of buffers that should be displayed specially. Displaying a buffer with `display-buffer' or `pop-to-buffer', if -its name is in this list, makes a special frame for it using -`special-display-function'. See also `special-display-regexps'. - -An element of the list can be a list instead of just a string. -There are two ways to use a list as an element: - (BUFFER FRAME-PARAMETERS...) (BUFFER FUNCTION OTHER-ARGS...) -In the first case, the FRAME-PARAMETERS are pairs of the form -\(PARAMETER . VALUE); these parameter values are used to create -the frame. In the second case, FUNCTION is called with BUFFER as -the first argument, followed by the OTHER-ARGS--it can display -BUFFER in any way it likes. All this is done by the function -found in `special-display-function'. - -If the specified frame parameters include (same-buffer . t), the -buffer is displayed in the currently selected window. Otherwise, if -they include (same-frame . t), the buffer is displayed in a new window -in the currently selected frame. - -If this variable appears \"not to work\", because you add a name to it -but that buffer still appears in the selected window, look at the -values of `same-window-buffer-names' and `same-window-regexps'. -Those variables take precedence over this one." - :type '(repeat (choice :tag "Buffer" - :value "" - (string :format "%v") - (cons :tag "With attributes" - :format "%v" - :value ("" . nil) - (string :format "%v") - (repeat :tag "Attributes" - (cons :format "%v" - (symbol :tag "Parameter") - (sexp :tag "Value")))))) +its name is in this list, displays the buffer in a way specified +by `special-display-function'. `special-display-popup-frame' +\(the default for `special-display-function') usually displays +the buffer in a separate frame made with the parameters specified +by `special-display-frame-alist'. If `special-display-function' +has been set to some other function, that function is called with +the buffer as first, and nil as second argument. + +Alternatively, an element of this list can be specified as +\(BUFFER-NAME FRAME-PARAMETERS), where BUFFER-NAME is a buffer +name and FRAME-PARAMETERS an alist of \(PARAMETER . VALUE) pairs. +`special-display-popup-frame' will interpret such pairs as frame +parameters when it creates a special frame, overriding the +corresponding values from `special-display-frame-alist'. + +As a special case, if FRAME-PARAMETERS contains (same-window . t) +`special-display-popup-frame' displays that buffer in the +selected window. If FRAME-PARAMETERS contains (same-frame . t), +it displays that buffer in a window on the selected frame. + +If `special-display-function' specifies some other function than +`special-display-popup-frame', that function is called with the +buffer named BUFFER-NAME as first, and FRAME-PARAMETERS as second +argument. + +Finally, an element of this list can be also specified as +\(BUFFER-NAME FUNCTION OTHER-ARGS). In that case, +`special-display-popup-frame' will call FUNCTION with the buffer +named BUFFER-NAME as first argument, and OTHER-ARGS as the +second. If `special-display-function' specifies some other +function, that function is called with the buffer named +BUFFER-NAME as first, and the element's cdr as second argument. + +If this variable appears \"not to work\", because you added a +name to it but the corresponding buffer is displayed in the +selected window, look at the values of `same-window-buffer-names' +and `same-window-regexps'. Those variables take precedence over +this one. + +See also `special-display-regexps'." + :type '(repeat + (choice :tag "Buffer" + :value "" + (string :format "%v") + (cons :tag "With parameters" + :format "%v" + :value ("" . nil) + (string :format "%v") + (repeat :tag "Parameters" + (cons :format "%v" + (symbol :tag "Parameter") + (sexp :tag "Value")))) + (list :tag "With function" + :format "%v" + :value ("" . nil) + (string :format "%v") + (function :tag "Function") + (repeat :tag "Arguments" (sexp))))) + :group 'windows :group 'frames) +;;;###autoload +(put 'special-display-buffer-names 'risky-local-variable t) + (defcustom special-display-regexps nil - "List of regexps saying which buffers should have their own special frames. -When displaying a buffer with `display-buffer' or -`pop-to-buffer', if any regexp in this list matches the buffer -name, it makes a special frame for the buffer by calling -`special-display-function'. - -An element of the list can be a list instead of just a string. -There are two ways to use a list as an element: - (REGEXP FRAME-PARAMETERS...) (REGEXP FUNCTION OTHER-ARGS...) -In the first case, the FRAME-PARAMETERS are pairs of the form -\(PARAMETER . VALUE); these parameter values are used to create -the frame. In the second case, FUNCTION is called with BUFFER as -the first argument, followed by the OTHER-ARGS--it can display -the buffer in any way it likes. All this is done by the function -found in `special-display-function'. - -If the specified frame parameters include (same-buffer . t), the -buffer is displayed in the currently selected window. Otherwise, -if they include (same-frame . t), the buffer is displayed in a -new window in the currently selected frame. - -If this variable appears \"not to work\", because you add a -regexp to it but the matching buffers still appear in the + "List of regexps saying which buffers should be displayed specially. +Displaying a buffer with `display-buffer' or `pop-to-buffer', if +any regexp in this list matches its name, displays it specially +using `special-display-function'. `special-display-popup-frame' +\(the default for `special-display-function') usually displays +the buffer in a separate frame made with the parameters specified +by `special-display-frame-alist'. If `special-display-function' +has been set to some other function, that function is called with +the buffer as first, and nil as second argument. + +Alternatively, an element of this list can be specified as +\(REGEXP FRAME-PARAMETERS), where REGEXP is a regexp as above and +FRAME-PARAMETERS an alist of (PARAMETER . VALUE) pairs. +`special-display-popup-frame' will then interpret these pairs as +frame parameters when creating a special frame for a buffer whose +name matches REGEXP, overriding the corresponding values from +`special-display-frame-alist'. + +As a special case, if FRAME-PARAMETERS contains (same-window . t) +`special-display-popup-frame' displays buffers matching REGEXP in +the selected window. \(same-frame . t) in FRAME-PARAMETERS means +to display such buffers in a window on the selected frame. + +If `special-display-function' specifies some other function than +`special-display-popup-frame', that function is called with the +buffer whose name matched REGEXP as first, and FRAME-PARAMETERS +as second argument. + +Finally, an element of this list can be also specified as +\(REGEXP FUNCTION OTHER-ARGS). `special-display-popup-frame' +will then call FUNCTION with the buffer whose name matched +REGEXP as first, and OTHER-ARGS as second argument. If +`special-display-function' specifies some other function, that +function is called with the buffer whose name matched REGEXP +as first, and the element's cdr as second argument. + +If this variable appears \"not to work\", because you added a +name to it but the corresponding buffer is displayed in the selected window, look at the values of `same-window-buffer-names' and `same-window-regexps'. Those variables take precedence over -this one." - :type '(repeat (choice :tag "Buffer" - :value "" - (regexp :format "%v") - (cons :tag "With attributes" - :format "%v" - :value ("" . nil) - (regexp :format "%v") - (repeat :tag "Attributes" - (cons :format "%v" - (symbol :tag "Parameter") - (sexp :tag "Value")))))) +this one. + +See also `special-display-buffer-names'." + :type '(repeat + (choice :tag "Buffer" + :value "" + (regexp :format "%v") + (cons :tag "With parameters" + :format "%v" + :value ("" . nil) + (regexp :format "%v") + (repeat :tag "Parameters" + (cons :format "%v" + (symbol :tag "Parameter") + (sexp :tag "Value")))) + (list :tag "With function" + :format "%v" + :value ("" . nil) + (regexp :format "%v") + (function :tag "Function") + (repeat :tag "Arguments" (sexp))))) + :group 'windows :group 'frames) +(defun special-display-p (buffer-name) + "Return non-nil if a buffer named BUFFER-NAME gets a special frame. +More precisely, return t if `special-display-buffer-names' or +`special-display-regexps' contain a string entry equaling or +matching BUFFER-NAME. If `special-display-buffer-names' or +`special-display-regexps' contain a list entry whose car equals +or matches BUFFER-NAME, the return value is the cdr of that +entry." + (let (tmp) + (cond + ((not (stringp buffer-name))) + ((member buffer-name special-display-buffer-names) + t) + ((setq tmp (assoc buffer-name special-display-buffer-names)) + (cdr tmp)) + ((catch 'found + (dolist (regexp special-display-regexps) + (cond + ((stringp regexp) + (when (string-match-p regexp buffer-name) + (throw 'found t))) + ((and (consp regexp) (stringp (car regexp)) + (string-match-p (car regexp) buffer-name)) + (throw 'found (cdr regexp)))))))))) + (defcustom special-display-function 'special-display-popup-frame - "Function to call to make a new frame for a special buffer. -It is called with two arguments, the buffer and optional buffer -specific data, and should return a window displaying that buffer. -The default value normally makes a separate frame for the buffer, -using `special-display-frame-alist' to specify the frame -parameters. - -But if the buffer specific data includes (same-buffer . t) then -the buffer is displayed in the current selected window. -Otherwise if it includes (same-frame . t) then the buffer is -displayed in a new window in the currently selected frame. - -A buffer is special if it is listed in + "Function to call for displaying special buffers. +This function is called with two arguments - the buffer and, +optionally, a list - and should return a window displaying that +buffer. The default value usually makes a separate frame for the +buffer using `special-display-frame-alist' to specify the frame +parameters. See the definition of `special-display-popup-frame' +for how to specify such a function. + +A buffer is special when its name is either listed in `special-display-buffer-names' or matches a regexp in `special-display-regexps'." :type 'function :group 'frames) -(defun same-window-p (buffer-name) - "Return non-nil if a buffer named BUFFER-NAME would be shown in the \"same\" window. -This function returns non-nil if `display-buffer' or -`pop-to-buffer' would show a buffer named BUFFER-NAME in the -selected rather than \(as usual\) some other window. See -`same-window-buffer-names' and `same-window-regexps'." - (cond - ((not (stringp buffer-name))) - ;; The elements of `same-window-buffer-names' can be buffer - ;; names or cons cells whose cars are buffer names. - ((member buffer-name same-window-buffer-names)) - ((assoc buffer-name same-window-buffer-names)) - ((catch 'found - (dolist (regexp same-window-regexps) - ;; The elements of `same-window-regexps' can be regexps - ;; or cons cells whose cars are regexps. - (when (or (and (stringp regexp) - (string-match regexp buffer-name)) - (and (consp regexp) (stringp (car regexp)) - (string-match-p (car regexp) buffer-name))) - (throw 'found t))))))) - (defcustom same-window-buffer-names nil "List of names of buffers that should appear in the \"same\" window. `display-buffer' and `pop-to-buffer' show a buffer whose name is on this list in the selected rather than some other window. An element of this list can be a cons cell instead of just a -string. In that case the car must be a string specifying the -buffer name. This is for compatibility with +string. In that case, the cell's car must be a string specifying +the buffer name. This is for compatibility with `special-display-buffer-names'; the cdr of the cons cell is ignored. @@ -705,18 +753,39 @@ matches a regexp on this list in the selected rather than some other window. An element of this list can be a cons cell instead of just a -string. In that case the car must be a string, which specifies +string. In that case, the cell's car must be a regexp matching the buffer name. This is for compatibility with -`special-display-buffer-names'; the cdr of the cons cell is -ignored. +`special-display-regexps'; the cdr of the cons cell is ignored. See also `same-window-buffer-names'." :type '(repeat (regexp :format "%v")) :group 'windows) +(defun same-window-p (buffer-name) + "Return non-nil if a buffer named BUFFER-NAME would be shown in the \"same\" window. +This function returns non-nil if `display-buffer' or +`pop-to-buffer' would show a buffer named BUFFER-NAME in the +selected rather than \(as usual\) some other window. See +`same-window-buffer-names' and `same-window-regexps'." + (cond + ((not (stringp buffer-name))) + ;; The elements of `same-window-buffer-names' can be buffer + ;; names or cons cells whose cars are buffer names. + ((member buffer-name same-window-buffer-names)) + ((assoc buffer-name same-window-buffer-names)) + ((catch 'found + (dolist (regexp same-window-regexps) + ;; The elements of `same-window-regexps' can be regexps + ;; or cons cells whose cars are regexps. + (when (or (and (stringp regexp) + (string-match regexp buffer-name)) + (and (consp regexp) (stringp (car regexp)) + (string-match-p (car regexp) buffer-name))) + (throw 'found t))))))) + (defcustom pop-up-frames nil "Whether `display-buffer' should make a separate frame. -If nil, never make a seperate frame. +If nil, never make a separate frame. If the value is `graphic-only', make a separate frame on graphic displays only. Any other non-nil value means always make a separate frame." @@ -739,66 +808,78 @@ that frame." :type 'boolean :group 'windows) -(defcustom split-height-threshold 80 - "Minimum height of window to be split vertically. -If the value is a number, `display-buffer' can split a window -only if it has at least as many lines. If the value is nil, -`display-buffer' cannot split a window vertically. - -If the window is the only window on its frame, `display-buffer' -can split it regardless of this value." - :type '(choice (const nil) (number :tag "lines")) +(defcustom split-window-preferred-function 'split-window-sensibly + "Function called by `display-buffer' routines to split a window. +This function is called with a window as single argument and is +supposed to split that window and return the new window. If the +window can (or shall) not be split, it is supposed to return nil. +The default is to call the function `split-window-sensibly' which +tries to split the window in a way which seems most suitable. +You can customize the options `split-height-threshold' and/or +`split-width-threshold' in order to have `split-window-sensibly' +prefer either vertical or horizontal splitting. + +If you set this to any other function, bear in mind that the +`display-buffer' routines may call this function two times. The +argument of the first call is the largest window on its frame. +If that call fails to return a live window, the function is +called again with the least recently used window as argument. If +that call fails too, `display-buffer' will use an existing window +to display its buffer. + +The window selected at the time `display-buffer' was invoked is +still selected when this function is called. Hence you can +compare the window argument with the value of `selected-window' +if you intend to split the selected window instead or if you do +not want to split the selected window." + :type 'function :version "23.1" :group 'windows) -(defcustom split-width-threshold 160 - "Minimum width of window to be split horizontally. -If the value is a number, `display-buffer' can split a window -only if it has at least as many columns. If the value is nil, -`display-buffer' cannot split a window horizontally." - :type '(choice (const nil) (number :tag "columns")) +(defcustom split-height-threshold 80 + "Minimum height for splitting windows sensibly. +If this is an integer, `split-window-sensibly' may split a window +vertically only if it has at least this many lines. If this is +nil, `split-window-sensibly' is not allowed to split a window +vertically. If, however, a window is the only window on its +frame, `split-window-sensibly' may split it vertically +disregarding the value of this variable." + :type '(choice (const nil) (integer :tag "lines")) :version "23.1" :group 'windows) -(defcustom split-window-preferred-function nil - "Function used by `display-buffer' to split windows. -If non-nil, a function called with a window as single argument -supposed to split that window and return the new window. If the -function returns nil the window is not split. - -If nil, `display-buffer' will split the window respecting the -values of `split-height-threshold' and `split-width-threshold'." - :type '(choice (const nil) (function :tag "Function")) +(defcustom split-width-threshold 160 + "Minimum width for splitting windows sensibly. +If this is an integer, `split-window-sensibly' may split a window +horizontally only if it has at least this many columns. If this +is nil, `split-window-sensibly' is not allowed to split a window +horizontally." + :type '(choice (const nil) (integer :tag "columns")) :version "23.1" :group 'windows) -(defun window--splittable-p (window &optional horizontal) - "Return non-nil if window WINDOW can be split evenly. -Optional argument HORIZONTAL non-nil means check whether WINDOW -can be split horizontally. +(defun window-splittable-p (window &optional horizontal) + "Return non-nil if `split-window-sensibly' may split WINDOW. +Optional argument HORIZONTAL nil or omitted means check whether +`split-window-sensibly' may split WINDOW vertically. HORIZONTAL +non-nil means check whether WINDOW may be split horizontally. -WINDOW can be split vertically when the following conditions +WINDOW may be split vertically when the following conditions hold: - - `window-size-fixed' is either nil or equals `width' for the buffer of WINDOW. - -- `split-height-threshold' is a number and WINDOW is at least as +- `split-height-threshold' is an integer and WINDOW is at least as high as `split-height-threshold'. - - When WINDOW is split evenly, the emanating windows are at least `window-min-height' lines tall and can accommodate at least one - line plus - if WINDOW has one - a modeline. + line plus - if WINDOW has one - a mode line. -WINDOW can be split horizontally when the following conditions +WINDOW may be split horizontally when the following conditions hold: - - `window-size-fixed' is either nil or equals `height' for the buffer of WINDOW. - -- `split-width-threshold' is a number and WINDOW is at least as +- `split-width-threshold' is an integer and WINDOW is at least as wide as `split-width-threshold'. - - When WINDOW is split evenly, the emanating windows are at least `window-min-width' or two (whichever is larger) columns wide." (when (window-live-p window) @@ -827,41 +908,85 @@ hold: (* 2 (max window-min-height (if mode-line-format 2 1)))))))))) +(defun split-window-sensibly (window) + "Split WINDOW in a way suitable for `display-buffer'. +If `split-height-threshold' specifies an integer, WINDOW is at +least `split-height-threshold' lines tall and can be split +vertically, split WINDOW into two windows one above the other and +return the lower window. Otherwise, if `split-width-threshold' +specifies an integer, WINDOW is at least `split-width-threshold' +columns wide and can be split horizontally, split WINDOW into two +windows side by side and return the window on the right. If this +can't be done either and WINDOW is the only window on its frame, +try to split WINDOW vertically disregarding any value specified +by `split-height-threshold'. If that succeeds, return the lower +window. Return nil otherwise. + +By default `display-buffer' routines call this function to split +the largest or least recently used window. To change the default +customize the option `split-window-preferred-function'. + +You can enforce this function to not split WINDOW horizontally, +by setting \(or binding) the variable `split-width-threshold' to +nil. If, in addition, you set `split-height-threshold' to zero, +chances increase that this function does split WINDOW vertically. + +In order to not split WINDOW vertically, set \(or bind) the +variable `split-height-threshold' to nil. Additionally, you can +set `split-width-threshold' to zero to make a horizontal split +more likely to occur. + +Have a look at the function `window-splittable-p' if you want to +know how `split-window-sensibly' determines whether WINDOW can be +split." + (or (and (window-splittable-p window) + ;; Split window vertically. + (with-selected-window window + (split-window-vertically))) + (and (window-splittable-p window t) + ;; Split window horizontally. + (with-selected-window window + (split-window-horizontally))) + (and (eq window (frame-root-window (window-frame window))) + (not (window-minibuffer-p window)) + ;; If WINDOW is the only window on its frame and is not the + ;; minibuffer window, try to split it vertically disregarding + ;; the value of `split-height-threshold'. + (let ((split-height-threshold 0)) + (when (window-splittable-p window) + (with-selected-window window + (split-window-vertically))))))) + (defun window--try-to-split-window (window) - "Split window WINDOW if it is splittable. -See `window--splittable-p' for how to determine whether a window -is splittable. If WINDOW can be split, return the value returned -by `split-window' or `split-window-preferred-function'." - (when (and (window-live-p window) - (not (frame-parameter (window-frame window) 'unsplittable))) - (if (functionp split-window-preferred-function) - ;; `split-window-preferred-function' is specified, so use it. - (funcall split-window-preferred-function window) - (or (and (window--splittable-p window) - ;; Split window vertically. - (split-window window)) - (and (window--splittable-p window t) - ;; Split window horizontally. - (split-window window nil t)) - (and (eq window (frame-root-window (window-frame window))) - (not (window-minibuffer-p window)) - ;; If WINDOW is the only window on its frame and not the - ;; minibuffer window, attempt to split it vertically - ;; disregarding the value of `split-height-threshold'. - (let ((split-height-threshold 0)) - (and (window--splittable-p window) - (split-window window)))))))) + "Try to split WINDOW. +Return value returned by `split-window-preferred-function' if it +represents a live window, nil otherwise." + (and (window-live-p window) + (not (frame-parameter (window-frame window) 'unsplittable)) + (let ((new-window + ;; Since `split-window-preferred-function' might + ;; throw an error use `condition-case'. + (condition-case nil + (funcall split-window-preferred-function window) + (error nil)))) + (and (window-live-p new-window) new-window)))) (defun window--frame-usable-p (frame) - "Return frame FRAME if it can be used to display another buffer." - (when (framep frame) + "Return FRAME if it can be used to display a buffer." + (when (frame-live-p frame) (let ((window (frame-root-window frame))) ;; `frame-root-window' may be an internal window which is considered ;; "dead" by `window-live-p'. Hence if `window' is not live we ;; implicitly know that `frame' has a visible window we can use. - (when (or (not (window-live-p window)) - (and (not (window-minibuffer-p window)) - (not (window-dedicated-p window)))) + (unless (and (window-live-p window) + (or (window-minibuffer-p window) + ;; If the window is soft-dedicated, the frame is usable. + ;; Actually, even if the window is really dedicated, + ;; the frame is still usable by splitting it. + ;; At least Emacs-22 allowed it, and it is desirable + ;; when displaying same-frame windows. + nil ; (eq t (window-dedicated-p window)) + )) frame)))) (defcustom even-window-heights t @@ -873,7 +998,7 @@ window that appears above or below the selected window." :group 'windows) (defun window--even-window-heights (window) - "Even heights of window WINDOW and selected window. + "Even heights of WINDOW and selected window. Do this only if these windows are vertically adjacent to each other, `even-window-heights' is non-nil, and the selected window is higher than WINDOW." @@ -881,7 +1006,7 @@ is higher than WINDOW." (not (eq window (selected-window))) ;; Don't resize minibuffer windows. (not (window-minibuffer-p (selected-window))) - (> (window-height (selected-window)) (window-height window)) + (> (window-height (selected-window)) (window-height window)) (eq (window-frame window) (window-frame (selected-window))) (let ((sel-edges (window-edges (selected-window))) (win-edges (window-edges window))) @@ -897,7 +1022,7 @@ is higher than WINDOW." (error nil))))) (defun window--display-buffer-1 (window) - "Raise the frame containing the window WINDOW. + "Raise the frame containing WINDOW. Do not raise the selected frame. Return WINDOW." (let* ((frame (window-frame window)) (visible (frame-visible-p frame))) @@ -911,13 +1036,21 @@ Do not raise the selected frame. Return WINDOW." (raise-frame frame)) window)) -(defun window--display-buffer-2 (buffer window) - "Display buffer BUFFER in window WINDOW and make its frame visible. +(defun window--display-buffer-2 (buffer window &optional dedicated) + "Display BUFFER in WINDOW and make its frame visible. +Set `window-dedicated-p' to DEDICATED if non-nil. Return WINDOW." (when (and (buffer-live-p buffer) (window-live-p window)) (set-window-buffer window buffer) + (when dedicated + (set-window-dedicated-p window dedicated)) (window--display-buffer-1 window))) +(defvar display-buffer-mark-dedicated nil + "If non-nil, `display-buffer' marks the windows it creates as dedicated. +The actual non-nil value of this variable will be copied to the +`window-dedicated-p' flag.") + (defun display-buffer (buffer-or-name &optional not-this-window frame) "Make buffer BUFFER-OR-NAME appear in some window but don't select it. BUFFER-OR-NAME must be a buffer or the name of an existing @@ -990,10 +1123,15 @@ consider all visible or iconified frames." (not (last-nonminibuffer-frame))) 0) (last-nonminibuffer-frame)))) - (and (setq window-to-use (get-buffer-window buffer frames)) - (or can-use-selected-window - (not (eq (selected-window) window-to-use))))) - ;; If the buffer is already displayed in some window use that. + (setq window-to-use + (catch 'found + ;; Search frames for a window displaying BUFFER. Return + ;; the selected window only if we are allowed to do so. + (dolist (window (get-buffer-window-list buffer 'nomini frames)) + (when (or can-use-selected-window + (not (eq (selected-window) window))) + (throw 'found window)))))) + ;; The buffer is already displayed in some window; use that. (window--display-buffer-1 window-to-use)) ((and special-display-function ;; `special-display-p' returns either t or a list of frame @@ -1004,8 +1142,8 @@ consider all visible or iconified frames." buffer (if (listp pars) pars)))))) ((or use-pop-up-frames (not frame-to-use)) ;; We want or need a new frame. - (window--display-buffer-2 - buffer (frame-selected-window (funcall pop-up-frame-function)))) + (let ((win (frame-selected-window (funcall pop-up-frame-function)))) + (window--display-buffer-2 buffer win display-buffer-mark-dedicated))) ((and pop-up-windows ;; Make a new window. (or (not (frame-parameter frame-to-use 'unsplittable)) @@ -1020,16 +1158,34 @@ consider all visible or iconified frames." (or (window--try-to-split-window (get-largest-window frame-to-use t)) (window--try-to-split-window - (get-lru-window frame-to-use t)))) - (window--display-buffer-2 buffer window-to-use))) - ((setq window-to-use - ;; Reuse an existing window. - (or (get-lru-window frame-to-use) - (get-buffer-window buffer 'visible) - (get-largest-window 'visible nil) - (get-buffer-window buffer 0) - (get-largest-window 0 nil) - (frame-selected-window (funcall pop-up-frame-function)))) + (get-lru-window frame-to-use t))))) + (window--display-buffer-2 buffer window-to-use + display-buffer-mark-dedicated)) + ((let ((window-to-undedicate + ;; When NOT-THIS-WINDOW is non-nil, temporarily dedicate + ;; the selected window to its buffer, to avoid that some of + ;; the `get-' routines below choose it. (Bug#1415) + (and not-this-window (not (window-dedicated-p)) + (set-window-dedicated-p (selected-window) t) + (selected-window)))) + (unwind-protect + (setq window-to-use + ;; Reuse an existing window. + (or (get-lru-window frame-to-use) + (let ((window (get-buffer-window buffer 'visible))) + (unless (and not-this-window + (eq window (selected-window))) + window)) + (get-largest-window 'visible) + (let ((window (get-buffer-window buffer 0))) + (unless (and not-this-window + (eq window (selected-window))) + window)) + (get-largest-window 0) + (frame-selected-window (funcall pop-up-frame-function)))) + (when (window-live-p window-to-undedicate) + ;; Restore dedicated status of selected window. + (set-window-dedicated-p window-to-undedicate nil)))) (window--even-window-heights window-to-use) (window--display-buffer-2 buffer window-to-use))))) @@ -1093,16 +1249,17 @@ point in both children." :type 'boolean :group 'windows) -(defun split-window-vertically (&optional arg) - "Split current window into two windows, one above the other. -The uppermost window gets ARG lines and the other gets the rest. -Negative ARG means select the size of the lowermost window instead. -With no argument, split equally or close to it. -Both windows display the same buffer now current. +(defun split-window-vertically (&optional size) + "Split selected window into two windows, one above the other. +The upper window gets SIZE lines and the lower one gets the rest. +SIZE negative means the lower window gets -SIZE lines and the +upper one the rest. With no argument, split windows equally or +close to it. Both windows display the same buffer, now current. -If the variable `split-window-keep-point' is non-nil, both new windows -will get the same value of point as the current window. This is often -more convenient for editing. The upper window is the selected window. +If the variable `split-window-keep-point' is non-nil, both new +windows will get the same value of point as the selected window. +This is often more convenient for editing. The upper window is +the selected window. Otherwise, we choose window starts so as to minimize the amount of redisplay; this is convenient on slow terminals. The new selected @@ -1114,87 +1271,85 @@ Regardless of the value of `split-window-keep-point', the upper window is the original one and the return value is the new, lower window." (interactive "P") - (let ((old-w (selected-window)) + (let ((old-window (selected-window)) (old-point (point)) - (size (and arg (prefix-numeric-value arg))) - (window-full-p nil) - new-w bottom moved) - (and size (< size 0) (setq size (+ (window-height) size))) - (setq new-w (split-window nil size)) - (or split-window-keep-point - (progn - (save-excursion - (set-buffer (window-buffer)) - (goto-char (window-start)) - (setq moved (vertical-motion (window-height))) - (set-window-start new-w (point)) - (if (> (point) (window-point new-w)) - (set-window-point new-w (point))) - (and (= moved (window-height)) - (progn - (setq window-full-p t) - (vertical-motion -1))) - (setq bottom (point))) - (and window-full-p - (<= bottom (point)) - (set-window-point old-w (1- bottom))) - (and window-full-p - (<= (window-start new-w) old-point) - (progn - (set-window-point new-w old-point) - (select-window new-w))))) - (split-window-save-restore-data new-w old-w))) + (size (and size (prefix-numeric-value size))) + moved-by-window-height moved new-window bottom) + (and size (< size 0) + ;; Handle negative SIZE value. + (setq size (+ (window-height) size))) + (setq new-window (split-window nil size)) + (unless split-window-keep-point + (with-current-buffer (window-buffer) + (goto-char (window-start)) + (setq moved (vertical-motion (window-height))) + (set-window-start new-window (point)) + (when (> (point) (window-point new-window)) + (set-window-point new-window (point))) + (when (= moved (window-height)) + (setq moved-by-window-height t) + (vertical-motion -1)) + (setq bottom (point))) + (and moved-by-window-height + (<= bottom (point)) + (set-window-point old-window (1- bottom))) + (and moved-by-window-height + (<= (window-start new-window) old-point) + (set-window-point new-window old-point) + (select-window new-window))) + (split-window-save-restore-data new-window old-window))) ;; This is to avoid compiler warnings. (defvar view-return-to-alist) -(defun split-window-save-restore-data (new-w old-w) +(defun split-window-save-restore-data (new-window old-window) (with-current-buffer (window-buffer) - (if view-mode - (let ((old-info (assq old-w view-return-to-alist))) - (if old-info - (push (cons new-w (cons (car (cdr old-info)) t)) - view-return-to-alist)))) - new-w)) - -(defun split-window-horizontally (&optional arg) - "Split current window into two windows side by side. -This window becomes the leftmost of the two, and gets ARG columns. -Negative ARG means select the size of the rightmost window instead. -The argument includes the width of the window's scroll bar; if there -are no scroll bars, it includes the width of the divider column -to the window's right, if any. No ARG means split equally. - -The original, leftmost window remains selected. -The return value is the new, rightmost window." + (when view-mode + (let ((old-info (assq old-window view-return-to-alist))) + (when old-info + (push (cons new-window (cons (car (cdr old-info)) t)) + view-return-to-alist)))) + new-window)) + +(defun split-window-horizontally (&optional size) + "Split selected window into two windows side by side. +The selected window becomes the left one and gets SIZE columns. +SIZE negative means the right window gets -SIZE lines. + +SIZE includes the width of the window's scroll bar; if there are +no scroll bars, it includes the width of the divider column to +the window's right, if any. SIZE omitted or nil means split +window equally. + +The selected window remains selected. Return the new window." (interactive "P") - (let ((old-w (selected-window)) - (size (and arg (prefix-numeric-value arg)))) + (let ((old-window (selected-window)) + (size (and size (prefix-numeric-value size)))) (and size (< size 0) + ;; Handle negative SIZE value. (setq size (+ (window-width) size))) - (split-window-save-restore-data (split-window nil size t) old-w))) + (split-window-save-restore-data (split-window nil size t) old-window))) (defun set-window-text-height (window height) - "Sets the height in lines of the text display area of WINDOW to HEIGHT. -This doesn't include the mode-line (or header-line if any) or any -partial-height lines in the text display area. - -If WINDOW is nil, the selected window is used. - -Note that the current implementation of this function cannot always set -the height exactly, but attempts to be conservative, by allocating more -lines than are actually needed in the case where some error may be present." + "Set the height in lines of the text display area of WINDOW to HEIGHT. +HEIGHT doesn't include the mode line or header line, if any, or +any partial-height lines in the text display area. + +Note that the current implementation of this function cannot +always set the height exactly, but attempts to be conservative, +by allocating more lines than are actually needed in the case +where some error may be present." (let ((delta (- height (window-text-height window)))) (unless (zerop delta) ;; Setting window-min-height to a value like 1 can lead to very ;; bizarre displays because it also allows Emacs to make *other* ;; windows 1-line tall, which means that there's no more space for ;; the modeline. - (let ((window-min-height (min 2 height))) ;One text line plus a modeline. + (let ((window-min-height (min 2 height))) ; One text line plus a modeline. (if (and window (not (eq window (selected-window)))) (save-selected-window - (select-window window) + (select-window window 'norecord) (enlarge-window delta)) (enlarge-window delta)))))) @@ -1260,96 +1415,129 @@ in some window." (1+ (vertical-motion (buffer-size) window)))))) (defun fit-window-to-buffer (&optional window max-height min-height) - "Make WINDOW the right height to display its contents exactly. -If WINDOW is omitted or nil, it defaults to the selected window. -If the optional argument MAX-HEIGHT is supplied, it is the maximum height - the window is allowed to be, defaulting to the frame height. -If the optional argument MIN-HEIGHT is supplied, it is the minimum - height the window is allowed to be, defaulting to `window-min-height'. - -The heights in MAX-HEIGHT and MIN-HEIGHT include the mode-line and/or -header-line." + "Adjust height of WINDOW to display its buffer's contents exactly. +WINDOW defaults to the selected window. +Optional argument MAX-HEIGHT specifies the maximum height of the +window and defaults to the maximum permissible height of a window +on WINDOW's frame. +Optional argument MIN-HEIGHT specifies the minimum height of the +window and defaults to `window-min-height'. +Both, MAX-HEIGHT and MIN-HEIGHT are specified in lines and +include the mode line and header line, if any. + +Return non-nil if height was orderly adjusted, nil otherwise. + +Caution: This function can delete WINDOW and/or other windows +when their height shrinks to less than MIN-HEIGHT." (interactive) + ;; Do all the work in WINDOW and its buffer and restore the selected + ;; window and the current buffer when we're done. + (let ((old-buffer (current-buffer)) + value) + (with-selected-window (or window (setq window (selected-window))) + (set-buffer (window-buffer)) + ;; Use `condition-case' to handle any fixed-size windows and other + ;; pitfalls nearby. + (condition-case nil + (let* (;; MIN-HEIGHT must not be less than 1 and defaults to + ;; `window-min-height'. + (min-height (max (or min-height window-min-height) 1)) + (max-window-height + ;; Maximum height of any window on this frame. + (min (window-height (frame-root-window)) (frame-height))) + ;; MAX-HEIGHT must not be larger than max-window-height and + ;; defaults to max-window-height. + (max-height + (min (or max-height max-window-height) max-window-height)) + (desired-height + ;; The height necessary to show all of WINDOW's buffer, + ;; constrained by MIN-HEIGHT and MAX-HEIGHT. + (max + (min + ;; For an empty buffer `count-screen-lines' returns zero. + ;; Even in that case we need one line for the cursor. + (+ (max (count-screen-lines) 1) + ;; For non-minibuffers count the mode line, if any. + (if (and (not (window-minibuffer-p)) mode-line-format) + 1 0) + ;; Count the header line, if any. + (if header-line-format 1 0)) + max-height) + min-height)) + (delta + ;; How much the window height has to change. + (if (= (window-height) (window-height (frame-root-window))) + ;; Don't try to resize a full-height window. + 0 + (- desired-height (window-height)))) + ;; Do something reasonable so `enlarge-window' can make + ;; windows as small as MIN-HEIGHT. + (window-min-height (min min-height window-min-height))) + ;; Don't try to redisplay with the cursor at the end on its + ;; own line--that would force a scroll and spoil things. + (when (and (eobp) (bolp) (not (bobp))) + (set-window-point window (1- (window-point)))) + ;; Adjust WINDOW's height to the nominally correct one + ;; (which may actually be slightly off because of variable + ;; height text, etc). + (unless (zerop delta) + (enlarge-window delta)) + ;; `enlarge-window' might have deleted WINDOW, so make sure + ;; WINDOW's still alive for the remainder of this. + ;; Note: Deleting WINDOW is clearly counter-intuitive in + ;; this context, but we can't do much about it given the + ;; current semantics of `enlarge-window'. + (when (window-live-p window) + ;; Check if the last line is surely fully visible. If + ;; not, enlarge the window. + (let ((end (save-excursion + (goto-char (point-max)) + (when (and (bolp) (not (bobp))) + ;; Don't include final newline. + (backward-char 1)) + (when truncate-lines + ;; If line-wrapping is turned off, test the + ;; beginning of the last line for + ;; visibility instead of the end, as the + ;; end of the line could be invisible by + ;; virtue of extending past the edge of the + ;; window. + (forward-line 0)) + (point)))) + (set-window-vscroll window 0) + (while (and (< desired-height max-height) + (= desired-height (window-height)) + (not (pos-visible-in-window-p end))) + (enlarge-window 1) + (setq desired-height (1+ desired-height)))) + ;; Return non-nil only if nothing "bad" happened. + (setq value t))) + (error nil))) + (when (buffer-live-p old-buffer) + (set-buffer old-buffer)) + value)) - (when (null window) - (setq window (selected-window))) - (when (null max-height) - (setq max-height (frame-height (window-frame window)))) - - (let* ((buf - ;; Buffer that is displayed in WINDOW - (window-buffer window)) - (window-height - ;; The current height of WINDOW - (window-height window)) - (desired-height - ;; The height necessary to show the buffer displayed by WINDOW - ;; (`count-screen-lines' always works on the current buffer). - (with-current-buffer buf - (+ (count-screen-lines) - ;; If the buffer is empty, (count-screen-lines) is - ;; zero. But, even in that case, we need one text line - ;; for cursor. - (if (= (point-min) (point-max)) - 1 0) - ;; For non-minibuffers, count the mode-line, if any - (if (and (not (window-minibuffer-p window)) - mode-line-format) - 1 0) - ;; Count the header-line, if any - (if header-line-format 1 0)))) - (delta - ;; Calculate how much the window height has to change to show - ;; desired-height lines, constrained by MIN-HEIGHT and MAX-HEIGHT. - (- (max (min desired-height max-height) - (or min-height window-min-height)) - window-height))) - - ;; Don't try to redisplay with the cursor at the end - ;; on its own line--that would force a scroll and spoil things. - (when (with-current-buffer buf - (and (eobp) (bolp) (not (bobp)))) - (set-window-point window (1- (window-point window)))) - - (save-selected-window - (select-window window) - - ;; Adjust WINDOW to the nominally correct size (which may actually - ;; be slightly off because of variable height text, etc). - (unless (zerop delta) - (enlarge-window delta)) - - ;; Check if the last line is surely fully visible. If not, - ;; enlarge the window. - (let ((end (with-current-buffer buf - (save-excursion - (goto-char (point-max)) - (when (and (bolp) (not (bobp))) - ;; Don't include final newline - (backward-char 1)) - (when truncate-lines - ;; If line-wrapping is turned off, test the - ;; beginning of the last line for visibility - ;; instead of the end, as the end of the line - ;; could be invisible by virtue of extending past - ;; the edge of the window. - (forward-line 0)) - (point))))) - (set-window-vscroll window 0) - (while (and (< desired-height max-height) - (= desired-height (window-height window)) - (not (pos-visible-in-window-p end window))) - (enlarge-window 1) - (setq desired-height (1+ desired-height))))))) +(defun window-safely-shrinkable-p (&optional window) + "Return t if WINDOW can be shrunk without shrinking other windows. +WINDOW defaults to the selected window." + (with-selected-window (or window (selected-window)) + (let ((edges (window-edges))) + (or (= (nth 2 edges) (nth 2 (window-edges (previous-window)))) + (= (nth 0 edges) (nth 0 (window-edges (next-window)))))))) (defun shrink-window-if-larger-than-buffer (&optional window) - "Shrink the WINDOW to be as small as possible to display its contents. -If WINDOW is omitted or nil, it defaults to the selected window. -Do not shrink to less than `window-min-height' lines. -Do nothing if the buffer contains more lines than the present window height, -or if some of the window's contents are scrolled out of view, -or if shrinking this window would also shrink another window, -or if the window is the only window of its frame." + "Shrink height of WINDOW if its buffer doesn't need so many lines. +More precisely, shrink WINDOW vertically to be as small as +possible, while still showing the full contents of its buffer. +WINDOW defaults to the selected window. + +Do not shrink to less than `window-min-height' lines. Do nothing +if the buffer contains more lines than the present window height, +or if some of the window's contents are scrolled out of view, or +if shrinking this window would also shrink another window, or if +the window is the only window of its frame. + +Return non-nil if the window was shrunk, nil otherwise." (interactive) (when (null window) (setq window (selected-window))) @@ -1357,7 +1545,7 @@ or if the window is the only window of its frame." (mini (frame-parameter frame 'minibuffer)) (edges (window-edges window))) (if (and (not (eq window (frame-root-window frame))) - (window-safely-shrinkable-p) + (window-safely-shrinkable-p window) (pos-visible-in-window-p (point-min) window) (not (eq mini 'only)) (or (not mini) @@ -1395,88 +1583,131 @@ or if the window is the only window of its frame." (error nil))))) (defun quit-window (&optional kill window) - "Quit the current buffer. Bury it, and maybe delete the selected frame. -\(The frame is deleted if it contains a dedicated window for the buffer.) -With a prefix argument, kill the buffer instead. + "Quit WINDOW and bury its buffer. +With a prefix argument, kill the buffer instead. WINDOW defaults +to the selected window. -Noninteractively, if KILL is non-nil, then kill the current buffer, -otherwise bury it. +If WINDOW is non-nil, dedicated, or a minibuffer window, delete +it and, if it's alone on its frame, its frame too. Otherwise, or +if deleting WINDOW fails in any of the preceding cases, display +another buffer in WINDOW using `switch-to-buffer'. -If WINDOW is non-nil, it specifies a window; we delete that window, -and the buffer that is killed or buried is the one in that window." +Optional argument KILL non-nil means kill WINDOW's buffer. +Otherwise, bury WINDOW's buffer, see `bury-buffer'." (interactive "P") - (let ((buffer (window-buffer window)) - (frame (window-frame (or window (selected-window)))) - (window-solitary - (save-selected-window - (if window - (select-window window)) - (one-window-p t))) - window-handled) - - (save-selected-window - (if window - (select-window window)) - (or (window-minibuffer-p) - (window-dedicated-p (selected-window)) - (switch-to-buffer (other-buffer)))) - - ;; Get rid of the frame, if it has just one dedicated window - ;; and other visible frames exist. - (and (or (window-minibuffer-p) (window-dedicated-p window)) - (delq frame (visible-frame-list)) - window-solitary - (if (and (eq default-minibuffer-frame frame) - (= 1 (length (minibuffer-frame-list)))) - (setq window nil) - (delete-frame frame) - (setq window-handled t))) + (let ((buffer (window-buffer window))) + (if (or window + (window-minibuffer-p window) + (window-dedicated-p window)) + ;; WINDOW is either non-nil, a minibuffer window, or dedicated; + ;; try to delete it. + (let* ((window (or window (selected-window))) + (frame (window-frame window))) + (if (eq window (frame-root-window frame)) + ;; WINDOW is alone on its frame. `delete-windows-on' + ;; knows how to handle that case. + (delete-windows-on buffer frame) + ;; There are other windows on its frame, delete WINDOW. + (delete-window window))) + ;; Otherwise, switch to another buffer in the selected window. + (switch-to-buffer nil)) ;; Deal with the buffer. (if kill (kill-buffer buffer) - (bury-buffer buffer)) - - ;; Maybe get rid of the window. - (and window (not window-handled) (not window-solitary) - (delete-window window)))) + (bury-buffer buffer)))) (defvar recenter-last-op nil "Indicates the last recenter operation performed. -Possible values: `top', `middle', `bottom'.") +Possible values: `top', `middle', `bottom', integer or float numbers.") + +(defcustom recenter-positions '(middle top bottom) + "Cycling order for `recenter-top-bottom'. +A list of elements with possible values `top', `middle', `bottom', +integer or float numbers that define the cycling order for +the command `recenter-top-bottom'. + +Top and bottom destinations are `scroll-margin' lines the from true +window top and bottom. Middle redraws the frame and centers point +vertically within the window. Integer number moves current line to +the specified absolute window-line. Float number between 0.0 and 1.0 +means the percentage of the screen space from the top. The default +cycling order is middle -> top -> bottom." + :type '(repeat (choice + (const :tag "Top" top) + (const :tag "Middle" middle) + (const :tag "Bottom" bottom) + (integer :tag "Line number") + (float :tag "Percentage"))) + :version "23.2" + :group 'windows) (defun recenter-top-bottom (&optional arg) - "Move current line to window center, top, and bottom, successively. -With no prefix argument, the first call redraws the frame and - centers point vertically within the window. Successive calls - scroll the window, placing point on the top, bottom, and middle - consecutively. The cycling order is middle -> top -> bottom. + "Move current buffer line to the specified window line. +With no prefix argument, successive calls place point according +to the cycling order defined by `recenter-positions'. A prefix argument is handled like `recenter': With numeric prefix ARG, move current line to window-line ARG. - With plain `C-u', move current line to window center. - -Top and bottom destinations are actually `scroll-margin' lines - the from true window top and bottom." + With plain `C-u', move current line to window center." (interactive "P") (cond - (arg (recenter arg)) ; Always respect ARG. - ((or (not (eq this-command last-command)) - (eq recenter-last-op 'bottom)) - (setq recenter-last-op 'middle) - (recenter)) + (arg (recenter arg)) ; Always respect ARG. (t + (setq recenter-last-op + (if (eq this-command last-command) + (car (or (cdr (member recenter-last-op recenter-positions)) + recenter-positions)) + (car recenter-positions))) (let ((this-scroll-margin (min (max 0 scroll-margin) (truncate (/ (window-body-height) 4.0))))) (cond ((eq recenter-last-op 'middle) - (setq recenter-last-op 'top) - (recenter this-scroll-margin)) + (recenter)) ((eq recenter-last-op 'top) - (setq recenter-last-op 'bottom) - (recenter (- -1 this-scroll-margin)))))))) + (recenter this-scroll-margin)) + ((eq recenter-last-op 'bottom) + (recenter (- -1 this-scroll-margin))) + ((integerp recenter-last-op) + (recenter recenter-last-op)) + ((floatp recenter-last-op) + (recenter (round (* recenter-last-op (window-height)))))))))) (define-key global-map [?\C-l] 'recenter-top-bottom) + +(defun move-to-window-line-top-bottom (&optional arg) + "Position point relative to window. + +With a prefix argument ARG, acts like `move-to-window-line'. + +With no argument, positions point at center of window. +Successive calls position point at positions defined +by `recenter-positions'." + (interactive "P") + (cond + (arg (move-to-window-line arg)) ; Always respect ARG. + (t + (setq recenter-last-op + (if (eq this-command last-command) + (car (or (cdr (member recenter-last-op recenter-positions)) + recenter-positions)) + (car recenter-positions))) + (let ((this-scroll-margin + (min (max 0 scroll-margin) + (truncate (/ (window-body-height) 4.0))))) + (cond ((eq recenter-last-op 'middle) + (call-interactively 'move-to-window-line)) + ((eq recenter-last-op 'top) + (move-to-window-line this-scroll-margin)) + ((eq recenter-last-op 'bottom) + (move-to-window-line (- -1 this-scroll-margin))) + ((integerp recenter-last-op) + (move-to-window-line recenter-last-op)) + ((floatp recenter-last-op) + (move-to-window-line (round (* recenter-last-op (window-height)))))))))) + +(define-key global-map [?\M-r] 'move-to-window-line-top-bottom) + (defvar mouse-autoselect-window-timer nil "Timer used by delayed window autoselection.") @@ -1637,12 +1868,12 @@ This may be a useful alternative binding for \\[delete-other-windows] (mapc 'delete-window delenda))) (defun truncated-partial-width-window-p (&optional window) - "Non-nil if lines in WINDOW are specifically truncated due to its width. -This returns nil if WINDOW is not a partial-width window + "Return non-nil if lines in WINDOW are specifically truncated due to its width. +WINDOW defaults to the selected window. +Return nil if WINDOW is not a partial-width window (regardless of the value of `truncate-lines'). Otherwise, consult the value of `truncate-partial-width-windows' - for the buffer shown in WINDOW. -If WINDOW is nil, use the selected window." + for the buffer shown in WINDOW." (unless window (setq window (selected-window))) (unless (window-full-width-p window)