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

From: martin rudalics <[email-masked]>
Date: Wed, 3 Jan 2024 11:35:25 +0100

Attached find a patch for info.el on master that should handle all cases
I found in a fairly sane way.  Please test it with the info commands you
know of.

I've not eliminated things like

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

While these will hardly DTRT with multiple frames and occasionally
produce some flickering here, they allow to navigate info buffers that
are not displayed (however useful that is).

As for compilation modes, please provide an example of how they
misbehave.

Thanks, martin

diff --git a/lisp/info.el b/lisp/info.el
index 51e9eb72edf..6f69af99958 100644
--- a/lisp/info.el
+++ b/lisp/info.el
@@ -732,8 +732,53 @@ info-other-window
 		    (read-file-name "Info file name: " nil nil t))
 		(if (numberp current-prefix-arg)
 		    (format "*info*<%s>" current-prefix-arg))))
-  (info-setup file-or-node
-	      (switch-to-buffer-other-window (or buffer "*info*"))))
+  (info-pop-to-buffer file-or-node buffer t))
+
+(defun info-pop-to-buffer (&optional file-or-node buffer-or-name other-window)
+  "Put Info node FILE-OR-NODE in specified buffer and display it.
+Optional argument FILE-OR-NODE is as for `info'.
+
+If the optional argument BUFFER-OR-NAME is a buffer, use that
+buffer.  If it is a string, use that string as the name of the
+buffer, creating it if it does not exist.  Otherwise, use a
+buffer with the name `*info*', creating it if it does not exist.
+
+Optional argument OTHER-WINDOW nil means to prefer the selected
+window.  OTHER-WINDOW non-nil means to prefer another window.
+Select the window used, if it has been made."
+  (let ((buffer (cond
+		 ((bufferp buffer-or-name)
+		  buffer-or-name)
+		 ((stringp buffer-or-name)
+		  (get-buffer-create buffer-or-name))
+		 (t
+		  (get-buffer-create "*info*")))))
+    (with-current-buffer buffer
+      (unless (derived-mode-p 'Info-mode)
+	(Info-mode)))
+
+    (let* ((window
+	    (display-buffer buffer
+			    (if other-window
+				'(nil (inhibit-same-window . t))
+			      '(display-buffer-same-window)))))
+      (with-current-buffer buffer
+	(if file-or-node
+	    ;; If argument already contains parentheses, don't add another set
+	    ;; since the argument will then be parsed improperly.  This also
+	    ;; has the added benefit of allowing node names to be included
+	    ;; following the parenthesized filename.
+	    (Info-goto-node
+	     (if (and (stringp file-or-node) (string-match "(.*)" file-or-node))
+		 file-or-node
+               (concat "(" file-or-node ")")))
+	  (if (and (zerop (buffer-size))
+		   (null Info-history))
+	      ;; If we just created the Info buffer, go to the directory.
+	      (Info-directory))))
+
+      (when window
+	(select-window window)))))
 
 ;;;###autoload (put 'info 'info-file (purecopy "emacs"))
 ;;;###autoload
@@ -768,8 +813,8 @@ info
     ;; of names that might have been wrapped (in emails, etc.).
     (setq file-or-node
           (string-replace "\n" " " file-or-node)))
-  (info-setup file-or-node
-	      (pop-to-buffer-same-window (or buffer "*info*"))))
+
+  (info-pop-to-buffer file-or-node buffer))
 
 (defun info-setup (file-or-node buffer)
   "Display Info node FILE-OR-NODE in BUFFER."
@@ -789,6 +834,8 @@ info-setup
 	;; If we just created the Info buffer, go to the directory.
 	(Info-directory))))
 
+(make-obsolete 'info-setup "use `info-pop-to-buffer' instead" "30.1")
+
 ;;;###autoload
 (defun info-emacs-manual ()
   "Display the Emacs manual in Info mode."
@@ -927,7 +974,7 @@ Info-find-node
   (setq nodename (info--node-canonicalize-whitespace nodename))
   (setq filename (Info-find-file filename noerror))
   ;; Go into Info buffer.
-  (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
+  (or (derived-mode-p 'Info-mode) (info-pop-to-buffer filename))
   ;; Record the node we are leaving, if we were in one.
   (and (not no-going-back)
        Info-current-file
@@ -957,7 +1004,7 @@ Info-revert-find-node
   "Go to an Info node FILENAME and NODENAME, re-reading disk contents.
 When *info* is already displaying FILENAME and NODENAME, the window position
 is preserved, if possible."
-  (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
+  (or (derived-mode-p 'Info-mode) (info-pop-to-buffer filename))
   (let ((old-filename Info-current-file)
 	(old-nodename Info-current-node)
 	(window-selected (eq (selected-window) (get-buffer-window)))
@@ -2290,7 +2337,7 @@ Info-next
   (interactive nil Info-mode)
   ;; In case another window is currently selected
   (save-window-excursion
-    (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
+    (or (derived-mode-p 'Info-mode) (info-pop-to-buffer))
     (Info-goto-node (Info-extract-pointer "next"))))
 
 (defun Info-prev ()
@@ -2299,7 +2346,7 @@ Info-prev
   (interactive nil Info-mode)
   ;; In case another window is currently selected
   (save-window-excursion
-    (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
+    (or (derived-mode-p 'Info-mode) (info-pop-to-buffer))
     (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous"))))
 
 (defun Info-up (&optional same-file)
@@ -2308,7 +2355,7 @@ Info-up
   (interactive nil Info-mode)
   ;; In case another window is currently selected
   (save-window-excursion
-    (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
+    (or (derived-mode-p 'Info-mode) (info-pop-to-buffer))
     (let ((old-node Info-current-node)
 	  (old-file Info-current-file)
 	  (node (Info-extract-pointer "up")) p)
@@ -5485,7 +5532,7 @@ info-display-manual
                 (raise-frame (window-frame window))
                 (select-frame-set-input-focus (window-frame window))
                 (select-window window))
-	    (switch-to-buffer found)))
+	    (info-pop-to-buffer nil found)))
       ;; The buffer doesn't exist; create it.
       (info-initialize)
       (info (Info-find-file manual)