unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Arthur Miller <arthur.miller@live.com>
To: Manuel Giraud <manuel@ledu-giraud.fr>
Cc: emacs-devel@gnu.org
Subject: Re: Control help- and Info-mode buffers from other buffers
Date: Tue, 30 May 2023 15:31:27 +0200	[thread overview]
Message-ID: <AM9PR09MB4977534E18EE7BFAC956ADB0964B9@AM9PR09MB4977.eurprd09.prod.outlook.com> (raw)
In-Reply-To: <87h6ruf09e.fsf@ledu-giraud.fr> (Manuel Giraud's message of "Tue,  30 May 2023 14:54:05 +0200")

[-- Attachment #1: Type: text/plain, Size: 4024 bytes --]

Manuel Giraud <manuel@ledu-giraud.fr> writes:

> Arthur Miller <arthur.miller@live.com> writes:
>
>> I would like to be able to control Info and Help buffers from other buffers, to
>> lessen switch between windows.
>
> Hi Arthur,
>
> I like this.  I'm often reading some documentation while testing things
> into the current buffer so I think I would be a great addition.  I'm
> testing your patch now (and will report any issue if needed).

Hi Manuel;

thank you very much for testing it, and for the kind words. I have reworked
somewhat the patch, because of some unnecessary warnings. I am attaching the
reworked version in this mail too (I have send it in another mail). Sorry for
the inconvenience, but if you test, I suggest testing with the later one. It is
very much the same, minus some re-furnituring for the code, some local variables
renaming and some doc string changes.

> As a potential future user, I have some comments/requests:
>
>      - Could it be extend to Man buffers?  I think it would even be more
>        useful than *Help* buffers which are short most of the time (or
>        maybe it is just me).

Probably, I don't use man a lot so I am not sure to be honest.

The technique can be extended to "remotely" control any buffer, but it works
well only with "unique" buffers (one of a kind), like Help buffer for
example. Even with *info* buffers it is a bit less clean since there can be few
of those. But technically it is possible (I have been doing this
programmatically too).

>      - Is it possible to have repeat mode for this?  So one could do
>        "C-h M-i SPC SPC SPC DEL" to browse some info pages.

I am not a repeat-mode user myself to be honest, so IDK, but I guess there
should be no problems. These are just commands that can be called very well via
M-x as any other command, so I guess it should work with repeat mode as well?

Also I suggest rebinding to M-i instead of C-h M-i for more pleasant experience
(or whichever key suits the best :)). I have chosen C-h M-i just to be on the
conservative side to spare GNU server maintainers from excessive electrical
bills, which may result every time we suggest rebinding a default key on this
list. M-i does something in Outline mode by default, likewise, M-h is bound to
mark-pragraph by default, so I have opted for C-h M-h.

>      - Could we have a "umbrella" keybinding that does this for some
>        common denominator functions the other "documentation" buffer
>        (be it *info*, *Help* or a man page)?

Basicly yes. The idea is:

1. switch to desired buffer
2. do whatever needed in that buffer
3. switch back to original buffer

I have been doing this for quite a while, and I have a little mode were I do
this programmatically as mentioned above. I have a little library < 100 sloc,
where I iterate through help/info mode-maps, check if the command starts with
help- or Info- prefix and attch an advice around the command where I do this
switching between windows and call original function in-between. If it does not,
I create a new symbol with the correct prefix, alias it to the old function, and
then advise the new symbol.

It worked very well with help-mode, but some commands in Info-mode are not
possible to advice that way, about a couple of those. Info-menu was one for
example.

But in Info-mode there are some nested char-tables in the keymap which turns out
to be quite slow to process programmatically. Also, even if it was fast, doing
that every time Emacs starts is unnecessary, it is probably better to do it once
and for all, so I have implemented it now manually. Similar can be implemented
for any command to make it "buffer specific", but as said, works well only in
cases where there is just one buffer at a time, so I doubt in generality of this
technique. I have actually tried to find some general way to do this
programmatically with any buffer, but thus far, I don't see any general way
that is both efficient and 100% failsafe.

> Best regards,

Best regards and thanks for testing!


[-- Attachment #2: 0001-Use-help-and-Info-mode-commands-from-any-buffer.patch --]
[-- Type: text/x-patch, Size: 82312 bytes --]

From 04334fe2a97d8f21f684fa2860f343b4a236a391 Mon Sep 17 00:00:00 2001
From: Arthur Miller <arthur.miller@live.com>
Date: Tue, 30 May 2023 14:09:42 +0200
Subject: [PATCH] Use help- and Info-mode commands from any buffer

Allow commands that act on help-mode and Info-mode to be called from other
buffers than just help and Info buffer.
* etc/NEWS: Mention the change.
* doc/emacs/help.texi: Update relevant commands mentioned in the text.
* lisp/help-mode.el (help-jump):
New symbol used for both command and global variable.
Command jumps to/from help window; var records 'jumped from'.
* lisp/help-mode.el (help-view-source):
* lisp/help-mode.el (help-goto-info):
* lisp/help-mode.el (help-go-back):
* lisp/help-mode.el (help-go-forward):
* lisp/help-mode.el (help-goto-next-page):
* lisp/help-mode.el (help-goto-previous-page):
* lisp/help-mode.el (help-goto-lispref-info):
* lisp/help-mode.el (help-customize):
Help-mode commands adapted to be called from any buffer.
* lisp/help-mode.el (help-quit-window):
* lisp/help-mode.el (help-revert-buffer):
* lisp/help-mode.el (help-describe-mode):
* lisp/help-mode.el (help-beginning-of-buffer):
* lisp/help-mode.el (help-end-of-buffer):
* lisp/help-mode.el (help-scroll-up-command):
* lisp/help-mode.el (help-scroll-down-command):
* lisp/help-mode.el (help-forward-button):
* lisp/help-mode.el (help-backward-button):
* lisp/help-mode.el (help-button-describe):
* lisp/help-mode.el (help-push-button):
New commands. Do what they wrapped counterparts without 'help-'
prefix do, but specifically in help buffer.
* lisp/help-mode.el (help-mode-map):
* lisp/help-mode.el (help-mode-menu):
Update bindings to reflect the new commands for previously
generic commands Bind help-jump to 'j' (previously unused).
* lisp/help-mode.el (help-mode-prefix-key):
New defcustom declaration. Prefix key for help-mode-map.
* lisp/info.el (Info-mode-prefix-key):
New defcustom declaration. Prefix key for Info-mode-map.
* lisp/info.el (Info-menu):
* lisp/info.el (Info-next):
* lisp/info.el (Info-prev):
* lisp/info.el (Info-up):
* lisp/info.el (Info-history-back):
* lisp/info.el (Info-history-forward):
* lisp/info.el (Info-directory):
* lisp/info.el (Info-toc):
* lisp/info.el (Info-nth-menu-item):
* lisp/info.el (Info-top-node):
* lisp/info.el (Info-final-node):
* lisp/info.el (Info-forward-node):
* lisp/info.el (Info-backward-node):
* lisp/info.el (Info-next-menu-item):
* lisp/info.el (Info-last-menu-item):
* lisp/info.el (Info-next-preorder):
* lisp/info.el (Info-last-preorder):
* lisp/info.el (Info-scroll-up):
* lisp/info.el (Info-scroll-down):
* lisp/info.el (Info-next-reference):
* lisp/info.el (Info-prev-reference):
* lisp/info.el (Info-index):
* lisp/info.el (Info-index-next):
* lisp/info.el (Info-virtual-next):
* lisp/info.el (Info-apropos):
* lisp/info.el (Info-finder):
* lisp/info.el (Info-summary):
* lisp/info.el (Info-copy-current-node-name):
Info-mode commands adapted to be called from any buffer.
* lisp/info.el (Info-describe-mode):
* lisp/info.el (Info-quit-window):
* lisp/info.el (Info-beginning-of-buffer):
* lisp/info.el (Info-end-of-buffer):
New commands. Do what they wrapped counterparts without 'Info-'
prefix do, but specifically in Info buffer.
* lisp/info.el (Info-jump):
New symbol used for both command and global variable.
Command jumps to/from Info window; var records 'jumped from'.
* lisp/info.el (Info-mode-map):
* lisp/info.el (Info-mode-menu):
Update bindings to reflect the new commands for previously
generic commands Bind Info-jump to 'j' (previously unused).
---
 doc/emacs/help.texi |   12 +-
 etc/NEWS            |   19 +
 lisp/help-mode.el   |  260 ++++++++--
 lisp/info.el        | 1156 +++++++++++++++++++++++++------------------
 4 files changed, 928 insertions(+), 519 deletions(-)

diff --git a/doc/emacs/help.texi b/doc/emacs/help.texi
index 945e12a05d2..b50905c3dc4 100644
--- a/doc/emacs/help.texi
+++ b/doc/emacs/help.texi
@@ -494,9 +494,9 @@ Help Mode
 @item @key{RET}
 Follow a cross reference at point (@code{help-follow}).
 @item @key{TAB}
-Move point forward to the next hyperlink (@code{forward-button}).
+Move point forward to the next hyperlink (@code{help-forward-button}).
 @item S-@key{TAB}
-Move point back to the previous hyperlink (@code{backward-button}).
+Move point back to the previous hyperlink (@code{help-backward-button}).
 @item mouse-1
 @itemx mouse-2
 Follow a hyperlink that you click on.
@@ -544,12 +544,12 @@ Help Mode
 (@code{help-go-forward}).
 
 @kindex TAB @r{(Help mode)}
-@findex forward-button
+@findex help-forward-button
 @kindex S-TAB @r{(Help mode)}
-@findex backward-button
+@findex help-backward-button
   To move between hyperlinks in a help buffer, use @key{TAB}
-(@code{forward-button}) to move forward to the next hyperlink and
-@kbd{S-@key{TAB}} (@code{backward-button}) to move back to the
+(@code{help-forward-button}) to move forward to the next hyperlink and
+@kbd{S-@key{TAB}} (@code{help-backward-button}) to move back to the
 previous hyperlink.  These commands act cyclically; for instance,
 typing @key{TAB} at the last hyperlink moves back to the first
 hyperlink.
diff --git a/etc/NEWS b/etc/NEWS
index 3c71e52fff4..fe241d604cb 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -30,6 +30,25 @@ applies, and please also update docstrings as needed.
 \f
 * Changes in Emacs 30.1
 
++++
+** New user option 'info-mode-prefix-key.
+Prefix key for `Info-mode-map, so it can be used from other buffer but
+just *info* buffer.
+
+** Commands acting on Info buffer can now be used from any buffer
+Similar to help-mode, keyboard-driven commands acting in Info-mode and
+bound in Info-mode-map can now be used from any buffer (acting on Info
+buffer).
++++
+** New user option 'help-mode-prefix-key'.
+The key is used to assign `help-mode-map' to a prefix, so it can be
+used as a prefix-key from other buffers but just *Help* buffer.
+
+** Commands acting on Help buffer can now be used from any buffer
+This means that all commands that normally required a user to switch
+to *Help* buffer before the invocation, can now be invoked from any
+buffer. With other words, you can now control Help buffer from any
+other buffer, which lessens amount of switching between buffers.
 ---
 ** New user option 'describe-bindings-outline-rules'.
 This user option controls outline visibility in the output buffer of
diff --git a/lisp/help-mode.el b/lisp/help-mode.el
index bf64d032b65..af41686e3fd 100644
--- a/lisp/help-mode.el
+++ b/lisp/help-mode.el
@@ -47,7 +47,22 @@ help-mode-map
   "s"             #'help-view-source
   "I"             #'help-goto-lispref-info
   "i"             #'help-goto-info
-  "c"             #'help-customize)
+  "j"             #'help-jump
+  "c"             #'help-customize
+  "g"             #'help-revert-buffer
+  "q"             #'help-quit-window
+  "<"             #'help-beginning-of-buffer
+  ">"             #'help-end-of-buffer
+  "h"             #'help-describe-mode
+  "?"             #'help-describe-mode
+  "DEL"           #'help-scroll-down-command
+  "SPC"           #'help-scroll-down-command
+  "S-SPC"         #'help-scroll-up-command
+  "RET"           #'help-push-button
+  "TAB"           #'help-forward-button
+  "C-M-i"         #'help-backward-button
+  "<backtab>"     #'help-backward-button
+  "ESC TAB"       #'help-backward-button)
 
 (easy-menu-define help-mode-menu help-mode-map
   "Menu for Help mode."
@@ -60,9 +75,9 @@ help-mode-menu
     ["Next Topic" help-go-forward
      :help "Go back to next topic in this help buffer"
      :active help-xref-forward-stack]
-    ["Move to Previous Button" backward-button
+    ["Move to Previous Button" help-backward-button
      :help "Move to the Previous Button in the help buffer"]
-    ["Move to Next Button" forward-button
+    ["Move to Next Button" help-forward-button
      :help "Move to the Next Button in the help buffer"]
     ["View Source" help-view-source
      :help "Go to the source file for the current help item"]
@@ -150,6 +165,22 @@ help-mode-hook
   "Hook run by `help-mode'."
   :type 'hook
   :group 'help)
+
+;; with a little help from Helm
+(defcustom help-mode-prefix-key "C-h M-h"
+  "`help-mode-map' prefix key for invocation from other buffers."
+  :version "30.1"
+  :type '(choice (string :tag "Key") (const :tag "no binding"))
+  :set (lambda (var key)
+         (when (and (boundp var) (symbol-value var))
+           (define-key (current-global-map)
+                       (read-kbd-macro (symbol-value var)) nil))
+         (when key
+           (define-key (current-global-map)
+                       (read-kbd-macro key)
+                       help-mode-map))
+         (set var key))
+  :group 'help)
 \f
 ;; Button types used by help
 
@@ -763,7 +794,127 @@ help-xref-on-pp
 
 ;;;###autoload
 (define-obsolete-function-alias 'help-xref-interned #'describe-symbol "25.1")
+\f
+;; commands from special-mode wrapped to work on help-mode only
+
+(defun help-quit-window ()
+  "As `quit-window' but works only on *Help* buffer."
+  (interactive)
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (quit-window nil help-window))))
+
+(defun help-describe-mode ()
+  "As `describe-mode' but for *Help* buffer only."
+  (interactive)
+  (let ((help-buffer (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window help-buffer))
+      (user-error "Help buffer is not currently visible."))
+    (describe-mode help-buffer)))
+
+(defun help-beginning-of-buffer ()
+  "As `help-beginning-of-buffer' but for *Help* buffer only."
+  (interactive)
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (goto-char (point-min)))))
+
+(defun help-end-of-buffer ()
+  "As `help-end-of-buffer' but for *Help* buffer only."
+  (interactive)
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (goto-char (point-max)))))
+\f
+;; from files.el, for completeness and to eliminate potential confusion
 
