unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Info-mode patch
@ 2023-06-26 16:09 Arthur Miller
  2023-06-26 17:56 ` Juri Linkov
  0 siblings, 1 reply; 53+ messages in thread
From: Arthur Miller @ 2023-06-26 16:09 UTC (permalink / raw)
  To: emacs-devel

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


After some re-writes and tests with multiple frames and windows; it is possible
to make all commands be callable from other windows, with relatively little
effort, but it is not just to wrap them in with-selected-window.

Care has to be taken to ensure all prompts are presented to the user on the
selected frame, so they don't interfere with the window manager if user has
focus follow mouse enabled.

Some commands call themselves recursively, or they call other info commands
internally. Those commands now take an optional 'window' parameter to avoid
prompting user multiple times in what would be percieved as a single command
from the user perspective.

In general, I have tried to minimze prompting as much as possible; hopefully
user is prompted only when there is ambiguity about which window to act on.

If user is already in an info window, all commands act by default on that
window, so user can use single keys to invoke info commands. This is the same
behavior as pre-patch. If there are multiple info windows, then the user can
press C-u to act on another info window. If there are more then 2 other info
windows, then the user is prompted to choose one, otherwise the "other info
window" is selected automatically (no prompting).

With numeric prefix, C-n, if there is *info*<n>, that window is chosen. If there
are multiple windows with the same number, say two *info*<1> windows, then the
user is prompted which one to choose.

To minimize both prompting, and typing of the prefix; it is also possible to
have multiple info windows, and to create a "connection" to a particular info
window. Consider info buffers *info*<1> and *info*<2>, and say user works with
files "foo" and "bar". It is possible to press C-u C-u before any Info command
and choose *info*<1> in foo, and C-u C-u some-info-command and select *info*<2>
in bar, to create a connection for foo and bar.

After the connection is created the user can just press Info-mode prefix key to
execute info commands in foo or bar and not be prompted which info window to
choose, and not having to type C-1 or C-2 either. To override the default
"link", again user can press C-u to execute in other info window.

Problems:

1. Some commands are already using nummeric prefix, which clashes with using
numeric prefix to choose an info window.

Examples:

info-next-reference
info-prev-reference

When called from other info window, with C-u prefix, the reference jumps four
references instead of one. That because a count is expected in prefix arg, so
when C-u is seen, Emacs jumps 4 refereces forward. I am not sure how, or if it is
possible to fix that one, so I have left it as-is (user can still switch to the
buffer and press TAB in which case it works normally).

2. Due the fact that interactive form has to be the very first form in a command,
we can't just wrap all commands into with-selected-frame, especially when a
command is called from Lisp. To avoid unnecessary extra prompts, I need a way to
communicate that user has already selected a window. I have tried to declare an
optional variable in function declaration, but interactive form does not seem to
have access to those? For example like this:

#+begin_src emacs-lisp
(defun info-finder (&optional keywords window)
  (interactive
   (let ((w (or window (info-window))))
    ...
#+end_src

but Emacs complains that window is an undefined variable when in interactive
call. Perhaps I am just not familiar enough with interactive forms, but as I
understand I would have to make it a non-optional argument in the function
signature. But that would mess signatures for functions and potentially break
3rd party applications and peoples setups, so I don't think it is an option. But
if I just misunderstand how to write interactive forms, and someone has a better
way, I am happy to learn it.

My option, was to use either a global variable, or closure, or as I did, to 
stash the variable into the symbol plist. I tried with closures, but then I got
some cconv--helper-something as unknown symbol when I was building Emacs. If I
forward declare it in loadup.el, then it works fine. I haven't looked into
cconv.el why is that, but I choose to skipp that, and used the plists instead.

3. Tests

I did attempt to write few; I wanted mostly to test all commands if called from
lisp (non-interactively). However, since most of the functionality require live
buffers and visible windows, I am not really sure how to make a test that work
in batch, without popping and hiding stuff on the screen. Also mouse cursor
position seem to mess up bit with focus when I split windows from lisp and try
to manipulate visible bufers, so I have gave up on automated tests.

I have factored out everything about selecting window to act on, into one a bit
more general function called 'find-window-for-help' and one, more Info specific,
'info-window'. I am not really happy how I have implemented those two, I think
they look too messy, but they seem to work for now, and I am already dizzy from
testing all kind of setups and selections, so I they'll have to do for now.

If someone would prefer different strategy for the interaction, for example
no prompting at all, than info-window is the place to change that, since all
other commands call that one to obtain a window to act on.

I have tested each and every command in all those possible combinations, and
paths I have described above, but I would not be surprised if I have still
missed some (or many? :)).

Anyway, here is a patch; this time only for Info mode, but it is easily to
produce one for help-mode if this one works in regard to the user interaction,
and how it is implemented.


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

From 5e64e0ff8fcb22414aae5acb69aebc58f7658887 Mon Sep 17 00:00:00 2001
From: Arthur Miller <arthur.miller@live.com>
Date: Mon, 26 Jun 2023 16:58:55 +0200
Subject: [PATCH] Use Info-mode commands from any buffer

Allow commands acting on Info-mode to be called from other
buffers.
* lisp/info.el (Info-virtual-file-p):
* 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):
* lisp/info.el (Info-history-back):
* lisp/info.el (Info-history-forward):
* lisp/info.el (Info-directory):
* lisp/info.el (Info-toc-insert):
* lisp/info.el (Info-toc):
* lisp/info.el (Info-extract-menu-item):
* 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-mouse-scroll-up):
* 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-find-index-name):
* lisp/info.el (Info-virtual-index):
* lisp/info.el (Info-apropos):
* lisp/info.el (Info-finder):
* lisp/info.el (Info-undefined):
* lisp/info.el (Info-follow-nearest-node):
* 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-link):
New symbol used to establish a connection between a buffer and
an Info buffer.
* lisp/info.el (Info-mode-map):
* lisp/info.el (Info-mode-menu):
Update bindings to reflect the new commands for previously
generic commands.
* lisp/window.el (find-window-for-help):
New function. Used to obtain a window for Info- or help-mode.
---
 lisp/info.el   | 1320 +++++++++++++++++++++++++++---------------------
 lisp/window.el |   62 +++
 2 files changed, 794 insertions(+), 588 deletions(-)

diff --git a/lisp/info.el b/lisp/info.el
index 035dff66e75..f53fccb6d75 100644
--- a/lisp/info.el
+++ b/lisp/info.el
@@ -400,6 +400,54 @@ Info-virtual-nodes
 (defvar-local Info-current-node-virtual nil
   "Non-nil if the current Info node is virtual.")
 
+(defvar-local Info-link nil
+  "A connection to an Info-mode buffer.")
+
+(defun info-window (&optional only-selected-frame)
+  "Return an info window from the list of all windows.
+
+If the current buffer is already an info buffer return selected
+window, else find an info window from the list of visible info buffers.
+When ONLY-SELECTED-FRAME is true, consider window list only on
+selected frame.
+
+When invoked with a numeric prefix N return info window named *info*<N>.
+When called with a non-numeric prefix, C-u C-u, create a connection to an Info
+window that will be used as the default for further choice in case of multiple
+info windows. If the connection is set, C-u prefix can bes used to acto on other
+info window but the connected one.
+
+This function memoizes its result in symbols value slot. The last
+result is not preserved between function calls."
+  (let* ((all-frames (not only-selected-frame))
+         window)
+    (cond ((equal current-prefix-arg '(16)) ; always ask
+           (setq Info-link nil
+                 current-prefix-arg nil
+                 window (find-window-for-help 'Info-mode all-frames))
+           (when window (setq Info-link window)))
+          ((equal current-prefix-arg '(4)) ; do in other info window
+           (setq current-prefix-arg nil
+                 window (find-window-for-help
+                         'Info-mode all-frames
+                         (unless (eq major-mode 'Info-mode)
+                           Info-link))))
+          ((and (not current-prefix-arg) (eq major-mode 'Info-mode))
+           (setq window (selected-window)))
+          (t
+           (setq window (if (and Info-link
+                                 (window-live-p Info-link)
+                                 (not (numberp current-prefix-arg)))
+                            Info-link
+                          (find-window-for-help 'Info-mode all-frames)))))
+    (unless window
+      (user-error
+       (if (numberp current-prefix-arg)
+           (format "No info buffer named *info*<%s> found."
+                   current-prefix-arg)
+         "There are no visible info buffers.")))
+    window))
+
 (defun Info-virtual-file-p (filename)
   "Check if Info file FILENAME is virtual."
   (Info-virtual-fun 'find-file filename nil))
@@ -787,7 +835,7 @@ info-setup
     (if (and (zerop (buffer-size))
 	     (null Info-history))
 	;; If we just created the Info buffer, go to the directory.
-	(Info-directory))))
+	(Info-directory (selected-window)))))
 
 ;;;###autoload
 (defun info-emacs-manual ()
@@ -831,12 +879,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)
@@ -2269,89 +2317,84 @@ Info-following-node-name
   (and (looking-at (Info-following-node-name-re))
        (match-string-no-properties 1)))
 
-(defun Info-next ()
+(defun Info-next (&optional window)
   "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*"))
+  (with-selected-window (or window (info-window))
     (Info-goto-node (Info-extract-pointer "next"))))
 
-(defun Info-prev ()
+(defun Info-prev (&optional window)
   "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*"))
+  (with-selected-window (or window (info-window))
     (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous"))))
 
-(defun Info-up (&optional same-file)
+(defun Info-up (&optional same-file window)
   "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*"))
+  (with-selected-window (or window (info-window))
     (let ((old-node Info-current-node)
-	  (old-file Info-current-file)
-	  (node (Info-extract-pointer "up")) p)
+          (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"))
+           (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)))
+               (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))))
 
 (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)))
+  (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)))
+  (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\\'"
@@ -2374,10 +2417,11 @@ Info-directory-find-node
   (Info-insert-dir))
 
 ;;;###autoload
-(defun Info-directory ()
+(defun Info-directory (&optional window)
   "Go to the Info directory node."
   (interactive)
-  (Info-find-node "dir" "top"))
+  (with-selected-window (or window (info-window))
+    (Info-find-node "dir" "top")))
 \f
 (add-to-list 'Info-virtual-files
 	     '("\\`\\*History\\*\\'"
