unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Control help- and Info-mode buffers from other buffers
@ 2023-05-30  5:38 Arthur Miller
  2023-05-30 12:54 ` Manuel Giraud via Emacs development discussions.
                   ` (2 more replies)
  0 siblings, 3 replies; 62+ messages in thread
From: Arthur Miller @ 2023-05-30  5:38 UTC (permalink / raw)
  To: emacs-devel

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


I would like to be able to control Info and Help buffers from other buffers, to
lessen switch between windows.

All commands are (should be) completely backwards compatible, so when called
from within help- and Info-mode buffers, they should act as before the
patch. Some rudimentary error checking is performed to ensure that user has a
visible help- and Info-bufers. Mouse commands are not touched (should not be
needed).

This patch also introduces two defcustomed prefix keys for help- and
Info-mode-map, so they can be assigned a prefix key. I have used somewhat
cumbersome to use, C-h M-h for help-mode-map, and C-h M-i Info-mode-map as
prefix keys. I personally use M-h and M-i as prefixes, but those are already
assigned by default, and since I do understand the conseervative nature of the
list, I prefer to default to something cumbersome and let user remap to anything
they find suitable. These are just suggestions. It is also completely valid to
not define any prefix key, and leave that code completely out, but a
pre-defined prefix key does suggest a certain usage pattern.

The defcustom init funciton to read user key is adapted from Helm, I am not sure
how/if that works with copyrights (I have signed myself, and this re-uses a
relatively small piece of code out of Helm).