+(defun help-revert-buffer ()
+  "As `revert-buffer', but act on help buffer specifically."
+  (interactive)
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (call-interactively #'revert-buffer))))
+\f
+;; Commands from button.el wrapped to work on help-mode only
+
+(defun help-forward-button ()
+  (interactive)
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (forward-button 1))))
+
+(defun help-backward-button ()
+  (interactive)
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (backward-button 1))))
+
+(defun help-button-describe ()
+  (interactive)
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (button-describe))))
+
+(defun help-push-button ()
+  (interactive)
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (push-button))))
+\f
+;; Commands from window.el wrapped to work on help-mode only
+
+(defun help-scroll-up-command (&optional arg)
+  "As `scroll-up-command' but works only on *Help* buffer."
+  (interactive "^P")
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (scroll-up-command arg))))
+
+(defun help-scroll-down-command (&optional arg)
+  "As `scroll-down-command' but works only on *Help* buffer."
+  (interactive "^P")
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (scroll-down-command arg))))
+
+\f
+
+(defvar help-jump nil)
+(defun help-jump ()
+  "Jump to and from *Help* window."
+  (interactive)
+  (let ((help-window (get-buffer-window (help-buffer)))
+        (current-window (selected-window)))
+    (cond
+     ((eq help-window current-window)
+      (unless help-jump
+        (user-error "No previously selected window to jump to."))
+      (select-window help-jump))
+     (t
+      (unless (window-live-p help-window)
+        (user-error "Help buffer is not currently visible."))
+      (setq help-jump current-window)
+      (select-window help-window)))))
 \f
 ;; Navigation/hyperlinking with xrefs
 