@@ -2416,9 +2460,10 @@ 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))
+  (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 +2498,13 @@ 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)))))
+  (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."
@@ -2577,91 +2623,97 @@ Info-follow-reference
   (interactive
    (let ((completion-ignore-case t)
 	 (case-fold-search t)
+         (window (info-window))
 	 completions default alt-default (start-point (point)) str i bol eol)
-     (save-excursion
-       ;; Store end and beginning of line.
-       (setq eol (line-end-position)
-             bol (line-beginning-position))
-       (goto-char (point-min))
-       (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t)
-	 (setq str (match-string-no-properties 1))
-	 ;; See if this one should be the default.
-	 (and (null default)
-	      (<= (match-beginning 0) start-point)
-	      (<= start-point (point))
-	      (setq default t))
-	 ;; See if this one should be the alternate default.
-	 (and (null alt-default)
-	      (and (<= bol (match-beginning 0))
-		   (<= (point) eol))
-	      (setq alt-default t))
-	 (setq i 0)
-	 (while (setq i (string-match "[ \n\t]+" str i))
-	   (setq str (concat (substring str 0 i) " "
-			     (substring str (match-end 0))))
-	   (setq i (1+ i)))
-	 ;; Record as a completion and perhaps as default.
-	 (if (eq default t) (setq default str))
-	 (if (eq alt-default t) (setq alt-default str))
-	 ;; Don't add this string if it's a duplicate.
-	 (or (assoc-string str completions t)
-	     (push str completions)))
-       (setq completions (nreverse completions)))
-     ;; If no good default was found, try an alternate.
-     (or default
-	 (setq default alt-default))
-     ;; If only one cross-reference found, then make it default.
-     (if (eq (length completions) 1)
-         (setq default (car completions)))
-     (if completions
-         (let ((input (completing-read (format-prompt "Follow reference named"
-                                                      default)
-                                       completions nil t)))
-	   (list (if (equal input "")
-		     default input)
-                 current-prefix-arg))
-       (user-error "No cross-references in this node")))
+     (put 'Info-follow-reference :selected-window window)
+     (with-current-buffer (window-buffer window)
+       (save-excursion
+         ;; Store end and beginning of line.
+         (setq eol (line-end-position)
+               bol (line-beginning-position))
+         (goto-char (point-min))
+         (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t)
+	   (setq str (match-string-no-properties 1))
+	   ;; See if this one should be the default.
+	   (and (null default)
+	        (<= (match-beginning 0) start-point)
+	        (<= start-point (point))
+	        (setq default t))
+	   ;; See if this one should be the alternate default.
+	   (and (null alt-default)
+	        (and (<= bol (match-beginning 0))
+		     (<= (point) eol))
+	        (setq alt-default t))
+	   (setq i 0)
+	   (while (setq i (string-match "[ \n\t]+" str i))
+	     (setq str (concat (substring str 0 i) " "
+			       (substring str (match-end 0))))
+	     (setq i (1+ i)))
+	   ;; Record as a completion and perhaps as default.
+	   (if (eq default t) (setq default str))
+	   (if (eq alt-default t) (setq alt-default str))
+	   ;; Don't add this string if it's a duplicate.
+	   (or (assoc-string str completions t)
+	       (push str completions)))
+         (setq completions (nreverse completions)))
+       ;; If no good default was found, try an alternate.
+       (or default
+	   (setq default alt-default))
+       ;; If only one cross-reference found, then make it default.
+       (if (eq (length completions) 1)
+           (setq default (car completions)))
+       (if completions
+           (let ((input (completing-read (format-prompt "Follow reference named"
+                                                        default)
+                                         completions nil t)))
+	     (list (if (equal input "")
+		       default input)
+                   current-prefix-arg))
+         (user-error "No cross-references in this node"))))
    Info-mode)
