Subject: Re: bug#68081: 30.0.50; derived-mode and display-buffer-alist

From: martin rudalics <[email-masked]>
Date: Thu, 4 Jan 2024 11:21:17 +0100

>> 'info' initially used 'switch-to-buffer'
 >>
 >>       (if (get-buffer "*info*")
 >> 	(switch-to-buffer "*info*")
 >>         (Info-directory))))
 >>
 >> Later it called 'pop-to-buffer' as
 >>
 >>       (if (get-buffer "*info*")
 >> 	(pop-to-buffer "*info*")
 >>         (Info-directory))))
 >>
 >> The breakage occurred when it started to call
 >>
 >>     (pop-to-buffer "*info*")
 >>
 >> without checking whether that buffer exists.  It sometimes backfires to
 >> use a feature meant for interactive use (like 'pop-to-buffer' creating
 >> its buffer autonomously) in non-interactive calls.  Sometimes it happens
 >> decades after that feature was misused.
 >
 > Do other places that are affected by the same change do the same
 > mistake of unconditionally calling pop-to-buffer?

Maybe my formulation was not clear.  Basically, all calls of 'info'
without first argument are affected by the change.  But note that the
"callers" do not call 'pop-to-buffer' - 'info' does that.  And the
problem became virulent only when 'buffer-match-p' started to ask for
the buffer's derived mode.  With other words: Emacs versions before that
change were prepared for the eventual use of 'buffer-match-p'.  Later
versions were not.

The real crux is that non-interactively, 'info' displays the buffer at
all.  This considerably confuses programmers.  Take this (arbitrarily
taken) snippet from 'prolog-build-info-alist' and note how the author
tries to override the buffer display behavior of 'info' with the help of
two window excursions.

           (save-excursion
             (save-window-excursion
               ;; select any window but the minibuffer (as we cannot switch
               ;; buffers in minibuffer window.
               ;; I am not sure this is the right/best way
               (if (active-minibuffer-window)  ; nil if none active
                   (select-window (next-window)))
               ;; Do this after going away from minibuffer window
               (save-window-excursion
                 (info))
               (Info-goto-node prolog-info-predicate-index)

Or a similar snippet from 'cperl-info-buffer':

       (save-window-excursion
	;; Get Info running
	(require 'info)
	(cond (oldbuf
	       (set-buffer oldbuf)
	       (rename-buffer "*info-perl-tmp*")))
	(save-window-excursion
	  (info))
	(Info-find-node cperl-info-page (if type "perlvar" "perlfunc"))

Now if a user does

(customize-set-variable
  'display-buffer-alist
  '(("\\*info\\*" display-buffer-pop-up-frame)))

to display 'info' in a separate frame, restoring the previous window
configuration in these cases will not restore anything.  All these
usually "work" by virtue of one fact: That 'display-buffer' by default
reuses a window that shows the info buffer already and that the node
eventually found will be displayed in that window regardless of which
frame it is on.  All this is fragile though and will fail as soon as a
users starts to more thoroughly customize 'display-buffer-alist'.

The idea that info inherently works on displayed buffers only is also a
problem for info itself: 'Info-next' does

   (save-window-excursion
     (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
     (Info-goto-node (Info-extract-pointer "next"))))

If you call it with the info window not selected, this first displays
*info* in the selected window and goes to the next node there.  Then it
shows the original buffer in the selected window again in the hope that
the effect shows up in any other window that displays *info*.  I have no
idea why info does not just work on the info buffer here as also Chong's
change log entry

     * info.el (Info-next, Info-prev, Info-up): Select info buffer, in
             case the user clicks on the link while another window is selected.

would indicate.

martin