@@ -810,25 +961,37 @@ help-xref-go-forward
 (defun help-go-back ()
   "Go back to previous topic in this help buffer."
   (interactive)
-  (if help-xref-stack
-      (help-xref-go-back (current-buffer))
-    (user-error "No previous help buffer")))
+  (let ((help-buffer (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window help-buffer))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer help-buffer
+      (if help-xref-stack
+          (help-xref-go-back (current-buffer))
+        (user-error "No previous help buffer")))))
 
 (defun help-go-forward ()
   "Go to the next topic in this help buffer."
   (interactive)
-  (if help-xref-forward-stack
-      (help-xref-go-forward (current-buffer))
-    (user-error "No next help buffer")))
+  (let ((help-buffer (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window help-buffer))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer help-buffer
+      (if help-xref-forward-stack
+          (help-xref-go-forward (current-buffer))
+        (user-error "No next help buffer")))))
 
 (defun help-goto-next-page ()
   "Go to the next page (if any) in the current buffer.
 The help buffers are divided into \"pages\" by the ^L character."
   (interactive nil help-mode)
-  (push-mark)
-  (forward-page)
-  (unless (eobp)
-    (forward-line 1)))
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (push-mark)
+      (forward-page)
+      (unless (eobp)
+        (forward-line 1)))))
 
 (defun help-goto-previous-page ()
   "Go to the previous page (if any) in the current buffer.
@@ -836,47 +999,68 @@ help-goto-previous-page
 
 The help buffers are divided into \"pages\" by the ^L character."
   (interactive nil help-mode)
-  (push-mark)
-  (backward-page (if (looking-back "\f\n" (- (point) 5)) 2 1))
-  (unless (bobp)
-    (forward-line 1)))
+  (let ((help-window (get-buffer-window (help-buffer))))
+    (unless (window-live-p help-window)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window help-window
+      (push-mark)
+      (backward-page (if (looking-back "\f\n" (- (point) 5)) 2 1))
+      (unless (bobp)
+        (forward-line 1)))))
 
 (defun help-view-source ()
   "View the source of the current help item."
   (interactive nil help-mode)
-  (unless (plist-get help-mode--current-data :file)
-    (error "Source file for the current help item is not defined"))
-  (help-function-def--button-function
-   (plist-get help-mode--current-data :symbol)
-   (plist-get help-mode--current-data :file)
-   (plist-get help-mode--current-data :type)))
+  (let ((help-buffer (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window help-buffer))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer help-buffer
+      (unless (plist-get help-mode--current-data :file)
+        (error "Source file for the current help item is not defined"))
+      (help-function-def--button-function
+       (plist-get help-mode--current-data :symbol)
+       (plist-get help-mode--current-data :file)
+       (plist-get help-mode--current-data :type)))))
 
 (defun help-goto-info ()
   "View the *info* node of the current help item."
   (interactive nil help-mode)
-  (unless help-mode--current-data
-    (error "No symbol to look up in the current buffer"))
-  (info-lookup-symbol (plist-get help-mode--current-data :symbol)
-                      'emacs-lisp-mode
-                      help-window-keep-selected))
+  (let ((help-buffer (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window help-buffer))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer help-buffer
+      (unless help-mode--current-data
+        (error "No symbol to look up in the current buffer"))
+      (with-selected-window (get-buffer-window help-buffer)
+        (info-lookup-symbol (plist-get help-mode--current-data :symbol)
+                            'emacs-lisp-mode
+                            help-window-keep-selected)))))
 
 (defun help-goto-lispref-info ()
   "View the Emacs Lisp manual *info* node of the current help item."
   (interactive nil help-mode)
-  (unless help-mode--current-data
-    (error "No symbol to look up in the current buffer"))
-  (info-lookup-symbol (plist-get help-mode--current-data :symbol)
-                      'emacs-lisp-only))
+  (let ((help-buffer (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window help-buffer))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer help-buffer
+      (unless help-mode--current-data
+        (error "No symbol to look up in the current buffer"))
+      (info-lookup-symbol (plist-get help-mode--current-data :symbol)
+                          'emacs-lisp-only))))
 
 (defun help-customize ()
   "Customize variable or face whose doc string is shown in the current buffer."
   (interactive nil help-mode)
-  (let ((sym (plist-get help-mode--current-data :symbol)))
-    (unless (or (boundp sym) (facep sym))
-      (user-error "No variable or face to customize"))
-    (cond
-     ((boundp sym) (customize-variable sym))
-     ((facep sym) (customize-face sym)))))
+  (let ((help-buffer (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window help-buffer))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer help-buffer
+      (let ((sym (plist-get help-mode--current-data :symbol)))
+        (unless (or (boundp sym) (facep sym))
+          (user-error "No variable or face to customize"))
+        (cond
+         ((boundp sym) (customize-variable sym))
+         ((facep sym) (customize-face sym)))))))
 
 (defun help-do-xref (_pos function args)
   "Call the help cross-reference function FUNCTION with args ARGS.
diff --git a/lisp/info.el b/lisp/info.el
index 035dff66e75..dcc6ed667d8 100644
--- a/lisp/info.el
+++ b/lisp/info.el
@@ -819,6 +819,22 @@ info-standalone
 	       (save-buffers-kill-emacs)))
     (info)))
 \f
+(defvar Info-jump nil)
+(defun Info-jump ()
+  "Jump to and from *info* window."
+  (interactive)
+  (let ((info-window (get-buffer-window "*info*"))
+        (current-window (selected-window)))
+    (cond
+     ((eq info-window (selected-window))
+      (unless Info-jump
+        (error "No previously selected window to jump to."))
+      (select-window Info-jump))
+     (t
+      (when (window-live-p info-window)
+        (setq Info-jump current-window)
+        (select-window info-window))))))
+\f
 ;; See if the accessible portion of the buffer begins with a node
 ;; delimiter, and the node header line which follows matches REGEXP.
 ;; Typically, this test will be followed by a loop that examines the
@@ -831,12 +847,12 @@ info-standalone
 ;; The return value is the value of point at the beginning of matching
 ;; REGEXP, if the function succeeds, nil otherwise.
 (defun Info-node-at-bob-matching (regexp)
-  (and (bobp)				; are we at beginning of buffer?
-       (looking-at "\^_")		; does it begin with node delimiter?
+  (and (bobp)                     ; are we at beginning of buffer?
+       (looking-at "\^_")         ; does it begin with node delimiter?
        (let (beg)
 	 (forward-line 1)
 	 (setq beg (point))
-	 (forward-line 1)		; does the line after delimiter match REGEXP?
+	 (forward-line 1) ; does the line after delimiter match REGEXP?
 	 (re-search-backward regexp beg t))))
 
 (defun Info-find-file (filename &optional noerror no-pop-to-dir)
@@ -2273,85 +2289,108 @@ Info-next
   "Go to the \"next\" node, staying on the same hierarchical level.
 This command doesn't descend into sub-nodes, like \\<Info-mode-map>\\[Info-forward-node] does."
   (interactive nil Info-mode)
-  ;; In case another window is currently selected
-  (save-window-excursion
-    (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
-    (Info-goto-node (Info-extract-pointer "next"))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    ;; In case another window is currently selected
+    (save-window-excursion
+      (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
+      (Info-goto-node (Info-extract-pointer "next")))))
 
 (defun Info-prev ()
   "Go to the \"previous\" node, staying on the same hierarchical level.
 This command doesn't go up to the parent node, like \\<Info-mode-map>\\[Info-backward-node] does."
   (interactive nil Info-mode)
-  ;; In case another window is currently selected
-  (save-window-excursion
-    (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
-    (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous"))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    ;; In case another window is currently selected
+    (save-window-excursion
+      (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
+      (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous"))
+      ;; for some reason my Emacs does not update correctly info buffer after
+      ;; going back to a previous node, reverting buffer fixes it
+      (revert-buffer t t t))))
 
 (defun Info-up (&optional same-file)
   "Go to the superior node of this node.
 If SAME-FILE is non-nil, do not move to a different Info file."
   (interactive nil Info-mode)
-  ;; In case another window is currently selected
-  (save-window-excursion
-    (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
-    (let ((old-node Info-current-node)
-	  (old-file Info-current-file)
-	  (node (Info-extract-pointer "up")) p)
-      (and same-file
-	   (string-match "^(" node)
-	   (error "Up node is in another Info file"))
-      (Info-goto-node node)
-      (setq p (point))
-      (goto-char (point-min))
-      (if (and (stringp old-file)
-	       (search-forward "\n* Menu:" nil t)
-	       (re-search-forward
-		(if (string-equal old-node "Top")
-		    (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")")
-		  (concat "\n\\* +\\(" (regexp-quote old-node)
-			  ":\\|[^:]+: +" (regexp-quote old-node) "\\)"))
-		nil t))
-	  (progn (beginning-of-line) (if (looking-at "^\\* ") (forward-char 2)))
-	(goto-char p)
-	(Info-restore-point Info-history))))
-  ;; If scroll-conservatively is non-zero and less than 101, display
-  ;; as much of the superior node above the target line as possible.
-  (when (< 0 scroll-conservatively 101)
-    (recenter)))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      ;; In case another window is currently selected
+      (save-window-excursion
+        (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*"))
+        (let ((old-node Info-current-node)
+              (old-file Info-current-file)
+              (node (Info-extract-pointer "up")) p)
+          (and same-file
+               (string-match "^(" node)
+               (error "Up node is in another Info file"))
+          (Info-goto-node node)
+          (setq p (point))
+          (goto-char (point-min))
+          (if (and (stringp old-file)
+                   (search-forward "\n* Menu:" nil t)
+                   (re-search-forward
+                    (if (string-equal old-node "Top")
+                        (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")")
+                      (concat "\n\\* +\\(" (regexp-quote old-node)
+                              ":\\|[^:]+: +" (regexp-quote old-node) "\\)"))
+                    nil t))
+              (progn (beginning-of-line) (if (looking-at "^\\* ") (forward-char 2)))
+            (goto-char p)
+            (Info-restore-point Info-history)))
+        ;; If scroll-conservatively is non-zero and less than 101, display
+        ;; as much of the superior node above the target line as possible.
+        ;; (when (< 0 scroll-conservatively 101) (recenter))
+        ;; for some reason, in my Emacs, this is the only obe that actually
+        ;; displays correctly, recentering leavs last line at the top
+        (revert-buffer t t t)))))
 
 (defun Info-history-back ()
   "Go back in the history to the last node visited."
   (interactive nil Info-mode)
-  (or Info-history
-      (user-error "This is the first Info node you looked at"))
-  (let ((history-forward
-	 (cons (list Info-current-file Info-current-node (point))
-	       Info-history-forward))
-	filename nodename opoint)
-    (setq filename (car (car Info-history)))
-    (setq nodename (car (cdr (car Info-history))))
-    (setq opoint (car (cdr (cdr (car Info-history)))))
-    (setq Info-history (cdr Info-history))
-    (Info-find-node filename nodename)
-    (setq Info-history (cdr Info-history))
-    (setq Info-history-forward history-forward)
-    (goto-char opoint)))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (or Info-history
+          (user-error "This is the first Info node you looked at"))
+      (let ((history-forward
+             (cons (list Info-current-file Info-current-node (point))
+                   Info-history-forward))
+            filename nodename opoint)
+        (setq filename (car (car Info-history)))
+        (setq nodename (car (cdr (car Info-history))))
+        (setq opoint (car (cdr (cdr (car Info-history)))))
+        (setq Info-history (cdr Info-history))
+        (Info-find-node filename nodename)
+        (setq Info-history (cdr Info-history))
+        (setq Info-history-forward history-forward)
+        (goto-char opoint)))))
 
 (defalias 'Info-last 'Info-history-back)
 
 (defun Info-history-forward ()
   "Go forward in the history of visited nodes."
   (interactive nil Info-mode)
-  (or Info-history-forward
-      (user-error "This is the last Info node you looked at"))
-  (let ((history-forward (cdr Info-history-forward))
-	filename nodename opoint)
-    (setq filename (car (car Info-history-forward)))
-    (setq nodename (car (cdr (car Info-history-forward))))
-    (setq opoint (car (cdr (cdr (car Info-history-forward)))))
-    (Info-find-node filename nodename)
-    (setq Info-history-forward history-forward)
-    (goto-char opoint)))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (or Info-history-forward
+          (user-error "This is the last Info node you looked at"))
+      (let ((history-forward (cdr Info-history-forward))
+            filename nodename opoint)
+        (setq filename (car (car Info-history-forward)))
+        (setq nodename (car (cdr (car Info-history-forward))))
+        (setq opoint (car (cdr (cdr (car Info-history-forward)))))
+        (Info-find-node filename nodename)
+        (setq Info-history-forward history-forward)
+        (goto-char opoint)))))
 \f
 (add-to-list 'Info-virtual-files
 	     '("\\`dir\\'"
@@ -2377,7 +2416,11 @@ Info-directory-find-node
 (defun Info-directory ()
   "Go to the Info directory node."
   (interactive)
-  (Info-find-node "dir" "top"))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (Info-find-node "dir" "top"))))
 \f
 (add-to-list 'Info-virtual-files
 	     '("\\`\\*History\\*\\'"
@@ -2416,9 +2459,13 @@ Info-history-find-node
 (defun Info-history ()
   "Go to a node with a menu of visited nodes."
   (interactive nil Info-mode)
-  (Info-find-node "*History*" "Top")
-  (Info-next-reference)
-  (Info-next-reference))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (Info-find-node "*History*" "Top")
+      (Info-next-reference)
+      (Info-next-reference))))
 \f
 (add-to-list 'Info-virtual-nodes
 	     '("\\`\\*TOC\\*\\'"
@@ -2453,12 +2500,16 @@ Info-toc
   "Go to a node with table of contents of the current Info file.
 Table of contents is created from the tree structure of menus."
   (interactive nil Info-mode)
-  (Info-find-node Info-current-file "*TOC*")
-  (let ((prev-node (nth 1 (car Info-history))) p)
-    (goto-char (point-min))
-    (if (setq p (search-forward (concat "*Note " prev-node ":") nil t))
-	(setq p (- p (length prev-node) 2)))
-    (goto-char (or p (point-min)))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (Info-find-node Info-current-file "*TOC*")
+      (let ((prev-node (nth 1 (car Info-history))) p)
+        (goto-char (point-min))
+        (if (setq p (search-forward (concat "*Note " prev-node ":") nil t))
+            (setq p (- p (length prev-node) 2)))
+        (goto-char (or p (point-min)))))))
 
 (defun Info-toc-insert (nodes node-list level curr-file)
   "Insert table of contents with references to nodes."
@@ -2798,38 +2849,45 @@ Info-menu
 new buffer."
   (interactive
    (let (;; If point is within a menu item, use that item as the default
-	 (default nil)
-	 (p (point))
-	 beg
-	 (case-fold-search t))
+         (default nil)
+         (p (point))
+         beg
+         (case-fold-search t)
+         (info-window (get-buffer-window "*info*")))
+     (unless (window-live-p info-window)
+       (user-error "There is no visible Info buffer."))
+     (if (not (eq (selected-window) info-window))
+         (Info-jump)
+       (setq Info-jump nil))
      (save-excursion
        (goto-char (point-min))
        (if (not (search-forward "\n* menu:" nil t))
-	   (user-error "No menu in this node"))
+           (user-error "No menu in this node"))
        (setq beg (point))
        (and (< (point) p)
-	    (save-excursion
-	      (goto-char p)
-	      (end-of-line)
-	      (if (re-search-backward (concat "\n\\* +\\("
-					      Info-menu-entry-name-re
-					      "\\):")
+            (save-excursion
+              (goto-char p)
+              (end-of-line)
+              (if (re-search-backward (concat "\n\\* +\\("
+                                              Info-menu-entry-name-re
+                                              "\\):")
                                       beg t)
-		  (setq default (match-string-no-properties 1))))))
+                  (setq default (match-string-no-properties 1))))))
      (let ((item nil))
        (while (null item)
-	 (setq item (let ((completion-ignore-case t)
-			  (Info-complete-menu-buffer (current-buffer)))
-		      (completing-read (format-prompt "Menu item" default)
-				       #'Info-complete-menu-item nil t nil nil
+         (setq item (let ((completion-ignore-case t)
+                          (Info-complete-menu-buffer (current-buffer)))
+                      (completing-read (format-prompt "Menu item" default)
+                                       #'Info-complete-menu-item nil t nil nil
                                        default))))
        (list item current-prefix-arg)))
    Info-mode)
   ;; there is a problem here in that if several menu items have the same
   ;; name you can only go to the node of the first with this command.
   (Info-goto-node (Info-extract-menu-item menu-item)
-		  (and fork
-		       (if (stringp fork) fork menu-item))))
+                  (and fork
+                       (if (stringp fork) fork menu-item)))
+  (when Info-jump (Info-jump)))
 
 (defun Info-extract-menu-item (menu-item)
   (setq menu-item (regexp-quote menu-item))
@@ -2869,32 +2927,44 @@ Info-nth-menu-item
   "Go to the node of the Nth menu item.
 N is the digit argument used to invoke this command."
   (interactive nil Info-mode)
-  (Info-goto-node
-   (Info-extract-menu-counting
-    (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (Info-goto-node
+       (Info-extract-menu-counting
+        (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0))))))
 
 (defun Info-top-node ()
   "Go to the Top node of this file."
   (interactive nil Info-mode)
-  (Info-goto-node "Top"))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (Info-goto-node "Top"))))
 
 (defun Info-final-node ()
   "Go to the final node in this file."
   (interactive nil Info-mode)
-  (Info-goto-node "Top")
-  (let ((Info-history nil)
-	(case-fold-search t))
-    ;; Go to the last node in the menu of Top.  But don't delve into
-    ;; detailed node listings.
-    (Info-goto-node (Info-extract-menu-counting nil t))
-    ;; If the last node in the menu is not last in pointer structure,
-    ;; move forward (but not down- or upward - see bug#1116) until we
-    ;; can't go any farther.
-    (while (Info-forward-node t t t) nil)
-    ;; Then keep moving down to last subnode, unless we reach an index.
-    (while (and (not (Info-index-node))
-		(save-excursion (search-forward "\n* Menu:" nil t)))
-      (Info-goto-node (Info-extract-menu-counting nil)))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (Info-goto-node "Top")
+      (let ((Info-history nil)
+            (case-fold-search t))
+        ;; Go to the last node in the menu of Top.  But don't delve into
+        ;; detailed node listings.
+        (Info-goto-node (Info-extract-menu-counting nil t))
+        ;; If the last node in the menu is not last in pointer structure,
+        ;; move forward (but not down- or upward - see bug#1116) until we
+        ;; can't go any farther.
+        (while (Info-forward-node t t t) nil)
+        ;; Then keep moving down to last subnode, unless we reach an index.
+        (while (and (not (Info-index-node))
+                    (save-excursion (search-forward "\n* Menu:" nil t)))
+          (Info-goto-node (Info-extract-menu-counting nil)))))))
 
 (defun Info-forward-node (&optional not-down not-up no-error)
   "Go forward one node, considering all nodes as forming one sequence.
@@ -2905,66 +2975,76 @@ Info-forward-node
 NOT-UP non-nil means don't go to parent nodes, and NO-ERROR non-nil means
 don't signal a user-error if there's no node to go to."
   (interactive nil Info-mode)
-  (goto-char (point-min))
-  (forward-line 1)
-  (let ((case-fold-search t))
-    ;; three possibilities, in order of priority:
-    ;;     1. next node is in a menu in this node (but not in an index)
-    ;;     2. next node is next at same level
-    ;;     3. next node is up and next
-    (cond ((and (not not-down)
-		(save-excursion (search-forward "\n* menu:" nil t))
-		(not (Info-index-node)))
-	   (Info-goto-node (Info-extract-menu-counting 1))
-	   t)
-	  ((save-excursion (search-backward "next:" nil t))
-	   (Info-next)
-	   t)
-	  ((and (not not-up)
-		(save-excursion (search-backward "up:" nil t))
-		;; Use string-equal, not equal, to ignore text props.
-		(not (string-equal (downcase (Info-extract-pointer "up"))
-				   "top")))
-	   (let ((old-node Info-current-node))
-	     (Info-up)
-	     (let ((old-history Info-history)
-		   success)
-	       (unwind-protect
-		   (setq success (Info-forward-node t nil no-error))
-		 (or success (Info-goto-node old-node)))
-	       (if Info-history-skip-intermediate-nodes
-		   (setq Info-history old-history)))))
-	  (no-error nil)
-	  (t (user-error "No pointer forward from this node")))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (goto-char (point-min))
+      (forward-line 1)
+      (let ((case-fold-search t))
+        ;; three possibilities, in order of priority:
+        ;;     1. next node is in a menu in this node (but not in an index)
+        ;;     2. next node is next at same level
+        ;;     3. next node is up and next
+        (cond ((and (not not-down)
+                    (save-excursion (search-forward "\n* menu:" nil t))
+                    (not (Info-index-node)))
+               (Info-goto-node (Info-extract-menu-counting 1))
+               t)
+              ((save-excursion (search-backward "next:" nil t))
+               (Info-next)
+               t)
+              ((and (not not-up)
+                    (save-excursion (search-backward "up:" nil t))
+                    ;; Use string-equal, not equal, to ignore text props.
+                    (not (string-equal (downcase (Info-extract-pointer "up"))
+                                       "top")))
+               (let ((old-node Info-current-node))
+                 (Info-up)
+                 (let ((old-history Info-history)
+                       success)
+                   (unwind-protect
+                       (setq success (Info-forward-node t nil no-error))
+                     (or success (Info-goto-node old-node)))
+                   (if Info-history-skip-intermediate-nodes
+                       (setq Info-history old-history)))))
+              (no-error nil)
+              (t (user-error "No pointer forward from this node")))))))
 
 (defun Info-backward-node ()
   "Go backward one node, considering all nodes as forming one sequence.
 If the current node has a \"previous\" node, go to it, descending into its
 last sub-node, if any; otherwise go \"up\" to the parent node."
   (interactive nil Info-mode)
-  (let ((prevnode (Info-extract-pointer "prev[ious]*" t))
-	(upnode (Info-extract-pointer "up" t))
-	(case-fold-search t))
-    (cond ((and upnode (string-search "(" upnode))
-	   (user-error "First node in file"))
-	  ((and upnode (or (null prevnode)
-			   ;; Use string-equal, not equal,
-			   ;; to ignore text properties.
-			   (string-equal (downcase prevnode)
-					 (downcase upnode))))
-	   (Info-up))
-	  (prevnode
-	   ;; If we move back at the same level,
-	   ;; go down to find the last subnode*.
-	   (Info-prev)
-	   (let ((old-history Info-history))
-	     (while (and (not (Info-index-node))
-			 (save-excursion (search-forward "\n* Menu:" nil t)))
-	       (Info-goto-node (Info-extract-menu-counting nil)))
-	     (if Info-history-skip-intermediate-nodes
-		 (setq Info-history old-history))))
-	  (t
-	   (user-error "No pointer backward from this node")))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (let ((prevnode (Info-extract-pointer "prev[ious]*" t))
+            (upnode (Info-extract-pointer "up" t))
+            (case-fold-search t))
+        (cond ((and upnode (string-search "(" upnode))
+               (user-error "First node in file"))
+              ((and upnode (or (null prevnode)
+                               ;; Use string-equal, not equal,
+                               ;; to ignore text properties.
+                               (string-equal (downcase prevnode)
+                                             (downcase upnode))))
+               (Info-up))
+              (prevnode
+               ;; If we move back at the same level,
+               ;; go down to find the last subnode*.
+               (Info-prev)
+               (let ((old-history Info-history))
+                 (while (and (not (Info-index-node))
+                             (save-excursion (search-forward "\n* Menu:" nil t)))
+                   (Info-goto-node (Info-extract-menu-counting nil)))
+                 (if Info-history-skip-intermediate-nodes
+                     (setq Info-history old-history))))
+              (t
+               (user-error "No pointer backward from this node")))
+        ;; Emacs sometimes display just the very last line
+        (goto-char (point-min))))))
 
 (define-obsolete-function-alias 'Info-exit #'quit-window "27.1")
 