-
-  (unless footnotename
-    (error "No reference was specified"))
-
-  (let (target i (str (concat "\\*note " (regexp-quote footnotename)))
-	       (case-fold-search t))
-    (while (setq i (string-search " " str i))
-      (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i))))
-      (setq i (+ i 6)))
-    (save-excursion
-      ;; Move point to the beginning of reference if point is on reference
-      (or (looking-at "\\*note[ \n\t]+")
-          (and (looking-back "\\*note[ \n\t]+"
-                             (save-excursion (skip-chars-backward " \n\t")
-                                             (line-beginning-position)))
-               (goto-char (match-beginning 0)))
-          (if (and (save-excursion
-                     (goto-char (+ (point) 5)) ; skip a possible *note
-                     (re-search-backward "\\*note[ \n\t]+" nil t)
-                     (looking-at str))
-                   (<= (point) (match-end 0)))
-              (goto-char (match-beginning 0))))
-      ;; Go to the reference closest to point
-      (let ((next-ref (save-excursion (and (re-search-forward str nil t)
-                                           (+ (match-beginning 0) 5))))
-            (prev-ref (save-excursion (and (re-search-backward str nil t)
-                                           (+ (match-beginning 0) 5)))))
-        (goto-char (cond ((and next-ref prev-ref)
-                          (if (< (abs (- next-ref (point)))
-                                 (abs (- prev-ref (point))))
-                              next-ref prev-ref))
-                         ((or next-ref prev-ref))
-                         ((user-error "No cross-reference named %s"
-                                      footnotename))))
-        (setq target (Info-extract-menu-node-name t))))
-    (while (setq i (string-match "[ \t\n]+" target i))
-      (setq target (concat (substring target 0 i) " "
-			   (substring target (match-end 0))))
-      (setq i (+ i 1)))
-    (Info-goto-node target fork)))
+  (let ((window (if (called-interactively-p 'any)
+                    (get 'Info-follow-reference :selected-window)
+                  (info-window))))
+    (with-selected-window window
+      (unless footnotename
+        (error "No reference was specified"))
+      (let (target i (str (concat "\\*note " (regexp-quote footnotename)))
+	           (case-fold-search t))
+        (while (setq i (string-search " " str i))
+          (setq str (concat (substring str 0 i)
+                            "[ \t\n]+" (substring str (1+ i))))
+          (setq i (+ i 6)))
+        (save-excursion
+          ;; Move point to the beginning of reference if point is on reference
+          (or (looking-at "\\*note[ \n\t]+")
+              (and (looking-back "\\*note[ \n\t]+"
+                                 (save-excursion (skip-chars-backward " \n\t")
+                                                 (line-beginning-position)))
+                   (goto-char (match-beginning 0)))
+              (if (and (save-excursion
+                         (goto-char (+ (point) 5)) ; skip a possible *note
+                         (re-search-backward "\\*note[ \n\t]+" nil t)
+                         (looking-at str))
+                       (<= (point) (match-end 0)))
+                  (goto-char (match-beginning 0))))
+          ;; Go to the reference closest to point
+          (let ((next-ref (save-excursion (and (re-search-forward str nil t)
+                                               (+ (match-beginning 0) 5))))
+                (prev-ref (save-excursion (and (re-search-backward str nil t)
+                                               (+ (match-beginning 0) 5)))))
+            (goto-char (cond ((and next-ref prev-ref)
+                              (if (< (abs (- next-ref (point)))
+                                     (abs (- prev-ref (point))))
+                                  next-ref prev-ref))
+                             ((or next-ref prev-ref))
+                             ((user-error "No cross-reference named %s"
+                                          footnotename))))
+            (setq target (Info-extract-menu-node-name t))))
+        (while (setq i (string-match "[ \t\n]+" target i))
+          (setq target (concat (substring target 0 i) " "
+			       (substring target (match-end 0))))
+          (setq i (+ i 1)))
+        (Info-goto-node target fork)))))
 
 (defconst Info-menu-entry-name-re "\\(?:[^:]\\|:[^:,.;() \t\n]\\)*"
   ;; We allow newline because this is also used in Info-follow-reference,
@@ -2788,7 +2840,6 @@ Info-complete-menu-item
 			 Info-complete-nodes)))
             (complete-with-action action completions string predicate))))))))
 