I have tried to not introduce any visible changes, just to re-implement stuff
under the hood, minus the prefix key customization and extra jump command for
help and Info windows. Original commands as found in help- and Info-modes are
just wrapped into a `with-selected-window', while few commands from some other
modes are also wrapped into aformentioned macro and introduced into help- and
Info-mode as "new" commands. The patch looks big, but overall it is very simple
and "mechanical" hack to just adapt commands to be help- and Info-mode specific.

To note is that there can be several info buffer visible at a time. As now, user
can switch to any of those, and invoke info-mode commands in the buffer. That is
still possible, but when invoked from a non-info buffer, it is not clear what
get-buffer will return. It also seems that it returns even other buffers that
start with "info" prefix, for example "info-remote.el" would also be returned by
(get-buffer "*info*"). I haven't addressed that since I don't think it is very
common case, but it shouldn't be a big deal to fix; adding additional check
for major mode as done in some other places in info.el should suffice.

Perhaps it might be natural to expect this to work even with multiple frames,
but it does not. I am not sure what workflow would be, should user be switched
to the frame containing the help/info buffer, or should buffer be openend in a
new frame? I have no idea or opinion, so I have left that out, so currently same
workflow as now applys (user can't remotely control buffer on other frame).

The user-error message in Info and probably in Help could be expressed in better
English. I am not an English native speaker, so please feel free to improve
it. I am not sure how much, if some, should be mentioned in the manual.

I have tested the code myself, but it is possible that I have overlooked
something, or not tested it enough, in that case please let me know. Hope this
would be acceaptable for Emacs, since I find it quite useful to "remotely"
control help and info buffers, and it is relatively expensive to do this
programmatically via function advising (I have tested).

Thanks in advance and best regards.


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

From 668db11ed748e8426c389c716af1533611a5a725 Mon Sep 17 00:00:00 2001
From: Arthur Miller <arthur.miller@live.com>
Date: Tue, 30 May 2023 07:19:49 +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        | 1150 +++++++++++++++++++++++++------------------
 4 files changed, 925 insertions(+), 516 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..0d56bf0c70f 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
+  "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 `quite-window' but works only on *Help* buffer."
+  (interactive)
+  (let ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (quit-window nil hw))))
+
+(defun help-describe-mode ()
+  "As `describe-mode' but for *Help* buffer only."
+  (interactive)
+  (let ((hb (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window hb))
+      (user-error "Help buffer is not currently visible."))
+    (describe-mode hb)))
+
+(defun help-beginning-of-buffer ()
+  "Move point to the beginning of *Help* buffer."
+  (interactive)
+  (let ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (beginning-of-buffer))))
+
+(defun help-end-of-buffer ()
+  "Move point to the beginning of *Help* buffer."
+  (interactive)
+  (let ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (end-of-buffer))))
+\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 ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (call-interactively #'revert-buffer))))
+\f
+;; Commands from button.el wrapped to work on help-mode only
+
+(defun help-forward-button ()
+  (interactive)
+  (let ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (forward-button 1))))
+
+(defun help-backward-button ()
+  (interactive)
+  (let ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (backward-button 1))))
+
+(defun help-button-describe ()
+  (interactive)
+  (let ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (button-describe))))
+
+(defun help-push-button ()
+  (interactive)
+  (let ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (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 ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (scroll-up-command arg))))
+
+(defun help-scroll-down-command (&optional arg)
+  "As `scroll-down-command' but works only on *Help* buffer."
+  (interactive "^P")
+  (let ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (scroll-down-command arg))))
+
+\f
+
+(defvar help-jump nil)
+(defun help-jump ()
+  "Jump to and from *Help* window."
+  (interactive)
+  (let ((hw (get-buffer-window (help-buffer)))
+        (cw (selected-window)))
+    (cond
+     ((eq hw (selected-window))
+      (unless help-jump
+        (user-error "No previously selected window to jump to."))
+      (select-window help-jump))
+     (t
+      (unless (window-live-p hw)
+        (user-error "Help buffer is not currently visible."))
+      (setq help-jump cw)
+      (select-window hw)))))
 \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 ((hb (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window hb))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer hb
+      (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 ((hb (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window hb))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer hb
+      (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 ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (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 ((hw (get-buffer-window (help-buffer))))
+    (unless (window-live-p hw)
+      (user-error "Help buffer is not currently visible."))
+    (with-selected-window hw
+      (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 ((hb (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window hb))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer hb
+      (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 ((hb (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window hb))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer hb
+      (unless help-mode--current-data
+        (error "No symbol to look up in the current buffer"))
+      (with-selected-window (get-buffer-window hb)
+        (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 ((hb (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window hb))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer hb
+      (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 ((hb (get-buffer (help-buffer))))
+    (unless (window-live-p (get-buffer-window hb))
+      (user-error "Help buffer is not currently visible."))
+    (with-current-buffer hb
+      (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..004e3b9e6a9 100644
--- a/lisp/info.el
+++ b/lisp/info.el
@@ -2273,85 +2273,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      ;; 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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +2400,11 @@ Info-directory-find-node
 (defun Info-directory ()
   "Go to the Info directory node."
   (interactive)
-  (Info-find-node "dir" "top"))
+  (let ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (Info-find-node "dir" "top"))))
 \f
 (add-to-list 'Info-virtual-files
 	     '("\\`\\*History\\*\\'"
@@ -2416,9 +2443,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (Info-find-node "*History*" "Top")
+      (Info-next-reference)
+      (Info-next-reference))))
 \f
 (add-to-list 'Info-virtual-nodes
 	     '("\\`\\*TOC\\*\\'"
@@ -2453,12 +2484,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +2833,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)
+         (iw (get-buffer-window "*info*")))
+     (unless (window-live-p iw)
+       (user-error "There is no visible Info buffer."))
+     (if (not (eq (selected-window) iw))
+         (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 +2911,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +2959,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +3036,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +3076,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +3155,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+        (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 +3193,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +3263,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +3441,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 ((iw (get-buffer-window "*info*")))
+      (unless (window-live-p iw)
+        (user-error "There is no visible Info buffer."))
+      (if  (not (eq (selected-window) iw))
+          (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 +3514,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +3641,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 ((iw (get-buffer-window "*info*")))
+      (unless (window-live-p iw)
+        (user-error "There is no visible Info buffer."))
+      (if (not (eq (selected-window) iw))
+          (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 +3810,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +3960,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 ((iw (get-buffer-window "*info*")))
+       (unless (window-live-p iw)
+         (user-error "There is no visible Info buffer."))
+       (if (not (eq (selected-window) iw))
+           (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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (if (not (eq (selected-window) iw))
+        (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 +4001,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +4172,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 +4194,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,7 +4205,7 @@ 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 "I" 'Info-virtual-index)
     (define-key map "l" 'Info-history-back)
@@ -4079,7 +4213,7 @@ Info-mode-map
     (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 +4238,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 +4274,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 +4472,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 ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (user-error "There is no visible Info buffer."))
+    (with-selected-window iw
+      (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 +5662,59 @@ 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 ((hw (get-buffer-window "*info*")))
+    (unless (window-live-p hw)
+      (error "No Info buffers currently visible."))
+    (with-selected-window hw
+      (beginning-of-buffer))))
+
+(defun Info-end-of-buffer ()
+  "Move point to the beginning of *info* buffer."
+  (interactive)
+  (let ((hw (get-buffer-window "*info*")))
+    (unless (window-live-p hw)
+      (error "No Info buffers currently visible."))
+    (with-selected-window hw
+      (end-of-buffer))))
+
+(defun Info-describe-mode ()
+  "As `describe-mode' but for *Help* buffer only."
+  (interactive)
+  (let ((ib (get-buffer "*info*")))
+    (unless (window-live-p (get-buffer-window ib))
+      (error "No Info buffers currently visible."))
+    (describe-mode ib)))
+
+(defun Info-quit-window ()
+  (interactive)
+  (let ((iw (get-buffer-window "*info*")))
+    (unless (window-live-p iw)
+      (error "No Info buffers currently visible."))
+    (with-selected-window iw
+      (quit-window nil iw))))
+\f
+
+(defvar Info-jump nil)
+(defun Info-jump ()
+  "Jump to and from *info* window."
+  (interactive)
+  (let ((iw (get-buffer-window "*info*"))
+        (cw (selected-window)))
+    (cond
+     ((eq iw (selected-window))
+      (unless Info-jump
+        (error "No previously selected window to jump to."))
+      (select-window Info-jump))
+     (t
+      (when (window-live-p iw)
+        (setq Info-jump cw)
+        (select-window iw))))))
 
 (provide 'info)
 
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2023-06-04 16:54 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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

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).