@@ -2972,31 +3052,39 @@ Info-next-menu-item
   "Go to the node of the next menu item."
   (interactive nil Info-mode)
   ;; Bind this in case the user sets it to nil.
-  (let* ((case-fold-search t)
-	 (node
-	  (save-excursion
-	    (forward-line -1)
-	    (search-forward "\n* menu:" nil t)
-	    (and (search-forward "\n* " nil t)
-		 (Info-extract-menu-node-name)))))
-    (if node (Info-goto-node node)
-      (user-error "No more items in menu"))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (let* ((case-fold-search t)
+             (node
+              (save-excursion
+                (forward-line -1)
+                (search-forward "\n* menu:" nil t)
+                (and (search-forward "\n* " nil t)
+                     (Info-extract-menu-node-name)))))
+        (if node (Info-goto-node node)
+          (user-error "No more items in menu"))))))
 
 (defun Info-last-menu-item ()
   "Go to the node of the previous menu item."
   (interactive nil Info-mode)
-  (save-excursion
-    (forward-line 1)
-    ;; Bind this in case the user sets it to nil.
-    (let* ((case-fold-search t)
-	   (beg (save-excursion
-		  (and (search-backward "\n* menu:" nil t)
-		       (point)))))
-      (or (and beg (search-backward "\n* " beg t))
-	  (user-error "No previous items in menu")))
-    (Info-goto-node (save-excursion
-		      (goto-char (match-end 0))
-		      (Info-extract-menu-node-name)))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (save-excursion
+        (forward-line 1)
+        ;; Bind this in case the user sets it to nil.
+        (let* ((case-fold-search t)
+               (beg (save-excursion
+                      (and (search-backward "\n* menu:" nil t)
+                           (point)))))
+          (or (and beg (search-backward "\n* " beg t))
+              (user-error "No previous items in menu")))
+        (Info-goto-node (save-excursion
+                          (goto-char (match-end 0))
+                          (Info-extract-menu-node-name)))))))
 
 (defmacro Info-no-error (&rest body)
   `(condition-case nil (progn ,@body t) (error nil)))
@@ -3004,61 +3092,69 @@ Info-no-error
 (defun Info-next-preorder ()
   "Go to the next subnode or the next node, or go up a level."
   (interactive nil Info-mode)
-  (cond ((Info-no-error (Info-next-menu-item)))
-	((Info-no-error (Info-next)))
-	((Info-no-error (Info-up t))
-	 ;; Since we have already gone thru all the items in this menu,
-	 ;; go up to the end of this node.
-	 (goto-char (point-max))
-	 ;; Since logically we are done with the node with that menu,
-	 ;; move on from it.  But don't add intermediate nodes
-	 ;; to the history on recursive calls.
-	 (let ((old-history Info-history))
-	   (Info-next-preorder)
-	   (if Info-history-skip-intermediate-nodes
-	       (setq Info-history old-history))))
-	(t
-	 (user-error "No more nodes"))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (cond ((Info-no-error (Info-next-menu-item)))
+            ((Info-no-error (Info-next)))
+            ((Info-no-error (Info-up t))
+             ;; Since we have already gone thru all the items in this menu,
+             ;; go up to the end of this node.
+             (goto-char (point-max))
+             ;; Since logically we are done with the node with that menu,
+             ;; move on from it.  But don't add intermediate nodes
+             ;; to the history on recursive calls.
+             (let ((old-history Info-history))
+               (Info-next-preorder)
+               (if Info-history-skip-intermediate-nodes
+                   (setq Info-history old-history))))
+            (t
+             (user-error "No more nodes"))))))
 
 (defun Info-last-preorder ()
   "Go to the last node, popping up a level if there is none."
   (interactive nil Info-mode)
-  (cond ((and Info-scroll-prefer-subnodes
-	      (Info-no-error
-	       (Info-last-menu-item)
-	       ;; If we go down a menu item, go to the end of the node
-	       ;; so we can scroll back through it.
-	       (goto-char (point-max))))
-	 ;; Keep going down, as long as there are nested menu nodes.
-	 (let ((old-history Info-history))
-	   (while (Info-no-error
-		   (Info-last-menu-item)
-		   ;; If we go down a menu item, go to the end of the node
-		   ;; so we can scroll back through it.
-		   (goto-char (point-max))))
-	   (if Info-history-skip-intermediate-nodes
-	       (setq Info-history old-history)))
-	 (recenter -1))
-	((and (Info-no-error (Info-extract-pointer "prev"))
-	      (not (equal (Info-extract-pointer "up")
-			  (Info-extract-pointer "prev"))))
-	 (Info-no-error (Info-prev))
-	 (goto-char (point-max))
-	 (let ((old-history Info-history))
-	   (while (Info-no-error
-		   (Info-last-menu-item)
-		   ;; If we go down a menu item, go to the end of the node
-		   ;; so we can scroll back through it.
-		   (goto-char (point-max))))
-	   (if Info-history-skip-intermediate-nodes
-	       (setq Info-history old-history)))
-	 (recenter -1))
-	((Info-no-error (Info-up t))
-	 (goto-char (point-min))
-	 (let ((case-fold-search t))
-	   (or (search-forward "\n* Menu:" nil t)
-	       (goto-char (point-max)))))
-	(t (user-error "No previous nodes"))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (cond ((and Info-scroll-prefer-subnodes
+                  (Info-no-error
+                   (Info-last-menu-item)
+                   ;; If we go down a menu item, go to the end of the node
+                   ;; so we can scroll back through it.
+                   (goto-char (point-max))))
+             ;; Keep going down, as long as there are nested menu nodes.
+             (let ((old-history Info-history))
+               (while (Info-no-error
+                       (Info-last-menu-item)
+                       ;; If we go down a menu item, go to the end of the node
+                       ;; so we can scroll back through it.
+                       (goto-char (point-max))))
+               (if Info-history-skip-intermediate-nodes
+                   (setq Info-history old-history)))
+             (recenter -1))
+            ((and (Info-no-error (Info-extract-pointer "prev"))
+                  (not (equal (Info-extract-pointer "up")
+                              (Info-extract-pointer "prev"))))
+             (Info-no-error (Info-prev))
+             (goto-char (point-max))
+             (let ((old-history Info-history))
+               (while (Info-no-error
+                       (Info-last-menu-item)
+                       ;; If we go down a menu item, go to the end of the node
+                       ;; so we can scroll back through it.
+                       (goto-char (point-max))))
+               (if Info-history-skip-intermediate-nodes
+                   (setq Info-history old-history)))
+             (recenter -1))
+            ((Info-no-error (Info-up t))
+             (goto-char (point-min))
+             (let ((case-fold-search t))
+               (or (search-forward "\n* Menu:" nil t)
+                   (goto-char (point-max)))))
+            (t (user-error "No previous nodes"))))))
 
 (defun Info-scroll-up ()
   "Scroll one screenful forward in Info, considering all nodes as one sequence.
@@ -3075,23 +3171,27 @@ Info-scroll-up
 in other ways.)"
 
   (interactive nil Info-mode)
-  (if (or (< (window-start) (point-min))
-	  (> (window-start) (point-max)))
-      (set-window-start (selected-window) (point)))
-  (let* ((case-fold-search t)
-	 (virtual-end (save-excursion
-			(goto-char (point-min))
-			(if (and Info-scroll-prefer-subnodes
-				 (search-forward "\n* Menu:" nil t))
-			    (point)
-			  (point-max)))))
-    (if (or (< virtual-end (window-start))
-	    (pos-visible-in-window-p virtual-end))
-	(cond
-	 (Info-scroll-prefer-subnodes (Info-next-preorder))
-	 ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1))))
-	 (t (Info-next-preorder)))
-      (scroll-up))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+        (if (or (< (window-start) (point-min))
+                (> (window-start) (point-max)))
+            (set-window-start (selected-window) (point)))
+      (let* ((case-fold-search t)
+             (virtual-end (save-excursion
+                            (goto-char (point-min))
+                            (if (and Info-scroll-prefer-subnodes
+                                     (search-forward "\n* Menu:" nil t))
+                                (point)
+                              (point-max)))))
+        (if (or (< virtual-end (window-start))
+                (pos-visible-in-window-p virtual-end))
+            (cond
+             (Info-scroll-prefer-subnodes (Info-next-preorder))
+             ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1))))
+             (t (Info-next-preorder)))
+          (scroll-up))))))
 
 (defun Info-mouse-scroll-up (e)
   "Scroll one screenful forward in Info, using the mouse.
@@ -3109,21 +3209,25 @@ Info-scroll-down
 beginning of a node, that goes to the previous node or back up to the
 parent node."
   (interactive nil Info-mode)
-  (if (or (< (window-start) (point-min))
-	  (> (window-start) (point-max)))
-      (set-window-start (selected-window) (point)))
-  (let* ((case-fold-search t)
-	 (current-point (point))
-	 (virtual-end
-	  (and Info-scroll-prefer-subnodes
-	       (save-excursion
-		 (setq current-point (line-beginning-position))
-		 (goto-char (point-min))
-		 (search-forward "\n* Menu:" current-point t)))))
-    (if (or virtual-end
-	    (pos-visible-in-window-p (point-min) nil t))
-	(Info-last-preorder)
-      (scroll-down))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (if (or (< (window-start) (point-min))
+              (> (window-start) (point-max)))
+          (set-window-start (selected-window) (point)))
+      (let* ((case-fold-search t)
+             (current-point (point))
+             (virtual-end
+              (and Info-scroll-prefer-subnodes
+                   (save-excursion
+                     (setq current-point (line-beginning-position))
+                     (goto-char (point-min))
+                     (search-forward "\n* Menu:" current-point t)))))
+        (if (or virtual-end
+                (pos-visible-in-window-p (point-min) nil t))
+            (Info-last-preorder)
+          (scroll-down))))))
 
 (defun Info-mouse-scroll-down (e)
   "Scroll one screenful backward in Info, using the mouse.
@@ -3175,55 +3279,63 @@ Info-next-reference
 If COUNT is non-nil (interactively with a prefix arg), jump over
 COUNT cross-references."
   (interactive "i\np" Info-mode)
-  (unless count
-    (setq count 1))
-  (if (< count 0)
-      (Info-prev-reference recur (- count))
-    (while (unless (zerop count) (setq count (1- count)))
-      (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://")
-	    (old-pt (point))
-	    (case-fold-search t))
-	(or (eobp) (forward-char 1))
-	(or (Info-next-reference-or-link pat 'link)
-	    (progn
-	      (goto-char (point-min))
-	      (or (Info-next-reference-or-link pat 'link)
-		  (progn
-		    (goto-char old-pt)
-		    (user-error "No cross references in this node")))))
-	(if (looking-at "\\* Menu:")
-	    (if recur
-		(user-error "No cross references in this node")
-	      (Info-next-reference t))
-	  (if (looking-at "^\\* ")
-	      (forward-char 2)))))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (unless count
+        (setq count 1))
+      (if (< count 0)
+          (Info-prev-reference recur (- count))
+        (while (unless (zerop count) (setq count (1- count)))
+          (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://")
+                (old-pt (point))
+                (case-fold-search t))
+            (or (eobp) (forward-char 1))
+            (or (Info-next-reference-or-link pat 'link)
+                (progn
+                  (goto-char (point-min))
+                  (or (Info-next-reference-or-link pat 'link)
+                      (progn
+                        (goto-char old-pt)
+                        (user-error "No cross references in this node")))))
+            (if (looking-at "\\* Menu:")
+                (if recur
+                    (user-error "No cross references in this node")
+                  (Info-next-reference t))
+              (if (looking-at "^\\* ")
+                  (forward-char 2)))))))))
 
 (defun Info-prev-reference (&optional recur count)
   "Move cursor to the previous cross-reference or menu item in the node.
 If COUNT is non-nil (interactively with a prefix arg), jump over
 COUNT cross-references."
   (interactive "i\np" Info-mode)
-  (unless count
-    (setq count 1))
-  (if (< count 0)
-      (Info-next-reference recur (- count))
-    (while (unless (zerop count) (setq count (1- count)))
-      (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://")
-	    (old-pt (point))
-	    (case-fold-search t))
-	(or (Info-prev-reference-or-link pat 'link)
-	    (progn
-	      (goto-char (point-max))
-	      (or (Info-prev-reference-or-link pat 'link)
-		  (progn
-		    (goto-char old-pt)
-		    (user-error "No cross references in this node")))))
-	(if (looking-at "\\* Menu:")
-	    (if recur
-		(user-error "No cross references in this node")
-	      (Info-prev-reference t))
-	  (if (looking-at "^\\* ")
-	      (forward-char 2)))))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (unless count
+        (setq count 1))
+      (if (< count 0)
+          (Info-next-reference recur (- count))
+        (while (unless (zerop count) (setq count (1- count)))
+          (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://")
+                (old-pt (point))
+                (case-fold-search t))
+            (or (Info-prev-reference-or-link pat 'link)
+                (progn
+                  (goto-char (point-max))
+                  (or (Info-prev-reference-or-link pat 'link)
+                      (progn
+                        (goto-char old-pt)
+                        (user-error "No cross references in this node")))))
+            (if (looking-at "\\* Menu:")
+                (if recur
+                    (user-error "No cross references in this node")
+                  (Info-prev-reference t))
+              (if (looking-at "^\\* ")
+                  (forward-char 2)))))))))
 \f
 (defun Info-index-nodes (&optional file)
   "Return a list of names of all index nodes in Info FILE.
@@ -3345,64 +3457,71 @@ Info-index
 Give an empty topic name to go to the Index node itself."
   (interactive
    (list
-    (let ((completion-ignore-case t)
-	  (Info-complete-menu-buffer (clone-buffer))
-	  (Info-complete-nodes (Info-index-nodes))
-	  (Info-history-list nil))
-      (info--ensure-not-in-directory-node)
-      (unwind-protect
-	  (with-current-buffer Info-complete-menu-buffer
-	    (Info-goto-index)
-	    (completing-read "Index topic: " #'Info-complete-menu-item))
-	(kill-buffer Info-complete-menu-buffer)))))
+    (let ((info-window (get-buffer-window "*info*")))
+      (unless (window-live-p info-window)
+        (user-error "There is no visible Info buffer."))
+      (if  (not (eq (selected-window) info-window))
+          (Info-jump)
+        (setq Info-jump nil))
+      (let ((completion-ignore-case t)
+            (Info-complete-menu-buffer (clone-buffer))
+            (Info-complete-nodes (Info-index-nodes))
+            (Info-history-list nil))
+        (info--ensure-not-in-directory-node)
+        (unwind-protect
+            (with-current-buffer Info-complete-menu-buffer
+              (Info-goto-index)
+              (completing-read "Index topic: " #'Info-complete-menu-item))
+          (kill-buffer Info-complete-menu-buffer))))))
   (info--ensure-not-in-directory-node)
   ;; Strip leading colon in topic; index format does not allow them.
   (if (and (stringp topic)
-	   (> (length topic) 0)
-	   (= (aref topic 0) ?:))
+           (> (length topic) 0)
+           (= (aref topic 0) ?:))
       (setq topic (substring topic 1)))
   (let ((orignode Info-current-node)
-	(pattern (format "\n\\* +\\([^\n]*\\(%s\\)[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
-			 (regexp-quote topic)))
-	node (nodes (Info-index-nodes))
-	(ohist-list Info-history-list)
-	(case-fold-search t))
+        (pattern (format "\n\\* +\\([^\n]*\\(%s\\)[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
+                         (regexp-quote topic)))
+        node (nodes (Info-index-nodes))
+        (ohist-list Info-history-list)
+        (case-fold-search t))
     (Info-goto-index)
     (or (equal topic "")
-	(let ((matches nil)
-	      (exact nil)
-	      ;; We bind Info-history to nil for internal node-switches so
-	      ;; that we don't put junk in the history.  In the first
-	      ;; Info-goto-index call, above, we do update the history
-	      ;; because that is what the user's previous node choice into it.
-	      (Info-history nil)
-	      found)
-	  (while
-	      (progn
-		(goto-char (point-min))
-		(while (re-search-forward pattern nil t)
-		  (let ((entry (match-string-no-properties 1))
-			(nodename (match-string-no-properties 3))
-			(line (string-to-number (concat "0" (match-string 4)))))
-		    (add-text-properties
-		     (- (match-beginning 2) (match-beginning 1))
-		     (- (match-end 2) (match-beginning 1))
-		     '(face info-index-match) entry)
-		    (push (list entry nodename Info-current-node line) matches)))
-		(setq nodes (cdr nodes) node (car nodes)))
-	    (Info-goto-node node))
-	  (or matches
-	      (progn
-		(Info-goto-node orignode)
-		(user-error "No `%s' in index" topic)))
-	  ;; Here it is a feature that assoc is case-sensitive.
-	  (while (setq found (assoc topic matches))
-	    (setq exact (cons found exact)
-		  matches (delq found matches)))
+        (let ((matches nil)
+              (exact nil)
+              ;; We bind Info-history to nil for internal node-switches so
+              ;; that we don't put junk in the history.  In the first
+              ;; Info-goto-index call, above, we do update the history
+              ;; because that is what the user's previous node choice into it.
+              (Info-history nil)
+              found)
+          (while
+              (progn
+                (goto-char (point-min))
+                (while (re-search-forward pattern nil t)
+                  (let ((entry (match-string-no-properties 1))
+                        (nodename (match-string-no-properties 3))
+                        (line (string-to-number (concat "0" (match-string 4)))))
+                    (add-text-properties
+                     (- (match-beginning 2) (match-beginning 1))
+                     (- (match-end 2) (match-beginning 1))
+                     '(face info-index-match) entry)
+                    (push (list entry nodename Info-current-node line) matches)))
+                (setq nodes (cdr nodes) node (car nodes)))
+            (Info-goto-node node))
+          (or matches
+              (progn
+                (Info-goto-node orignode)
+                (user-error "No `%s' in index" topic)))
+          ;; Here it is a feature that assoc is case-sensitive.
+          (while (setq found (assoc topic matches))
+            (setq exact (cons found exact)
+                  matches (delq found matches)))
           (setq Info-history-list ohist-list)
-	  (setq Info-index-alternatives (nconc exact (nreverse matches))
+          (setq Info-index-alternatives (nconc exact (nreverse matches))
                 Info--current-index-alternative 0)
-	  (Info-index-next 0)))))
+          (Info-index-next 0))))
+  (when Info-jump (select-window Info-jump)))
 
 (defun Info-index-next (num)
   "Go to the next matching index item from the last \\<Info-mode-map>\\[Info-index] command.
@@ -3411,45 +3530,49 @@ Info-index-next
 
 Also see the `Info-warn-on-index-alternatives-wrap' user option."
   (interactive "p" Info-mode)
-  (unless Info-index-alternatives
-    (user-error "No previous `i' command"))
-  (let ((index (+ Info--current-index-alternative num))
-        (total (length Info-index-alternatives))
-        (next-key (key-description (where-is-internal
-				    'Info-index-next overriding-local-map t))))
-    (if (and Info-warn-on-index-alternatives-wrap
-             (> total 1)
-             (cond
-              ((< index 0)
-               (setq Info--current-index-alternative (- total 2))
-               (message
-                "No previous matches, use `%s' to continue from end of list"
-                next-key)
-               t)
-              ((>= index total)
-               (setq Info--current-index-alternative -1)
-               (message
-                "No previous matches, use `%s' to continue from start of list"
-                next-key)
-               t)))
-        ()                              ; Do nothing
-      (setq index (mod index total)
-            Info--current-index-alternative index)
-      (let ((entry (nth index Info-index-alternatives)))
-        (Info-goto-node (nth 1 entry))
-        (if (> (nth 3 entry) 0)
-            ;; Forward 2 lines less because `Info-find-node-2' initially
-            ;; puts point to the 2nd line.
-            (forward-line (- (nth 3 entry) 2))
-          (forward-line 3)              ; don't search in headers
-          (Info-find-index-name (car entry)))
-        (message "Found `%s' in %s.  %s"
-	         (car entry)
-	         (nth 2 entry)
-	         (if (> total 1)
-	             (format-message
-                      "(%s total; use `%s' for next)" total next-key)
-	           "(Only match)"))))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (unless Info-index-alternatives
+        (user-error "No previous `i' command"))
+      (let ((index (+ Info--current-index-alternative num))
+            (total (length Info-index-alternatives))
+            (next-key (key-description (where-is-internal
+                                        'Info-index-next overriding-local-map t))))
+        (if (and Info-warn-on-index-alternatives-wrap
+                 (> total 1)
+                 (cond
+                  ((< index 0)
+                   (setq Info--current-index-alternative (- total 2))
+                   (message
+                    "No previous matches, use `%s' to continue from end of list"
+                    next-key)
+                   t)
+                  ((>= index total)
+                   (setq Info--current-index-alternative -1)
+                   (message
+                    "No previous matches, use `%s' to continue from start of list"
+                    next-key)
+                   t)))
+            ()                              ; Do nothing
+          (setq index (mod index total)
+                Info--current-index-alternative index)
+          (let ((entry (nth index Info-index-alternatives)))
+            (Info-goto-node (nth 1 entry))
+            (if (> (nth 3 entry) 0)
+                ;; Forward 2 lines less because `Info-find-node-2' initially
+                ;; puts point to the 2nd line.
+                (forward-line (- (nth 3 entry) 2))
+              (forward-line 3)              ; don't search in headers
+              (Info-find-index-name (car entry)))
+            (message "Found `%s' in %s.  %s"
+                     (car entry)
+                     (nth 2 entry)
+                     (if (> total 1)
+                         (format-message
+                          "(%s total; use `%s' for next)" total next-key)
+                       "(Only match)"))))))))
 
 (defun Info-find-index-name (name)
   "Move point to the place within the current node where NAME is defined."
@@ -3534,32 +3657,39 @@ Info-virtual-index
   ;; `interactive' is a copy from `Info-index'
   (interactive
    (list
-    (let ((completion-ignore-case t)
-	  (Info-complete-menu-buffer (clone-buffer))
-	  (Info-complete-nodes (Info-index-nodes))
-	  (Info-history-list nil))
-      (info--ensure-not-in-directory-node)
-      (unwind-protect
-	  (with-current-buffer Info-complete-menu-buffer
-	    (Info-goto-index)
-	    (completing-read "Index topic: " #'Info-complete-menu-item))
-	(kill-buffer Info-complete-menu-buffer))))
+    (let ((info-window (get-buffer-window "*info*")))
+      (unless (window-live-p info-window)
+        (user-error "There is no visible Info buffer."))
+      (if (not (eq (selected-window) info-window))
+          (Info-jump)
+        (setq Info-jump nil))
+      (let ((completion-ignore-case t)
+            (Info-complete-menu-buffer (clone-buffer))
+            (Info-complete-nodes (Info-index-nodes))
+            (Info-history-list nil))
+        (info--ensure-not-in-directory-node)
+        (unwind-protect
+            (with-current-buffer Info-complete-menu-buffer
+              (Info-goto-index)
+              (completing-read "Index topic: " #'Info-complete-menu-item))
+          (kill-buffer Info-complete-menu-buffer)))))
    Info-mode)
   (if (equal topic "")
       (Info-find-node Info-current-file "*Index*")
     (unless (assoc (cons Info-current-file topic) Info-virtual-index-nodes)
       (let ((orignode Info-current-node)
-	    (ohist-list Info-history-list))
-	;; Reuse `Info-index' to set `Info-index-alternatives'.
-	(Info-index topic)
-	(push (cons (cons Info-current-file topic) Info-index-alternatives)
-	      Info-virtual-index-nodes)
-	;; Clean up unnecessary side-effects of `Info-index'.
-	(setq Info-history-list ohist-list)
-	(Info-goto-node orignode)
-	(message "")))
+            (ohist-list Info-history-list))
+        ;; Reuse `Info-index' to set `Info-index-alternatives'.
+        (Info-index topic)
+        (push (cons (cons Info-current-file topic) Info-index-alternatives)
+              Info-virtual-index-nodes)
+        ;; Clean up unnecessary side-effects of `Info-index'.
+        (setq Info-history-list ohist-list)
+        (Info-goto-node orignode)
+        (message "")))
     (Info-find-node Info-current-file
-                    (format "*Index for ‘%s’*" topic))))
+                    (format "*Index for ‘%s’*" topic)))
+  (when Info-jump (Info-jump)))
 \f
 (add-to-list 'Info-virtual-files
 	     '("\\`\\*Apropos\\*\\'"
@@ -3696,18 +3826,22 @@ info-apropos
 
 Display a menu of the possible matches."
   (interactive "sIndex apropos: \nP")
-  (if (equal string "")
-      (Info-find-node Info-apropos-file "Top")
-    (let ((nodes Info-apropos-nodes)
-          nodename)
-      (while (and nodes (not (equal string (nth 1 (car nodes)))))
-	(setq nodes (cdr nodes)))
-      (if nodes
-	  (Info-find-node Info-apropos-file (car (car nodes)) nil nil t)
-	(setq nodename (format "Index for ‘%s’" string))
-	(push (list nodename string (Info-apropos-matches string regexp))
-	      Info-apropos-nodes)
-	(Info-find-node Info-apropos-file nodename)))))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (if (equal string "")
+          (Info-find-node Info-apropos-file "Top")
+        (let ((nodes Info-apropos-nodes)
+              nodename)
+          (while (and nodes (not (equal string (nth 1 (car nodes)))))
+            (setq nodes (cdr nodes)))
+          (if nodes
+              (Info-find-node Info-apropos-file (car (car nodes)) nil nil t)
+            (setq nodename (format "Index for ‘%s’" string))
+            (push (list nodename string (Info-apropos-matches string regexp))
+                  Info-apropos-nodes)
+            (Info-find-node Info-apropos-file nodename)))))))
 \f
 (add-to-list 'Info-virtual-files
 	     '("\\`\\*Finder.*\\*\\'"
@@ -3842,17 +3976,29 @@ info-finder
   (interactive
    (when current-prefix-arg
      (require 'finder)
-     (list
-      (completing-read-multiple
-       "Keywords (separated by comma): "
-       (mapcar #'symbol-name (mapcar #'car (append finder-known-keywords
-                                                   (finder-unknown-keywords))))
-       nil t))))
+     (let ((info-window (get-buffer-window "*info*")))
+       (unless (window-live-p info-window)
+         (user-error "There is no visible Info buffer."))
+       (if (not (eq (selected-window) info-window))
+           (Info-jump)
+         (setq Info-jump nil))
+       (list
+        (completing-read-multiple
+         "Keywords (separated by comma): "
+         (mapcar #'symbol-name (mapcar #'car (append finder-known-keywords
+                                                     (finder-unknown-keywords))))
+         nil t)))))
   (require 'finder)
-  (if keywords
-      (Info-find-node Info-finder-file (mapconcat 'identity keywords ", "))
-    (Info-find-node Info-finder-file "Top")))
-
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (if (not (eq (selected-window) info-window))
+        (Info-jump)
+      (setq Info-jump nil))
+    (if keywords
+        (Info-find-node Info-finder-file (mapconcat 'identity keywords ", "))
+      (Info-find-node Info-finder-file "Top")))
+  (when Info-jump (Info-jump)))
 \f
 (defun Info-undefined ()
   "Make command be undefined in Info."
@@ -3871,22 +4017,26 @@ Info-help
 (defun Info-summary ()
   "Display a brief summary of all Info commands."
   (interactive)
-  (save-window-excursion
-    (switch-to-buffer "*Help*")
-    (setq buffer-read-only nil)
-    (erase-buffer)
-    (insert (documentation 'Info-mode))
-    (help-mode)
-    (goto-char (point-min))
-    (let (ch flag)
-      (while (progn (setq flag (not (pos-visible-in-window-p (point-max))))
-		    (message (if flag "Type Space to see more"
-			       "Type Space to return to Info"))
-		    (if (not (eq ?\s (setq ch (read-event))))
-			(progn (push ch unread-command-events) nil)
-		      flag))
-	(scroll-up)))
-    (bury-buffer "*Help*")))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (save-window-excursion
+        (switch-to-buffer "*Help*")
+        (setq buffer-read-only nil)
+        (erase-buffer)
+        (insert (documentation 'Info-mode))
+        (help-mode)
+        (goto-char (point-min))
+        (let (ch flag)
+          (while (progn (setq flag (not (pos-visible-in-window-p (point-max))))
+                        (message (if flag "Type Space to see more"
+                                   "Type Space to return to Info"))
+                        (if (not (eq ?\s (setq ch (read-event))))
+                            (progn (push ch unread-command-events) nil)
+                          flag))
+            (scroll-up)))
+        (bury-buffer "*Help*")))))
 \f
 (defun Info-get-token (pos start all &optional errorstring)
   "Return the token around POS.
@@ -4038,7 +4188,7 @@ Info-mouse-follow-link
 (defvar Info-mode-map
   (let ((map (make-keymap)))
     (suppress-keymap map)
-    (define-key map "." 'beginning-of-buffer)
+    (define-key map "." 'Info-beginning-of-buffer)
     (define-key map " " 'Info-scroll-up)
     (define-key map [?\S-\ ] 'Info-scroll-down)
     (define-key map "\C-m" 'Info-follow-nearest-node)
@@ -4060,10 +4210,10 @@ Info-mode-map
     (define-key map "[" 'Info-backward-node)
     (define-key map "<" 'Info-top-node)
     (define-key map ">" 'Info-final-node)
-    (define-key map "b" 'beginning-of-buffer)
+    (define-key map "b" 'Info-beginning-of-buffer)
     (put 'beginning-of-buffer :advertised-binding "b")
     (define-key map "d" 'Info-directory)
-    (define-key map "e" 'end-of-buffer)
+    (define-key map "e" 'Info-end-of-buffer)
     (define-key map "f" 'Info-follow-reference)
     (define-key map "g" 'Info-goto-node)
     (define-key map "G" 'Info-goto-node-web)
@@ -4071,15 +4221,16 @@ Info-mode-map
     ;; This is for compatibility with standalone info (>~ version 5.2).
     ;; Though for some time, standalone info had H and h reversed.
     ;; See <https://debbugs.gnu.org/16455>.
-    (define-key map "H" 'describe-mode)
+    (define-key map "H" 'Info-describe-mode)
     (define-key map "i" 'Info-index)
+    (define-key map "j" 'Info-jump)
     (define-key map "I" 'Info-virtual-index)
     (define-key map "l" 'Info-history-back)
     (define-key map "L" 'Info-history)
     (define-key map "m" 'Info-menu)
     (define-key map "n" 'Info-next)
     (define-key map "p" 'Info-prev)
-    (define-key map "q" 'quit-window)
+    (define-key map "q" 'Info-quit-window)
     (define-key map "r" 'Info-history-forward)
     (define-key map "s" 'Info-search)
     (define-key map "S" 'Info-search-case-sensitively)
@@ -4104,6 +4255,21 @@ Info-mode-map
     map)
   "Keymap containing Info commands.")
 
+;; with a little help from Helm
+(defcustom Info-mode-prefix-key "C-h M-i"
+  "`Info-mode-map' prefix key for invocation from other buffers."
+  :version "30.1"
+  :type '(choice (string :tag "Key") (const :tag "no binding"))
+  :set (lambda (var key)
+         (when (and (boundp var) (symbol-value var))
+           (define-key (current-global-map)
+                       (read-kbd-macro (symbol-value var)) nil))
+         (when key
+           (define-key (current-global-map)
+                       (read-kbd-macro key)
+                       Info-mode-map))
+         (set var key))
+  :group 'info)
 
 (defun Info-check-pointer (item)
   "Non-nil if ITEM is present in this node."
@@ -4125,7 +4291,7 @@ Info-check-pointer
     :help "Go backward one node, considering all as a sequence"]
    ["Forward" Info-forward-node
     :help "Go forward one node, considering all as a sequence"]
-   ["Beginning" beginning-of-buffer
+   ["Beginning" Info-beginning-of-buffer
     :help "Go to beginning of this node"]
    ["Top" Info-top-node
     :help "Go to top node of file"]
@@ -4323,20 +4489,24 @@ Info-copy-current-node-name
 The name of the Info file is prepended to the node name in parentheses.
 With a zero prefix arg, put the name inside a function call to `info'."
   (interactive "P" Info-mode)
-  (unless Info-current-node
-    (user-error "No current Info node"))
-  (let ((node (if (stringp Info-current-file)
-		  (concat "(" (file-name-sans-extension
-			       (file-name-nondirectory Info-current-file))
-			  ") "
-			  Info-current-node))))
-    (if (zerop (prefix-numeric-value arg))
-        (setq node (concat "(info \"" node "\")")))
-    (unless (stringp Info-current-file)
-      (setq node (format "(Info-find-node '%S '%S)"
-			 Info-current-file Info-current-node)))
-    (kill-new node)
-    (message "%s" node)))
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (unless Info-current-node
+        (user-error "No current Info node"))
+      (let ((node (if (stringp Info-current-file)
+		      (concat "(" (file-name-sans-extension
+			           (file-name-nondirectory Info-current-file))
+			      ") "
+			      Info-current-node))))
+        (if (zerop (prefix-numeric-value arg))
+            (setq node (concat "(info \"" node "\")")))
+        (unless (stringp Info-current-file)
+          (setq node (format "(Info-find-node '%S '%S)"
+			     Info-current-file Info-current-node)))
+        (kill-new node)
+        (message "%s" node)))))
 
 \f
 ;; Info mode is suitable only for specially formatted data.
@@ -5509,6 +5679,42 @@ info--manual-names
 			    (apply-partially #'Info-read-node-name-2
 					     Info-directory-list
 					     (mapcar #'car Info-suffix-list))))))))
+\f
+;; commands from special-mode wrapped to work on Info-mode only
+
+(defun Info-beginning-of-buffer ()
+  "Move point to the beginning of *info* buffer."
+  (interactive)
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (goto-char (point-min)))))
+
+(defun Info-end-of-buffer ()
+  "Move point to the beginning of *info* buffer."
+  (interactive)
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (goto-char (point-max)))))
+
+(defun Info-describe-mode ()
+  "As `describe-mode' but for *Help* buffer only."
+  (interactive)
+  (let ((info-window (get-buffer "*info*")))
+    (unless (window-live-p (get-buffer-window info-window))
+      (user-error "There is no visible Info buffer."))
+    (describe-mode info-window)))
+
+(defun Info-quit-window ()
+  (interactive)
+  (let ((info-window (get-buffer-window "*info*")))
+    (unless (window-live-p info-window)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window info-window
+      (quit-window nil info-window))))
 
 (provide 'info)
 
-- 
2.40.1


  reply	other threads:[~2023-05-30 13:31 UTC|newest]

Thread overview: 62+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-05-30  5:38 Control help- and Info-mode buffers from other buffers Arthur Miller
2023-05-30 12:54 ` Manuel Giraud via Emacs development discussions.
2023-05-30 13:31   ` Arthur Miller [this message]
2023-05-30 15:22     ` Manuel Giraud via Emacs development discussions.
2023-05-30 17:29     ` Juri Linkov
2023-05-31  5:55       ` Arthur Miller
2023-05-31 17:13         ` Juri Linkov
2023-06-01  3:16           ` Arthur Miller
2023-06-01  6:35             ` Juri Linkov
2023-06-01  7:05               ` Eli Zaretskii
2023-06-01  7:20                 ` Juri Linkov
2023-06-01  9:03                   ` Arthur Miller
2023-06-01  9:55                     ` Eli Zaretskii
2023-06-01 14:01                       ` Arthur Miller
2023-06-01  9:16                   ` Arthur Miller
2023-06-01  9:58                     ` Eli Zaretskii
2023-06-01 13:45                       ` Arthur Miller
2023-06-01 16:19                         ` Eli Zaretskii
2023-06-02  1:26                           ` Arthur Miller
2023-06-02  6:34                             ` Juri Linkov
2023-06-02 15:11                               ` Arthur Miller
2023-06-02 15:29                               ` Yuri Khan
2023-06-02 16:32                                 ` Juri Linkov
2023-06-04 14:09                                   ` Arthur Miller
2023-06-02  7:11                             ` Eli Zaretskii
2023-06-02 15:09                               ` Arthur Miller
2023-06-02 15:16                                 ` Eli Zaretskii
2023-06-03 13:53                                   ` Arthur Miller
2023-06-03 14:04                                     ` Eli Zaretskii
2023-06-03 15:06                                       ` Arthur Miller
2023-06-03 15:15                                         ` Eli Zaretskii
2023-06-04 14:19                                           ` Arthur Miller
2023-06-04 14:33                                             ` Eli Zaretskii
2023-06-04  7:52                                     ` Juri Linkov
2023-06-04 14:04                                       ` Arthur Miller
2023-06-04 16:50                                         ` Juri Linkov
2023-06-02 16:13                                 ` Juri Linkov
2023-06-03 13:49                                   ` Manuel Giraud via Emacs development discussions.
2023-06-04  7:44                                     ` Juri Linkov
2023-06-04  8:50                                       ` Eli Zaretskii
2023-06-04 13:40                                         ` [External] : " Drew Adams
2023-06-04 13:53                                           ` Arthur Miller
2023-06-04 14:00                                             ` Drew Adams
2023-06-04 14:20                                           ` Eli Zaretskii
2023-06-04 13:38                                       ` Manuel Giraud via Emacs development discussions.
2023-06-04  7:48                                   ` Juri Linkov
2023-06-01  8:50               ` Arthur Miller
2023-06-01 10:04                 ` Eli Zaretskii
2023-06-01 11:33                   ` Arthur Miller
2023-06-01 16:39                 ` Juri Linkov
2023-06-01 19:15                   ` Eli Zaretskii
2023-06-02  1:10                   ` Arthur Miller
2023-06-02  6:32                     ` Juri Linkov
2023-06-04 14:41                       ` Arthur Miller
2023-06-04 16:54                         ` Juri Linkov
2023-06-01  6:31           ` Juri Linkov
2023-05-30 16:15 ` Eli Zaretskii
2023-05-31  6:38   ` Arthur Miller
2023-05-30 18:04 ` [External] : " Drew Adams
2023-05-31  6:06   ` Arthur Miller
2023-05-31 13:00     ` Drew Adams
2023-05-31 13:27       ` Arthur Miller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=AM9PR09MB4977534E18EE7BFAC956ADB0964B9@AM9PR09MB4977.eurprd09.prod.outlook.com \
    --to=arthur.miller@live.com \
    --cc=emacs-devel@gnu.org \
    --cc=manuel@ledu-giraud.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).