-
 (defun Info-menu (menu-item &optional fork)
   "Go to the node pointed to by the menu item named (or abbreviated) MENU-ITEM.
 The menu item should one of those listed in the current node's menu.
@@ -2798,38 +2849,43 @@ 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))
-     (save-excursion
-       (goto-char (point-min))
-       (if (not (search-forward "\n* menu:" nil t))
-	   (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
-					      "\\):")
-                                      beg t)
-		  (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
-                                       default))))
-       (list item current-prefix-arg)))
+         (default nil)
+         (p (point))
+         beg
+         (case-fold-search t)
+         (window (info-window)))
+     (with-current-buffer (window-buffer window)
+       (put 'Info-menu :selected-window window)
+       (save-excursion
+         (goto-char (point-min))
+         (if (not (search-forward "\n* menu:" nil t))
+             (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
+                                                "\\):")
+                                        beg t)
+                    (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
+                                         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))))
+  (let ((window (if (called-interactively-p 'any)
+                    (get 'Info-menu :selected-window)
+                  (info-window))))
+    (with-selected-window window
+      (Info-goto-node (Info-extract-menu-item menu-item)
+                      (and fork
+                           (if (stringp fork) fork menu-item))))))
 
 (defun Info-extract-menu-item (menu-item)
   (setq menu-item (regexp-quote menu-item))
@@ -2869,32 +2925,35 @@ 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))))
+  (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"))
+  (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)))))
+  (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,160 +2964,166 @@ 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")))))
+  (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 (selected-window))
+             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 nil (selected-window))
+               (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")))))
+  (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 nil (selected-window)))
+            (prevnode
+             ;; If we move back at the same level,
+             ;; go down to find the last subnode*.
+             (Info-prev (selected-window))
+             (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"))))))
 
 (define-obsolete-function-alias 'Info-exit #'quit-window "27.1")
 
-(defun Info-next-menu-item ()
+(defun Info-next-menu-item (&optional window)
   "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"))))
