emacs special-display-function: prefer-other-visible-frame

Emacs’s support for special buffer frames, specifically the special-display-function variable and friends, let you customize the way Emacs chooses a window and buffer for new frames that are displayed via display-buffer.

Here’s Elisp code you can use as special-display-function that prefers existing windows and frames, as opposed to splitting the current window and frame, but does not select (ie switch to) the window where the new buffer is displayed.

Also see display-buffer-reuse-frames.

(defvar non-special-display-buffer-names
  '("*Completions*" " *minibuf-isearch*")
  "Buffers that should *not* be handled by `prefer-other-visible-frame'.
   (I'd rather just exclude them in `special-display-regexps', but elisp regex
   doesn't support negative lookahead, ie (?!...), so i can't.)")

(defvar special-display-switch-to-regexp
  "^.TAGS: "
  "Regexp for buffer names that should be selected (switched to) when
   handled by `prefer-other-visible-frame'.")

(defun prefer-other-visible-frame (buffer &optional buffer-data)
  "If other frames are visible, display the buffer in one of them.
Otherwise, display the buffer in this frame in another window. If
there's only one window, split to create another. Also hide the
buffer in all windows other than the window it gets displayed in.

Intended to be set as `special-display-function'.

Ignores buffers in `non-special-display-buffer-names'.

I can *almost* do this with frame parameters alone, e.g. visibility, except for
forcing it to use a different frame if possible. (same-frame . t) lets you force
it to use the *same* frame, but (same-frame . nil) doesn't force a different
one.

http://www.gnu.org/software/emacs/manual/html_node/emacs/Special-Buffer-Frames.html
http://www.gnu.org/software/emacs/manual/html_node/emacs/Frame-Parameters.html

I wish i could just use `switch-to-buffer-other-window' here, but it calls
`display-buffer', which ends up calling this again. Sigh."
   ;; i'd like to use `other-frame', but it raises the frame :/
  (let ((window
    (cond
     ((member (buffer-name buffer) non-special-display-buffer-names) nil)
     ;; is the buffer already displayed in another visible window?
     ((get-buffer-window buffer 'visible))
     ;; find another window
     (t (let* ((existing (next-window (selected-window) 'never-minibuf 'visible))
               (new (if (and existing (not (eq existing (selected-window))))
                      ; found another existing one
                      existing
                      ; couldn't find an existing one; splitting the current one
                      (split-window))))
          (set-window-buffer new buffer)
          new)))))
    (when (string-match special-display-switch-to-regexp (buffer-name buffer))
      (select-frame (window-frame window))
      (select-window window))
    window))

(when (not (featurep 'xemacs))
  (custom-set-variables
   '(special-display-function 'prefer-other-visible-frame)
   '(special-display-regexps '(".*"))  ; modulo non-special-display-buffer-names
   '(special-display-buffer-names nil)))

Also see:

Leave a Reply

Your email address will not be published. Required fields are marked *