From: Chong Yidong Date: Fri, 2 Sep 2011 02:23:43 +0000 (-0400) Subject: Split default-buffer-default into separate display action components. X-Git-Tag: emacs-pretest-24.0.90~104^2~152^2~9 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=89894cd8be3e7d8f77474c9146ccad75cac07ddf;p=emacs.git Split default-buffer-default into separate display action components. * lisp/window.el (display-buffer-alist): Doc fix. (display-buffer): Add docstring. Don't treat display-buffer-default specially. (display-buffer-reuse-selected-window) (display-buffer-same-window, display-buffer-maybe-same-window) (display-buffer-reuse-window, display-buffer-pop-up-frame) (display-buffer-pop-up-window) (display-buffer-reuse-or-pop-window) (display-buffer-use-some-window): New functions. (display-buffer-default-action): Use them. (display-buffer-default): Deleted. (pop-to-buffer-1): Fix choice of actions. --- diff --git a/lisp/ChangeLog b/lisp/ChangeLog index a7d2083f4b1..7ea1dffe1c2 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,18 @@ +2011-09-02 Chong Yidong + + * window.el (display-buffer-alist): Doc fix. + (display-buffer): Add docstring. Don't treat + display-buffer-default specially. + (display-buffer-reuse-selected-window) + (display-buffer-same-window, display-buffer-maybe-same-window) + (display-buffer-reuse-window, display-buffer-pop-up-frame) + (display-buffer-pop-up-window) + (display-buffer-reuse-or-pop-window) + (display-buffer-use-some-window): New functions. + (display-buffer-default-action): Use them. + (display-buffer-default): Deleted. + (pop-to-buffer-1): Fix choice of actions. + 2011-09-02 Stefan Monnier * minibuffer.el (completion--insert-strings): Don't get confused by diff --git a/lisp/window.el b/lisp/window.el index 277b1fd5c09..b33c46acf93 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4478,155 +4478,6 @@ Return WINDOW." The actual non-nil value of this variable will be copied to the `window-dedicated-p' flag.") -(defun display-buffer-default (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 -buffer. Return the window chosen to display BUFFER-OR-NAME or -nil if no such window is found. - -Optional argument NOT-THIS-WINDOW non-nil means display the -buffer in a window other than the selected one, even if it is -already displayed in the selected window. - -Optional argument FRAME specifies which frames to investigate -when the specified buffer is already displayed. If the buffer is -already displayed in some window on one of these frames simply -return that window. Possible values of FRAME are: - -`visible' - consider windows on all visible frames on the current -terminal. - -0 - consider windows on all visible or iconified frames on the -current terminal. - -t - consider windows on all frames. - -A specific frame - consider windows on that frame only. - -nil - consider windows on the selected frame \(actually the -last non-minibuffer frame\) only. If, however, either -`display-buffer-reuse-frames' or `pop-up-frames' is non-nil -\(non-nil and not graphic-only on a text-only terminal), -consider all visible or iconified frames on the current terminal." - (interactive "BDisplay buffer:\nP") - (let* ((can-use-selected-window - ;; The selected window is usable unless either NOT-THIS-WINDOW - ;; is non-nil, it is dedicated to its buffer, or it is the - ;; `minibuffer-window'. - (not (or not-this-window - (window-dedicated-p (selected-window)) - (window-minibuffer-p)))) - (buffer (if (bufferp buffer-or-name) - buffer-or-name - (get-buffer buffer-or-name))) - (name-of-buffer (buffer-name buffer)) - ;; On text-only terminals do not pop up a new frame when - ;; `pop-up-frames' equals graphic-only. - (use-pop-up-frames (if (eq pop-up-frames 'graphic-only) - (display-graphic-p) - pop-up-frames)) - ;; `frame-to-use' is the frame where to show `buffer' - either - ;; the selected frame or the last nonminibuffer frame. - (frame-to-use - (or (window--frame-usable-p (selected-frame)) - (window--frame-usable-p (last-nonminibuffer-frame)))) - ;; `window-to-use' is the window we use for showing `buffer'. - window-to-use popped-up-frame) - (cond - ((not (buffer-live-p buffer)) - (error "No such buffer %s" buffer)) - (display-buffer-function - ;; Let `display-buffer-function' do the job. - (funcall display-buffer-function buffer not-this-window)) - ((and (not not-this-window) - (eq (window-buffer (selected-window)) buffer)) - ;; The selected window already displays BUFFER and - ;; `not-this-window' is nil, so use it. - (display-buffer-record-window 'reuse-window (selected-window) buffer) - (window--display-buffer-1 (selected-window))) - ((and can-use-selected-window (same-window-p name-of-buffer)) - ;; If the buffer's name tells us to use the selected window do so. - (display-buffer-record-window 'reuse-window (selected-window) buffer) - (window--display-buffer-2 buffer (selected-window))) - ((let ((frames (or frame - (and (or use-pop-up-frames - display-buffer-reuse-frames - (not (last-nonminibuffer-frame))) - 0) - (last-nonminibuffer-frame)))) - (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. - (display-buffer-record-window 'reuse-window window-to-use buffer) - (window--display-buffer-1 window-to-use)) - ((and special-display-function - ;; `special-display-p' returns either t or a list of frame - ;; parameters to pass to `special-display-function'. - (let ((pars (special-display-p name-of-buffer))) - (when pars - (funcall special-display-function - buffer (if (listp pars) pars)))))) - ((or use-pop-up-frames (not frame-to-use)) - ;; We want or need a new frame. - (setq window-to-use - (frame-selected-window (funcall pop-up-frame-function))) - (display-buffer-record-window 'pop-up-frame window-to-use buffer) - (window--display-buffer-2 buffer window-to-use)) - ((and pop-up-windows - ;; Make a new window. - (or (not (frame-parameter frame-to-use 'unsplittable)) - ;; If the selected frame cannot be split look at - ;; `last-nonminibuffer-frame'. - (and (eq frame-to-use (selected-frame)) - (setq frame-to-use (last-nonminibuffer-frame)) - (window--frame-usable-p frame-to-use) - (not (frame-parameter frame-to-use 'unsplittable)))) - ;; Attempt to split largest or least recently used window. - (setq window-to-use - (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)))) - (display-buffer-record-window 'pop-up-window window-to-use buffer) - (window--display-buffer-2 buffer window-to-use))) - ((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) - (prog1 - (frame-selected-window (funcall pop-up-frame-function)) - (setq popped-up-frame t)))) - (when (window-live-p window-to-undedicate) - ;; Restore dedicated status of selected window. - (set-window-dedicated-p window-to-undedicate nil)))) - (display-buffer-record-window - (if popped-up-frame 'pop-up-frame 'reuse-window) window-to-use buffer) - (window--even-window-heights window-to-use) - (window--display-buffer-2 buffer window-to-use))))) - (defun window-normalize-buffer-to-display (buffer-or-name) "Normalize BUFFER-OR-NAME argument for buffer display functions. If BUFFER-OR-NAME is nil, return the curent buffer. Else, if a @@ -4641,24 +4492,52 @@ BUFFER-OR-NAME and return that buffer." (current-buffer))) (defvar display-buffer-alist nil - "Specifications of user preferences for `display-buffer'. -This is a list of elements of the form (CONDITION . ACTION) where -CONDITION is either a regexp matching buffer names, or a function -that takes a buffer and returns a boolean. ACTION is a list of -the form (FUNCTION . ALIST) where FUNCTION can be either a -function or a list of functions. Those functions will be called -with 2 arguments: the buffer to display and an ALIST built from -the various alists specified in the various ACTIONs. It should -either return the window used, or nil to fallback to the next -function.") - -(defvar display-buffer-default-action (list #'display-buffer-default) - "Default action to perform to display a buffer. -This is an ACTION just like in `display-buffer-alist'.") - -(defvar display-buffer-overriding-action '(nil) + "Alist of conditional actions for `display-buffer'. +This is a list of elements (CONDITION . ACTION), where: + + CONDITION is either a regexp matching buffer names, or a function + that takes a buffer and returns a boolean. + + ACTION is a cons cell (FUNCTION . ALIST), where FUNCTION is + either a function or a list of functions. Each such function + should accept 2 arguments: a buffer to display and an alist of + the same form as ALIST. It should return the window used, or + nil if it fails to display the window. See `display-buffer' + for more details. + +Usable action functions include: + `display-buffer-reuse-selected-window' + `display-buffer-same-window' + `display-buffer-maybe-same-window' + `display-buffer-reuse-window' + `display-buffer-pop-up-frame' + `display-buffer-pop-up-window' + `display-buffer-reuse-or-pop-window' + `display-buffer-use-some-window' + +The above functions recognize the following alist entries: + - `inhibit-same-window', if non-nil, prevents the same window + from being used for display. + - `reuse-frame' specifies the frames that can be searched for a + window displaying the buffer. Its values have the same + meaning as the ALL-FRAMES arg to `get-buffer-window-list'.") + +(defvar display-buffer-default-action + '((display-buffer-reuse-selected-window + display-buffer-maybe-same-window + display-buffer-reuse-or-pop-window + display-buffer-use-some-window + ;; If all else fails, pop up a new frame regardless of + ;; restrictions. + display-buffer-pop-up-frame)) + "List of default actions for `display-buffer'. +It should be a cons cell of the form (FUNCTION . ALIST), which +has the same meaning as in `display-buffer-alist'.") + +(defvar display-buffer-overriding-action nil "Overriding action to perform to display a buffer. -This is an ACTION just like in `display-buffer-alist'.") +If non-nil, it should be a cons cell (FUNCTION . ALIST), which +has the same meaning as in `display-buffer-alist'.") (defun display-buffer-assq-regexp (buffer-name alist) "Retrieve ALIST entry corresponding to BUFFER-NAME." @@ -4673,46 +4552,72 @@ This is an ACTION just like in `display-buffer-alist'.") (throw 'match (cdr entry))))))) (defun display-buffer (&optional buffer-or-name action frame) - "Display BUFFER in some window." + "Display BUFFER-OR-NAME in some window. +BUFFER-OR-NAME must be a buffer or the name of an existing +buffer. Return the window chosen for displaying BUFFER-OR-NAME, +or nil if no such window is found. + +Optional argument ACTION should have the form (FUNCTION . ALIST). +FUNCTION is either a function or a list of functions. Each such +function is called with 2 arguments: the buffer to display and an +alist. It should either display the buffer and return the +window, or return nil if it is unable to display the buffer. + +`display-buffer' constructs a list of action functions and an +action alist from `display-buffer-overriding-action', +`user-action', ACTION, and `display-buffer-default-action' (in +order). It calls each action function in turn, passing the +consolidated action alist as the second argument, until one of +the functions returns non-nil. + +ACTION can also have a non-nil and non-list value. This means to +display the buffer in a window other than the selected one, even +if it is already displayed in the selected window. If called +interactively with a prefix argument, ACTION is t. + +Optional argument FRAME specifies where to look for a window that +already displays the buffer. If nil, check only the selected +frame (actually the last non-minibuffer frame), except if +`display-buffer-reuse-frames' or `pop-up-frames' is non-nil +\(non-nil and not graphic-only on a text-only terminal), in which +case check all visible or iconified frames. Otherwise, FRAME can +be a specific frame, `visible' (all visible frames), 0 (all +frames on the current terminal), or t (all frames)." (interactive "BDisplay buffer:\nP") - (let* ((buffer (window-normalize-buffer-to-display buffer-or-name)) - (buffer-name (buffer-name buffer)) - (user-action - (display-buffer-assq-regexp buffer-name display-buffer-alist)) - (functions - (append - (list (car display-buffer-overriding-action)) - (list (car user-action)) - (and (listp action) (list (car action))) - (list (car display-buffer-default-action)))) - (specifiers (append (cdr display-buffer-overriding-action) - (cdr user-action) - (and (listp action) (cdr action)) - (cdr display-buffer-default-action))) - function window) - (while (and functions (not window)) - (setq function (car functions)) - (cond - ((listp function) - (while (and function (not window)) - (cond - ((eq (car function) 'display-buffer-default) - (setq window - (display-buffer-default - buffer (memq action '(t other-window)) frame))) - ((functionp (car function)) - (setq window (funcall (car function) buffer specifiers)))) - (setq function (cdr function)))) - ((eq function 'display-buffer-default) - (setq window - (display-buffer-default - buffer (memq action '(t other-window)) frame))) - ((functionp function) - (setq window - (funcall function buffer specifiers)))) - (setq functions (cdr functions))) - - window)) + (let ((buffer (window-normalize-buffer-to-display buffer-or-name)) + ;; Handle the old form of the first argument. + (inhibit-same-window (and action (not (listp action))))) + (unless (listp action) (setq action nil)) + (if display-buffer-function + ;; If `display-buffer-function' is defined, let it do the job. + (funcall display-buffer-function buffer inhibit-same-window) + ;; Otherwise, use the defined actions. + (let* ((user-action + (display-buffer-assq-regexp (buffer-name buffer) + display-buffer-alist)) + ;; Extra actions from the arguments to this function: + (extra-action + (cons nil (append (if inhibit-same-window + '((inhibit-same-window . t))) + (if frame + `((reuse-frame . ,frame)))))) + ;; Construct action function list and action alist. + (actions (list display-buffer-overriding-action + user-action action extra-action + display-buffer-default-action)) + (functions (apply 'append + (mapcar (lambda (x) + (setq x (car x)) + (if (listp x) x (list x))) + actions))) + (alist (apply 'append (mapcar 'cdr actions))) + window) + (unless (buffer-live-p buffer) + (error "Invalid buffer")) + (while (and functions (not window)) + (setq window (funcall (car functions) buffer alist) + functions (cdr functions))) + window)))) (defun display-buffer-other-frame (buffer) "Display buffer BUFFER in another frame. @@ -4737,24 +4642,182 @@ its documentation for additional customization information." ;;(make-frame-visible (window-frame old-window)) )) -;;; Functions for use via `display-buffer-alist'. +;;; `display-buffer' action functions: -(defun display-buffer-same-window (buffer alist) - "Display BUFFER in the selected window, and return the window. -If BUFFER cannot be displayed in the selected window (usually -because it is dedicated to another buffer), return nil." - (let ((norecord (cadr (assq 'norecord alist)))) - (cond - ((eq buffer (window-buffer)) - (selected-window)) - ((not (or (window-minibuffer-p) (window-dedicated-p))) - (set-window-buffer nil buffer) - (selected-window))))) +(defun display-buffer-reuse-selected-window (buffer alist) + "Try to display BUFFER in the selected window if it is already there. +If this succeeds, return the selected window. -(defun display-buffer-other-window (buffer alist) - "Display BUFFER in another window, and return BUFFER. -If BUFFER cannot be displayed in another window, just return nil." - (display-buffer-default buffer t)) +This fails if BUFFER is not displayed in the selected window, or +if ALIST has a non-nil `inhibit-same-window' entry. In that +case, return nil." + (when (and (not (cdr (assq 'inhibit-same-window alist))) + (eq buffer (window-buffer))) + (display-buffer-record-window 'reuse-window (selected-window) buffer) + (window--display-buffer-1 (selected-window)))) + +(defun display-buffer-same-window (buffer alist) + "Try to display BUFFER in the selected window. +If this succeeds, return the selected window. + +This fails if the selected window is a minibuffer window or is +dedicated to another buffer, or if ALIST has a non-nil +`inhibit-same-window' entry. In that case, return nil." + (unless (or (cdr (assq 'inhibit-same-window alist)) + (window-minibuffer-p) + (window-dedicated-p)) + (display-buffer-record-window 'reuse-window (selected-window) buffer) + (window--display-buffer-2 buffer (selected-window)))) + +(defun display-buffer-maybe-same-window (buffer alist) + "Try to display BUFFER in the selected window. +This acts like `display-buffer-same-window', except that it also +fails if `same-window-p' returns nil for this buffer." + (and (same-window-p (buffer-name buffer)) + (display-buffer-same-window buffer alist))) + +(defun display-buffer-reuse-window (buffer alist) + "Return a window that is already displaying BUFFER. +If no usable window is found, return nil. + +If ALIST has a non-nil `inhibit-same-window' entry, the same +window cannot be reused. + +If ALIST contains a `reuse-frame' entry, that determines the +frames to check for a window displaying the buffer. If the entry +is omitted or the value is nil, check only this frame. The value +can also be a specific frame, `visible' (all visible frames), +0 (all frames on the current terminal), or t (all frames)." + (let* ((can-use-selected-window + (not (cdr (assq 'inhibit-same-window alist)))) + (frames (or (cdr (assq 'reuse-frame alist)) + (last-nonminibuffer-frame))) + (window (catch 'found + (dolist (window (get-buffer-window-list + buffer 'nomini frames)) + (when (or can-use-selected-window + (not (eq (selected-window) window))) + (throw 'found window)))))) + (when window + (display-buffer-record-window 'reuse-window window buffer) + (window--display-buffer-1 window)))) + +(defun display-buffer-pop-up-frame (buffer alist) + "Display BUFFER in a new frame. +This works by calling `pop-up-frame-function'. If sucessful, +return the window on the new frame; otherwise return nil." + (let ((fun pop-up-frame-function) + frame window) + (when (and fun + (setq frame (funcall fun)) + (setq window (frame-selected-window frame))) + (display-buffer-record-window 'pop-up-frame window buffer) + (window--display-buffer-2 buffer window)))) + +(defun display-buffer-pop-up-window (buffer alist) + "Display BUFFER by popping up a new window. +The new window is created on the selected frame, or in +`last-nonminibuffer-frame' if no windows can be created there. +If sucessful, return the new window; otherwise return nil." + (let ((frame (or (window--frame-usable-p (selected-frame)) + (window--frame-usable-p (last-nonminibuffer-frame)))) + window) + (when (and (or (not (frame-parameter frame 'unsplittable)) + ;; If the selected frame cannot be split, look at + ;; `last-nonminibuffer-frame'. + (and (eq frame (selected-frame)) + (setq frame (last-nonminibuffer-frame)) + (window--frame-usable-p frame) + (not (frame-parameter frame 'unsplittable)))) + ;; Attempt to split largest or least recently used window. + (setq window (or (window--try-to-split-window + (get-largest-window frame t)) + (window--try-to-split-window + (get-lru-window frame t))))) + (display-buffer-record-window 'pop-up-window window buffer) + (window--display-buffer-2 buffer window)))) + +;; This display action function groups together some lower-level ones: +(defun display-buffer-reuse-or-pop-window (buffer alist) + "Display BUFFER in some window other than the selected one. +This attempts to call the following functions (in order): + - `display-buffer-reuse-window', ensuring that it checks all + frames on this terminal if `display-buffer-reuse-frames' or + `pop-up-frames' is non-nil. + - `special-display-function', if it is available. + - `display-buffer-pop-up-frame', if specified by `pop-up-frames'. + - `display-buffer-pop-up-window', if specified by `pop-up-windows'. + +If BUFFER is sucessfully display, return its window; otherwise +return nil." + (let ((use-pop-up-frames (if (eq pop-up-frames 'graphic-only) + (display-graphic-p) + pop-up-frames))) + (or (display-buffer-reuse-window + buffer + ;; If `display-buffer-reuse-frames' or `pop-up-frames' is + ;; non-nil, check all frames on this terminal. + (if (and (null (cdr (assq 'reuse-frame alist))) + (or use-pop-up-frames display-buffer-reuse-frames)) + (cons '(reuse-frame . 0) alist) + alist)) + ;; Try with `special-display-function': + (and special-display-function + ;; `special-display-p' returns either t or a list of frame + ;; parameters to pass to `special-display-function'. + (let ((pars (special-display-p (buffer-name buffer)))) + (when pars + (funcall special-display-function + buffer (if (listp pars) pars))))) + (and use-pop-up-frames + (display-buffer-pop-up-frame buffer alist)) + (and pop-up-windows + (display-buffer-pop-up-window buffer alist))))) + +(defun display-buffer-use-some-window (buffer alist) + "Display BUFFER in an existing window. +Search for a usable window, set that window to the buffer, and +return the window. If no suitable window is found, return nil." + (let* ((not-this-window (cdr (assq 'inhibit-same-window alist))) + (window-to-undedicate + ;; When NOT-THIS-WINDOW is non-nil, temporarily dedicate the + ;; selected window to its buffer, to prevent any of the + ;; `get-' routines below from choosing it. (Bug#1415) + (and not-this-window (not (window-dedicated-p)) + (set-window-dedicated-p (selected-window) t) + (selected-window))) + (frame (or (window--frame-usable-p (selected-frame)) + (window--frame-usable-p (last-nonminibuffer-frame)))) + (use-pop-up-frames (if (eq pop-up-frames 'graphic-only) + (display-graphic-p) + pop-up-frames)) + window popped-up-frame) + (unwind-protect + (setq window + ;; Reuse an existing window. + (or (get-lru-window frame) + (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) + (and use-pop-up-frames + (prog1 + (frame-selected-window (funcall pop-up-frame-function)) + (setq popped-up-frame t))))) + (when (window-live-p window-to-undedicate) + ;; Restore dedicated status of selected window. + (set-window-dedicated-p window-to-undedicate nil))) + (when window + (display-buffer-record-window + (if popped-up-frame 'pop-up-frame 'reuse-window) window buffer) + (window--even-window-heights window) + (window--display-buffer-2 buffer window)))) ;;; Display + selection commands: @@ -4808,12 +4871,11 @@ for `pop-to-buffer'." ;; Based on the WINDOW-CHOICE argument, choose an action ;; argument to pass to `display-buffer'. (cond - ((null window-choice) - '((display-buffer-other-window display-buffer-same-window))) ((eq window-choice 'same-window) - '((display-buffer-same-window display-buffer-other-window))) - (t - '((display-buffer-other-window))))) + '((display-buffer-reuse-selected-window + display-buffer-same-window))) + (window-choice + '(nil (inhibit-same-window . t))))) (window (display-buffer (current-buffer) action)) (frame (window-frame window))) (if (eq frame old-frame)