+  (with-selected-window (or window (info-window))
+    ;; 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")))))
 
 (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)))))
+  (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)))
 
-(defun Info-next-preorder ()
+(defun Info-next-preorder (&optional window)
   "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"))))
-
-(defun Info-last-preorder ()
+  (with-selected-window (or window (info-window))
+    (cond ((Info-no-error (Info-next-menu-item (selected-window))))
+          ((Info-no-error (Info-next (selected-window))))
+          ((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 (selected-window))
+             (if Info-history-skip-intermediate-nodes
+                 (setq Info-history old-history))))
+          (t
+           (user-error "No more nodes")))))
+
+(defun Info-last-preorder (&optional window)
   "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"))))
+  (with-selected-window (or 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 (selected-window)))
+           (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.
@@ -3073,25 +3138,25 @@ Info-scroll-up
 the menu of a node, it moves to subnode indicated by the following menu
 item.  (That case won't normally result from this command, but can happen
 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))))
+  (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 (selected-window)))
+           ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1))))
+           (t (Info-next-preorder (selected-window))))
+        (scroll-up)))))
 
 (defun Info-mouse-scroll-up (e)
   "Scroll one screenful forward in Info, using the mouse.
@@ -3109,21 +3174,22 @@ 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))))
+  (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 (selected-window))
+        (scroll-down)))))
 
 (defun Info-mouse-scroll-down (e)
   "Scroll one screenful backward in Info, using the mouse.
@@ -3170,60 +3236,62 @@ Info-prev-reference-or-link
 	  (goto-char plink))
       (if pxref (goto-char (or (match-beginning 1) (match-beginning 0)))))))
 
-(defun Info-next-reference (&optional recur count)
+(defun Info-next-reference (&optional recur count window)
   "Move cursor to the next 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-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)
+  (with-selected-window (or window (info-window))
+    (unless count
+      (setq count 1))
+    (if (< count 0)
+        (Info-prev-reference recur (- count) (selected-window))
+      (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 nil (selected-window)))
+            (if (looking-at "^\\* ")
+                (forward-char 2))))))))
+
+(defun Info-prev-reference (&optional recur count window)
   "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)))))))
+  (with-selected-window (or window (info-window))
+    (unless count
+      (setq count 1))
+    (if (< count 0)
+        (Info-next-reference recur (- count) (selected-window))
+      (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 nil (selected-window)))
+            (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.
@@ -3337,72 +3405,83 @@ info--ensure-not-in-directory-node
                       "type \\[Info-menu] to select a manual")))))
 
 ;;;###autoload
-(defun Info-index (topic)
+(defun Info-index (topic &optional window)
   "Look up a string TOPIC in the index for this manual and go to that entry.
 If there are no exact matches to the specified topic, this chooses
 the first match which is a case-insensitive substring of a topic.
 Use the \\<Info-mode-map>\\[Info-index-next] command to see the other matches.
 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))
