Subject: bug#10144: 24.0.91; `pop-up-frames' with dedicated windows is broken: `C-x C-f'

From: "Drew Adams" <[email-masked]>
Date: Mon, 28 Nov 2011 07:30:24 -0800

> > emacs -Q
> > C-x d some-dir
> > M-x set-variable pop-up-frames t
> > (set-window-dedicated-p (selected-window) t)
> > RET ; on some file name
> 
> > In Emacs 24 you get the error "Cannot switch...".
> 
> In Emacs-24 and Emacs-21 and Emacs-22.

No, only in Emacs 24 - but with my setup only, it seems (see below).

With the recipe above, you are right.  But with my setup, the original problem I
reported occurs only for (recent) Emacs 24.

Actually, with only the original recipe I gave, in the older releases *info* is
not even shown as a special-display buffer, and its window is not dedicated
(why?).  So in that regard, that original recipe was also not complete for older
versions.

With the original recipe, why *info* is not dedicated, in spite of the settings
(non-nil `pop-up-frames', `special-display-regexps' matching *info*, *info*
removed from `same-window-regexps'), I don't know and don't have time to check
now.

But in my setup that is what I see in all Emacs versions except 24: the file is
simply visited in a separate frame.  See below for the likely reason.

> > In previous releases the chosen file is visited in another frame.
> 
> You mean in Emacs-23 only.

No, I meant in all Emacs versions prior to 24 (with my setup).
And in 24 too, back when Martin's code was used.

> > Dedicated windows should be primarily about making Emacs 
> > use a different window/frame.  They should not just prevent
> > showing the file altogether, by raising an error.
> 
> If the user uses find-file rather than find-file-other-window, she
> presumably knows what she's doing.

That's why (one reason) the user set `pop-up-frames' to non-nil. ;-)
`find-file' in a non-dedicated window should use the same window.  In a
dedicated window it should (IMO) visit the file somewhere else (another window
or, if non-nil `pop-up-frames' then another frame).

What's the point of such an error message when Emacs could DTRT and show the
user the buffer s?he asked to see?

And as Martin pointed out, it is not only about C-x C-f, but `find-file' in
general.  More generally than that, it is about dedicated windows: trying to
reuse a dedicated window should (IMO) just cause another window to be used.

What's the point of such an error message?  I can imagine that there might be
special cases, e.g. the minibuffer, that would be problematic, but in general
the buffer should (IMO) be shown, but in another window/frame.  And I've had
exactly that behavior for decades, with no special-casing and no problems.

---

OK, I haven't looked in detail, but I did take a quick look, and it's likely
that my setup works the way it does because, for versions < 24, I redefine
`switch-to-buffer' so that if the current window is dedicated then another
window is used.  In Emacs 24 I haven't done that (didn't need to, so far).  Here
is the code:

(if (< emacs-major-version 24)

    ;; REPLACES ORIGINAL (built-in):
    ;; 1) Use `read-buffer' in interactive spec.
    ;; 2) If current window is dedicated, then use another window.
    ;;    NOTE: Emacs versions >= 19.34 signal error if dedicated window,
instead
    ;;          of using another one.  Don't know what the 19.28 version did.
    ;; 3) Resize frame to fit sole window if `autofit-frames-flag'
    ;;    (unless BUFFER is already the `current-buffer').
    (defun switch-to-buffer (buffer &optional norecord)
      "Select buffer BUFFER in current window, unless the window is dedicated.
If current window is dedicated (`window-dedicated-p'), then another window
is used.

BUFFER may be a buffer, a string (a buffer name), or nil.  If BUFFER
is a string that does not identify an existing buffer, then a new
buffer with that name is created.  If BUFFER is nil, then function
`other-buffer' is used to choose a buffer.

Optional second arg NORECORD non-nil means do not put BUFFER at the
front of the list of recently selected buffers.

The buffer switched to is returned.

*WARNING*: This is NOT the way to work on another buffer temporarily
within a Lisp program!  Use `set-buffer' instead, to avoid messing
with correspondences between windows and buffers.

Resize frame to fit sole window if `autofit-frames-flag'
\(unless BUFFER is already the `current-buffer')."
      (interactive
       (list (read-buffer "Switch to buffer: "
                          (if (fboundp 'another-buffer) ; In `misc-fns.el'.
                              (another-buffer nil t)
                            (other-buffer (current-buffer))))))
      ;; If string arg, convert to a buffer.  If nil, use `other-buffer'.
      (setq buffer  (if buffer (get-buffer-create buffer) (other-buffer)))
      (let ((orig-buf  (current-buffer)))
        (prog1 (if (window-dedicated-p (selected-window))
                   (switch-to-buffer-other-window buffer)
                 (old-switch-to-buffer buffer norecord))
          (and (one-window-p t)
               (not (eq buffer orig-buf)) ; Don't resize if same buffer.
               autofit-frames-flag
               (fit-frame)))))

  ;; REPLACES ORIGINAL (built-in):
  ;;
  ;; Resize frame to fit sole window if `autofit-frames-flag'
  ;; (unless BUFFER is already the `current-buffer').
  ;;
  (defun switch-to-buffer (buffer-or-name &optional norecord force-same-window)
    "Switch to buffer BUFFER-OR-NAME in the selected window.
Return the buffer switched to.

If called interactively, prompt for the buffer name using the
minibuffer.  The variable `confirm-nonexistent-file-or-buffer'
determines whether to request confirmation before creating a new
buffer.

BUFFER-OR-NAME may be a buffer, a string (a buffer name), or nil.
If BUFFER-OR-NAME is a string that does not identify an existing
buffer, create a buffer with that name.  If BUFFER-OR-NAME is nil,
switch to the buffer returned by `other-buffer'.

Optional argument NORECORD non-nil means do not put the buffer
specified by BUFFER-OR-NAME at the front of the buffer list and
do not make the window displaying it the most recently selected
one.

If FORCE-SAME-WINDOW is non-nil, BUFFER-OR-NAME must be displayed in
the selected window.  Signal an error if that is impossible (e.g. if
the selected window is minibuffer-only).  If nil, BUFFER-OR-NAME may
be displayed in another window."
    (interactive
     (list (read-buffer-to-switch "Switch to buffer: ") nil 'force-same-window))
    (let ((orig-buf    (current-buffer))
          (switch-buf  (old-switch-to-buffer
                        buffer-or-name norecord force-same-window)))
      (when (and (one-window-p t)
                 (not (eq switch-buf orig-buf)) ; Don't resize if same buffer.
                 autofit-frames-flag
                 (fit-frame)))
      switch-buf)))

---

For my part, I will try tweaking my setup to make it work with Emacs 24.  But
you might also want to consider doing what I do.  I think users would prefer to
have the buffer shown to them than to just see an error message about window
dedication.  I can at least testify that that behavior has been natural and
helpful to me, for decades now.