+   (let ((frame-to-prompt-in (selected-frame))
+         (window (info-window)))
+     (put 'Info-index :selected-window window)
+     (with-selected-window window
+       (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)
+                (with-selected-frame frame-to-prompt-in
+	          (completing-read "Index topic: "
+                                   #'Info-complete-menu-item)))
+            (kill-buffer Info-complete-menu-buffer))))))
+   Info-mode)
+  (let ((window (or window (if (called-interactively-p 'any)
+                               (get 'Info-index :selected-window)
+                             (info-window)))))
+    (with-selected-window window
       (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) ?:))
-      (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))
-    (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)))
-          (setq Info-history-list ohist-list)
-	  (setq Info-index-alternatives (nconc exact (nreverse matches))
-                Info--current-index-alternative 0)
-	  (Info-index-next 0)))))
+      ;; Strip leading colon in topic; index format does not allow them.
+      (if (and (stringp topic)
+               (> (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))
+        (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)))
+              (setq Info-history-list ohist-list)
+              (setq Info-index-alternatives (nconc exact (nreverse matches))
+                    Info--current-index-alternative 0)
+              (Info-index-next 0)))))))
 
 (defun Info-index-next (num)
   "Go to the next matching index item from the last \\<Info-mode-map>\\[Info-index] command.
@@ -3411,45 +3490,46 @@ 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)"))))))
+  (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."
@@ -3533,33 +3613,44 @@ Info-virtual-index
 search results."
   ;; `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 ((frame-to-prompt-in (selected-frame))
+         (window (info-window)))
+     (put 'Info-virtual-index :selected-window window)
+     (with-selected-window window
+       (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)
+                (with-selected-frame frame-to-prompt-in
+	          (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 "")))
-    (Info-find-node Info-current-file
-                    (format "*Index for ‘%s’*" topic))))
+    (let ((window  (if (called-interactively-p 'any)
+                       (get 'Info-virtual-index :selected-window)
+                     (info-window))))
+      (with-selected-window window
+        (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 "")))
+        (Info-find-node Info-current-file
+                        (format "*Index for ‘%s’*" topic))
+        (put 'Info-virtual-index :selected-window nil)))))
 \f
 (add-to-list 'Info-virtual-files
 	     '("\\`\\*Apropos\\*\\'"
@@ -3694,20 +3785,25 @@ info-apropos
   "Search indices of all known Info files on your system for STRING.
 If REGEXP (interactively, the prefix), use a regexp match.
 
-Display a menu of the possible matches."
+Display a menu of the possible matches in selected window.  If given
+numeric prefix argument display results in window *info*<N>.  With
+non-numeric argument prompt user for info buffer to display results in."
   (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)))))
+  (with-selected-window (if current-prefix-arg
+                            (info-window)
+                          (selected-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.*\\*\\'"
@@ -3836,23 +3932,34 @@ Info-finder-find-node
 ;;;###autoload
 (defun info-finder (&optional keywords)
   "Display descriptions of the keywords in the Finder virtual manual.
-In interactive use, a prefix argument directs this command to read
+In interactive use, a non-numeric prefix argument directs this command to read
 a list of keywords separated by comma.  After that, it displays a node
-with a list of packages that contain all specified keywords."
+with a list of packages that contain all specified keywords.  Numeric
+prefix argument will choose window with name *info*<N> if such window
+exists otherwise it will prompt the user to choose a window."
   (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 ((window (info-window)))
+     (put 'info-finder :selected-window window)
+     (when (and current-prefix-arg
+                (not (numberp current-prefix-arg)))
+       (require 'finder)
+       (let ((current-prefix-arg nil))
+         (with-current-buffer (window-buffer window)
+           (list
+            (completing-read-multiple
+             "Keywords (separated by comma): "
+             (mapcar #'symbol-name (mapcar #'car (append finder-known-keywords
+                                                         (finder-unknown-keywords))))
+             nil t)))))))
+  (setq current-prefix-arg nil)
   (require 'finder)
-  (if keywords
-      (Info-find-node Info-finder-file (mapconcat 'identity keywords ", "))
-    (Info-find-node Info-finder-file "Top")))
-
+  (let ((window (if (called-interactively-p 'any)
+                    (get 'info-finder :selected-window)
+                  (info-window))))
+    (with-selected-window window
+      (if keywords
+          (Info-find-node Info-finder-file (mapconcat 'identity keywords ", "))
+        (Info-find-node Info-finder-file "Top")))))
 \f
 (defun Info-undefined ()
   "Make command be undefined in Info."
@@ -3937,7 +4044,7 @@ Info-mouse-follow-nearest-node
   (mouse-set-point click)
   (and (not (Info-follow-nearest-node))
        (save-excursion (forward-line 1) (eobp))
-       (Info-next-preorder)))
+       (Info-next-preorder (selected-window))))
 
 (defun Info-follow-nearest-node (&optional fork)
   "Follow a node reference near point.
@@ -4030,15 +4137,15 @@ Info-mouse-follow-link
 	   (Info-goto-node link-args))
 	  ;; These special values of the `link-args' property are used
 	  ;; for navigation; see `Info-fontify-node'.
-	  ((eq link-args 'prev) (Info-prev))
-	  ((eq link-args 'next) (Info-next))
-	  ((eq link-args 'up)   (Info-up)))))
+	  ((eq link-args 'prev) (Info-prev (selected-window)))
+	  ((eq link-args 'next) (Info-next (selected-window)))
+	  ((eq link-args 'up)   (Info-up (selected-window))))))
 
 \f
 (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 +4167,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 +4178,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 +4186,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,7 +4211,6 @@ Info-mode-map
     map)
   "Keymap containing Info commands.")
 
-
 (defun Info-check-pointer (item)
   "Non-nil if ITEM is present in this node."
   (condition-case nil
@@ -4125,7 +4231,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 +4429,21 @@ 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)))
+  (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.
@@ -5230,8 +5337,7 @@ Info-speedbar-browser
   ;; Make sure that speedbar is active
   (speedbar-frame-mode 1)
   ;; Now, throw us into Info mode on speedbar.
-  (speedbar-change-initial-expansion-list "Info")
-  )
+  (speedbar-change-initial-expansion-list "Info"))
 
 ;; speedbar loads dframe at runtime.
 (declare-function dframe-select-attached-frame "dframe" (&optional frame))
@@ -5509,6 +5615,44 @@ info--manual-names
 			    (apply-partially #'Info-read-node-name-2
 					     Info-directory-list
 					     (mapcar #'car Info-suffix-list))))))))
+\f
+;;; General buffer manipulation support
+;; 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)
+  (with-selected-window (info-window)
+    (goto-char (point-min))))
+
+(defun Info-end-of-buffer ()
+  "Move point to the beginning of *info* buffer."
+  (interactive)
+  (with-selected-window (info-window)
+    (goto-char (point-max))))
+
+(defun Info-describe-mode ()
+  "As `describe-mode' but for Info-mode only."
+  (interactive)
+  ;; I guess we are good if there is any buffer in Info-mode so
+  ;; we do a special here to prevent prompting
+  ;; If there are no info buffer, use (info-window) to signal
+  ;; the error; this to not duplicate the error message in two places
+  (catch 'found
+    (dolist (buffer (buffer-list))
+      (with-current-buffer buffer
+        (when (eq major-mode 'Info-mode)
+          (describe-mode)
+          (throw 'found buffer))))
+    (throw 'found
+           (with-temp-buffer
+             (Info-mode)
+             (describe-mode)))))
+
+(defun Info-quit-window ()
+  (interactive)
+  (with-selected-window (info-window)
+    (quit-window nil (selected-window))))
 
 (provide 'info)
 
diff --git a/lisp/window.el b/lisp/window.el
index 5964fe37ee6..91c764c2e54 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -27,6 +27,8 @@
 
 ;;; Code:
 
+(eval-when-compile (require 'cl-lib))
+
 (defun internal--before-save-selected-window ()
   (cons (selected-window)
         ;; We save and restore all frames' selected windows, because
@@ -10741,6 +10743,66 @@ window-prefix-map
   "0" #'delete-windows-on)
 (define-key ctl-x-map "w" window-prefix-map)
 
+\f
+;; help function to find an Info- or help-mode window
+(defun find-window-for-help (mode &optional all-frames exclude)
+  "Find window displaing buffer with major-mode MODE.
+
+With numeric argument N, return window containing <N> in the name otherwise nil.
+With non-numeric prefix, prompt user to select one of the buffers matching
+MODE.
+
+Meaning of ALL-FRAMES is same as in `window-list-1'.
+EXCLIDE is an Info window not to be considered as a candidate."
+  (cl-labels
+      ((window-candidates (mode)
+         (let (windows)
+           (walk-windows
+            (lambda (w)
+              (with-current-buffer (window-buffer w)
+                (and (eq major-mode mode)
+                     (not (eq exclude w))
+                     (not (eq (selected-window) w))
+                     (push (cons (prin1-to-string w) w) windows))))
+            nil all-frames)
+           windows))
+       (get-window-from-user (window-list)
+         (cdr
+          (assoc
+           (completing-read "Act on window: " window-list) window-list)))
+       (cull (list pred)
+         (let (new)
+           (dolist (elt list)
+             (when (funcall pred elt)
+               (push elt new)))
+           new))
+       (get-numbered-window (window-list N)
+         (let ((numbered-window-list
+                (cull window-list
+                      (lambda (w)
+                        (string-match-p (format "<%s>" N) (car w))))))
+           (cond
+            ((= (length numbered-window-list) 0) nil)
+            ((= (length numbered-window-list) 1)
+             (cdar numbered-window-list))
+            ((> (length numbered-window-list) 1)
+             (get-window-from-user numbered-window-list))))))
+    (let ((window-list (remq (selected-window) (window-candidates mode))))
+      (when (> (length window-list) 0)
+        (let ((window
+               (cond
+                ((numberp current-prefix-arg)
+                 (let ((window
+                        (get-numbered-window window-list current-prefix-arg)))
+                   (if window
+                       window
+                     (when (> (length window-list) 0) ;; misstyped number arg?
+                       (get-window-from-user window-list)))))
+                ((> (length window-list) 1)
+                 (get-window-from-user window-list))
+                (t (cdar window-list)))))
+          window)))))
+
 (provide 'window)
 
 ;;; window.el ends here
-- 
2.40.0


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

end of thread, other threads:[~2023-07-08  8:14 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-26 16:09 Info-mode patch Arthur Miller
2023-06-26 17:56 ` Juri Linkov
2023-06-26 20:17   ` Arthur Miller
2023-06-27  6:32     ` Juri Linkov
2023-06-27  7:54       ` Arthur Miller
2023-06-27 18:11         ` Juri Linkov
2023-06-27 23:09           ` Arthur Miller
2023-06-28  6:50             ` Juri Linkov
2023-06-28 21:52               ` Arthur Miller
2023-06-29  6:44                 ` Juri Linkov
2023-06-29 12:42                   ` Arthur Miller
2023-06-29 15:00                     ` [External] : " Drew Adams
2023-06-29 16:24                       ` Arthur Miller
2023-06-29 17:44                     ` Juri Linkov
2023-06-29 22:28                       ` Arthur Miller
2023-06-30  7:13                         ` Juri Linkov
2023-06-30  8:41                           ` Arthur Miller
2023-06-30 17:57                             ` Juri Linkov
2023-07-01  9:11                               ` Arthur Miller
2023-07-02 17:53                                 ` Juri Linkov
2023-07-02 18:39                                   ` Eli Zaretskii
2023-07-02 22:43                                     ` Arthur Miller
2023-07-03 11:46                                       ` Eli Zaretskii
2023-07-03 12:57                                         ` Arthur Miller
2023-07-03 13:17                                           ` Eli Zaretskii
2023-07-03 18:40                                             ` Juri Linkov
2023-07-03 18:57                                               ` Eli Zaretskii
2023-07-04  6:50                                                 ` easy-menu-define keys for key-valid-p (was: Info-mode patch) Juri Linkov
2023-07-04 11:33                                                   ` Eli Zaretskii
2023-07-03 21:07                                               ` Info-mode patch Arthur Miller
2023-07-04  7:59                                                 ` Andreas Schwab
2023-07-04  8:44                                                   ` Arthur Miller
2023-07-03 17:07                                       ` Eli Zaretskii
2023-07-04 23:58                                         ` Stefan Monnier
2023-07-08  8:14                                           ` Eli Zaretskii
2023-07-02 22:05                                   ` Arthur Miller
2023-07-03 18:45                                     ` Juri Linkov
2023-07-03 22:24                                       ` Arthur Miller
2023-07-04  6:54                                         ` Juri Linkov
2023-07-04  9:43                                           ` Arthur Miller
2023-07-04 17:51                                             ` Juri Linkov
2023-07-04 21:40                                               ` Arthur Miller
2023-07-05  6:17                                                 ` Juri Linkov
2023-07-05 14:25                                                   ` Arthur Miller
2023-07-01  9:59                         ` Getting Gnus to highlight citations in long mails (was: Info-mode patch) Kévin Le Gouguec
2023-07-01 12:40                           ` Getting Gnus to highlight citations in long mails Arthur Miller
2023-07-02 17:56                           ` Juri Linkov
2023-06-27 11:45   ` Info-mode patch Eli Zaretskii
2023-06-27 12:15     ` Arthur Miller
2023-06-27 12:42       ` Eli Zaretskii
2023-06-27 15:28         ` Arthur Miller
2023-06-27 16:03           ` Eli Zaretskii
2023-06-27 16:33             ` 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).