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

* Re: Control help- and Info-mode buffers from other buffers
  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 16:15 ` Eli Zaretskii
  2023-05-30 18:04 ` [External] : " Drew Adams
  2 siblings, 1 reply; 62+ messages in thread
From: Manuel Giraud via Emacs development discussions. @ 2023-05-30 12:54 UTC (permalink / raw)
  To: Arthur Miller; +Cc: emacs-devel

Arthur Miller <arthur.miller@live.com> writes:

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

Hi Arthur,

I like this.  I'm often reading some documentation while testing things
into the current buffer so I think I would be a great addition.  I'm
testing your patch now (and will report any issue if needed).

As a potential future user, I have some comments/requests:

     - Could it be extend to Man buffers?  I think it would even be more
       useful than *Help* buffers which are short most of the time (or
       maybe it is just me).

     - Is it possible to have repeat mode for this?  So one could do
       "C-h M-i SPC SPC SPC DEL" to browse some info pages.
       
     - Could we have a "umbrella" keybinding that does this for some
       common denominator functions the other "documentation" buffer
       (be it *info*, *Help* or a man page)?

Best regards,
-- 
Manuel Giraud



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

* Re: Control help- and Info-mode buffers from other buffers
  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
  0 siblings, 2 replies; 62+ messages in thread
From: Arthur Miller @ 2023-05-30 13:31 UTC (permalink / raw)
  To: Manuel Giraud; +Cc: emacs-devel

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

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

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

Hi Manuel;

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

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

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

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

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

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

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

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

Basicly yes. The idea is:

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

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

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

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

> Best regards,

Best regards and thanks for testing!


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

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

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

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


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

* Re: Control help- and Info-mode buffers from other buffers
  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
  1 sibling, 0 replies; 62+ messages in thread
From: Manuel Giraud via Emacs development discussions. @ 2023-05-30 15:22 UTC (permalink / raw)
  To: Arthur Miller; +Cc: emacs-devel

Arthur Miller <arthur.miller@live.com> writes:

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

I'm testing this one but I think SPC should be 'help-scroll-up-command',
no?

[...]

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

Yes, you're right: Man buffers do not have a unique name.  Maybe this
could be done later.

[...]

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

I, myself, have activated '(repeat-mode)' and I think your new bindings
are good candidate for it.  It could also come into another patch after
this one.

[...]

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

Ok, I think I start to see your motivations for this patch then.  And I
do think that having this in place for *Help* buffers and *info* makes
sense.
-- 
Manuel Giraud



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

* Re: Control help- and Info-mode buffers from other buffers
  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 16:15 ` Eli Zaretskii
  2023-05-31  6:38   ` Arthur Miller
  2023-05-30 18:04 ` [External] : " Drew Adams
  2 siblings, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-05-30 16:15 UTC (permalink / raw)
  To: Arthur Miller; +Cc: emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Date: Tue, 30 May 2023 07:38:54 +0200
> 
> 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.

This is a useful feature, especially the *Help* buffer part (I tend to
have a separate frame for Info manuals visible at all times).  But to
be really useful, the key bindings must be very easy and convenient to
type, because otherwise people will keep switching to the *Help*
window and doing it from there.  And "C-h M-h" is not easy to type.  I
hope we will be able to find an easier-typed prefix.

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

This is actually unfortunate.  I'd suggest that you rewrite those
parts from scratch, using just the general ideas, because otherwise I
envision it to be hard for us to accept the code.

> 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?

Why does it matter whether a *Help* or *Info* buffer is shown on
another frame?  Emacs can be told to select a window showing a buffer
either on the selected frame, or on any frame, AFAIR.

Thanks.



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

* Re: Control help- and Info-mode buffers from other buffers
  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
  1 sibling, 1 reply; 62+ messages in thread
From: Juri Linkov @ 2023-05-30 17:29 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Manuel Giraud, emacs-devel

> I have actually tried to find some general way to do this
> programmatically with any buffer, but thus far, I don't see any
> general way that is both efficient and 100% failsafe.

Please share your experience what have you tried
since it would be nice to have a general function
that will delegate key presses to any window,
not just the hard-coded *Help* and *Info*.



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

* RE: [External] : Control help- and Info-mode buffers from other buffers
  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 16:15 ` Eli Zaretskii
@ 2023-05-30 18:04 ` Drew Adams
  2023-05-31  6:06   ` Arthur Miller
  2 siblings, 1 reply; 62+ messages in thread
From: Drew Adams @ 2023-05-30 18:04 UTC (permalink / raw)
  To: Arthur Miller, emacs-devel@gnu.org

Apologies for not taking the time to look into the details of what you propose.  Some quick feedback:
___

As one user, I use (special-display) frames for *Help*, *info*, *Man*, *Completions*, etc. (all buffers named `*...*', in fact).

1. Please make your feature work with (such) frames, and not just with windows in the selected frame.

E.g., pass a non-nil ALL-FRAMES arg to `get-buffer-window'.  (The arg should be 0, `visible', or t, not a frame.  I'd vote for t.)

2. FWIW, in my use *Help* is no different from *info* (and *Man __*), in the sense that I can have multiple Help buffers just as I can have multiple Info (and *Man __* buffers).

Only one of the Help buffers is named *Help*, just as only one of the Info buffers is named *info* (e.g. `M-n' in Info gives you *info*<N> etc.).  A Help command shows buffer *Help*, an Info command shows buffer *info*, etc.

3. I think that this kind of error msg might need correcting, depending on what non-nil value you decide to pass to `get-buffer-window':

  "There is no visible Info buffer"
___

4. FWIW, it would be much easier to eyeball your patch if you used a unified diff (`diff -u').  There's a lot of code that is, I think, unchanged other than being wrapped with some code - only the wrapper was added.  But it's hard to see the real differences (the wrappers).  Eyeballing the patch helps those (like me) who have code similar to that in, say, info.el and will need to update it to accommodate this change.

Thx.





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

* Re: Control help- and Info-mode buffers from other buffers
  2023-05-30 17:29     ` Juri Linkov
@ 2023-05-31  5:55       ` Arthur Miller
  2023-05-31 17:13         ` Juri Linkov
  0 siblings, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-05-31  5:55 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Manuel Giraud, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> I have actually tried to find some general way to do this
>> programmatically with any buffer, but thus far, I don't see any
>> general way that is both efficient and 100% failsafe.
>
> Please share your experience what have you tried

Hi Juri;

I have tried several quite different things, so that would be quite a long mail
:-).

In that particular place, I am actually referring to something different
then what you think of, I believe. As I wrote to Manuel, I was basically doing
programmatic what I have done manually with the patch. Problem with the programmatic way
was too much processing, and some functions didn't work. The former could be
perhaps helped by using a macro to do it at compile time, and the latter is
perhaps lack of lisp-fu on my side. I don't know, but the idea was to advise the
already prefixed commands in a mode-map, and to generate new commands for those
that are not prefixed. The advice switches to the buffer, executes the original
commands, and switches back. Something like this (just the advising and generation part):

#+begin_src emacs-lisp
(defun remote--make-advice-body (buffer-name)
  "Create lambda function for buffer with name BUFFER-NAME."
  #'(lambda (fn &rest args)
      (let ((previous (selected-window))
            (buffer (get-buffer buffer-name))
            (window (get-buffer-window buffer-name)))
        (unwind-protect
            (when (get-buffer buffer)
              (unless (window-live-p window)
                (display-buffer buffer))
              (select-window window)
              (apply #'call-interactively fn args))
          (select-window previous)))))

(defun remote--make-advice-function (buffer-name)
  "Create advice function for buffer with name BUFFER-NAME."
  (let ((advice-name
         (intern (format "help-remote--%s-advice-fn" buffer-name))))
    (unless (fboundp advice-name)
      (defalias advice-name (remote--make-advice-body buffer-name)))
    advice-name))

(defun remote--advise (fn buffer-name advised)
  "Advice function FN to run in buffer with name BUFFER-NAME."
  (let ((advice (remote--make-advice-function buffer-name)))
    (add-to-list advised (cons fn advice))
    (advice-add fn :around advice)))

(defun remote--gen-cons (binding prefix buffer-name advised)
  "Ensure BINDING in buffer named BUFFER-NAME uses symbol prefixed with PREFIX."
  (let ((name (symbol-name (cdr binding)))
        (symb (cdr binding)))
    (unless (string-match-p prefix name)
      (setq symb (intern (concat prefix name)))
      (defalias symb (symbol-function (cdr binding))))
    (remote--advise symb buffer-name advised)
    (cons (car binding) symb)))
#+end_src

However I was not able to trick Emacs to trust me
its executing in the "correct" environment. Also, there was too much
processing in info mode. Some methods worked well; for example everything in
help-mode, but some functions that asks for user input in info-mode, for example
info-menu, didn't work at all.

> since it would be nice to have a general function
> that will delegate key presses to any window,

I think it is similar, but still a different problem then remotely executing 
commands available in other buffer. To start with there is a nice trick
via pre/post command hooks, someone posted to me on Reddit:

https://www.reddit.com/r/emacs/comments/x0r0pe/share_your_otherwindow_commands/

It works quite well in some cases, but not in all. For example it does not work
with Info-menu when invoked from other buffer either.

> not just the hard-coded *Help* and *Info*.

I understand  your sentiment, and I agree that it might be useful to have
a way to delegate input to other window, but I also think we would still like
to wrap such generic command into some hard-coded cases to save typing. For
example in case of generic command and multiple windows, you will have to ask
user which window to execute in. In order to save that prompting and additional
typing on user part, it is handy to hard code such generic function to certain
cases like help and info here.

To shorten, I haven't found a good way to delegate input to other windows and I
am not sure if it will even work as a general idea at all. Perhaps for some
cases, it would work, but not for all. I am inclined to believe that this is the
wrong side to attack the problem. Perhaps it is better to write commands that
are aware that they could be executed from other buffers than the one they act on?
In that case command could prompt the user for a window or buffer to act on and
auto switch to that window and back. Since the command usually knows which mode
it belongs, it could even filter out windows/buffer that does not have that
mode activated, or some other feature it requires. It would be relatively simple
to implement with prefix-argument?

But perhaps I am wrong too. Perhaps  you have something different? :)




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

* Re: [External] : Control help- and Info-mode buffers from other buffers
  2023-05-30 18:04 ` [External] : " Drew Adams
@ 2023-05-31  6:06   ` Arthur Miller
  2023-05-31 13:00     ` Drew Adams
  0 siblings, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-05-31  6:06 UTC (permalink / raw)
  To: Drew Adams; +Cc: emacs-devel@gnu.org

Drew Adams <drew.adams@oracle.com> writes:

> Apologies for not taking the time to look into the details of what you propose.  Some quick feedback:
> ___
>
> As one user, I use (special-display) frames for *Help*, *info*, *Man*, *Completions*, etc. (all buffers named `*...*', in fact).
>
> 1. Please make your feature work with (such) frames, and not just with windows in the selected frame.
>
> E.g., pass a non-nil ALL-FRAMES arg to `get-buffer-window'.  (The arg should be 0, `visible', or t, not a frame.  I'd vote for t.)

Thank you Drew, that was actually very useful tip; I will take a look and see if
I can rework the patch.

> 2. FWIW, in my use *Help* is no different from *info* (and *Man __*), in the sense that I can have multiple Help buffers just as I can have multiple Info (and *Man __* buffers).
>
> Only one of the Help buffers is named *Help*, just as only one of the Info buffers is named *info* (e.g. `M-n' in Info gives you *info*<N> etc.).  A Help command shows buffer *Help*, an Info command shows buffer *info*, etc.

I undersand,and I am aware that different *info* windows are numbered, like
other buffers. To me it seems that the name is matched by prefix, not by entire
name. I am not sure if *info* will always be matched before *info*<1> when
get-buffer or get-buffer-window is called. I am not sure though what alternative
would be, like prompting user with completing read? I am not sure that is a good
alternative either. In case of multiple windows I suggest the workflow remains
same as now (user manually switches to buffer).

> 3. I think that this kind of error msg might need correcting, depending on what non-nil value you decide to pass to `get-buffer-window':
>
>   "There is no visible Info buffer"
> ___

I agree. I gladly take suggestions :).

> 4. FWIW, it would be much easier to eyeball your patch if you used a unified
> diff (`diff -u').  

I have used: git format-patch -1, which is very simple for me, but I have no
problem running some other command if you give me the command. diff -u is
complete command?

>                    There's a lot of code that is, I think, unchanged other than
> being wrapped with some code - only the wrapper was added.

Pretty much so; basically most of those are just wrapped into
with-selected-window. Few have it done manually, but basically it is all the same
principle.

Thank you for suggestions; if you have more useful stuff I am glad to hear.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-05-30 16:15 ` Eli Zaretskii
@ 2023-05-31  6:38   ` Arthur Miller
  0 siblings, 0 replies; 62+ messages in thread
From: Arthur Miller @ 2023-05-31  6:38 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Arthur Miller <arthur.miller@live.com>
>> Date: Tue, 30 May 2023 07:38:54 +0200
>> 
>> 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.
>
> This is a useful feature, especially the *Help* buffer part (I tend to
> have a separate frame for Info manuals visible at all times).  But to
> be really useful, the key bindings must be very easy and convenient to
> type, because otherwise people will keep switching to the *Help*
> window and doing it from there.

I don't think it is a problem; it should still be valid workflow to do things in
help buffer itself. I see this just as a helper to help diminish some
switching. Those two jump functions are particullary for cases where user would
like to do several actions in help/info buffer. In that case simplicity of  a
single key probably wins. But I do agree that simple to type prefix is to prefer.

> window and doing it from there.  And "C-h M-h" is not easy to type.  I
> hope we will be able to find an easier-typed prefix.

I have used at some point C-h h for help-mode-map and C-h g for Info-mode-map,
but I am not sure those are much better. It still is 4 letter to type at the end.

>> 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).
>
> This is actually unfortunate.  I'd suggest that you rewrite those
> parts from scratch, using just the general ideas, because otherwise I
> envision it to be hard for us to accept the code.

It is basically just two define-key statements, five lines all-in-all; not much
to re-write. I'll see if there is something similar somewhere in Emacs, unless
the writer of that lambda does not want to step in and donate that code to Emacs? :)

>> 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?
>
> Why does it matter whether a *Help* or *Info* buffer is shown on
> another frame?  Emacs can be told to select a window showing a buffer
> either on the selected frame, or on any frame, AFAIR.

Yes, I tottally understand that :). That was mostly because I am not so familiar
with Emacs on multiple frames and that part of the API.Second reason is just the
tune ut takes to test all of it. Drew hinted me where to look, so I'll take a try. 

> Thanks.

Thanks for looking at it.



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

* RE: [External] : Control help- and Info-mode buffers from other buffers
  2023-05-31  6:06   ` Arthur Miller
@ 2023-05-31 13:00     ` Drew Adams
  2023-05-31 13:27       ` Arthur Miller
  0 siblings, 1 reply; 62+ messages in thread
From: Drew Adams @ 2023-05-31 13:00 UTC (permalink / raw)
  To: Arthur Miller; +Cc: emacs-devel@gnu.org

> > 4. FWIW, it would be much easier to eyeball your
> > patch if you used a unified diff (`diff -u').
> 
> I have used: git format-patch -1, which is very simple for me, but I have
> no problem running some other command if you give me the command.
> diff -u is complete command?

Sorry, I'm witless about git (aka gitless).
Maybe someone else can advise you about that.



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

* Re: [External] : Control help- and Info-mode buffers from other buffers
  2023-05-31 13:00     ` Drew Adams
@ 2023-05-31 13:27       ` Arthur Miller
  0 siblings, 0 replies; 62+ messages in thread
From: Arthur Miller @ 2023-05-31 13:27 UTC (permalink / raw)
  To: Drew Adams; +Cc: emacs-devel@gnu.org

Drew Adams <drew.adams@oracle.com> writes:

>> > 4. FWIW, it would be much easier to eyeball your
>> > patch if you used a unified diff (`diff -u').
>> 
>> I have used: git format-patch -1, which is very simple for me, but I have
>> no problem running some other command if you give me the command.
>> diff -u is complete command?
>
> Sorry, I'm witless about git (aka gitless).

:-) It is ok.

Git format-patch is extremely easy to use with multiple files, I don't have to
specifay each file to diff; git does everything behind the back comparing
changed files in different worktrees/branches; but yes it makes for a big file.

I had thoughts to make different patch, one for help one for info, for that very
same reason, but the convenience (just another word for laziness) won. I also
forgett which file goes first to diff, the new one or the old one too :).




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

* Re: Control help- and Info-mode buffers from other buffers
  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:31           ` Juri Linkov
  0 siblings, 2 replies; 62+ messages in thread
From: Juri Linkov @ 2023-05-31 17:13 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Manuel Giraud, emacs-devel

>> since it would be nice to have a general function
>> that will delegate key presses to any window,
>
> I think it is similar, but still a different problem then remotely executing 
> commands available in other buffer. To start with there is a nice trick
> via pre/post command hooks, someone posted to me on Reddit:
>
> https://www.reddit.com/r/emacs/comments/x0r0pe/share_your_otherwindow_commands/

A hook-based approach looks promising.

When such simplest prototype that demonstrates this solution
conceptually works well

  (progn
    (add-hook 'pre-command-hook (lambda () (other-window 1)))
    (add-hook 'post-command-hook (lambda () (other-window -1))))

then it could be extended by adding customization, etc.

> It works quite well in some cases, but not in all. For example it does not work
> with Info-menu when invoked from other buffer either.

If the code above doesn't work in all cases, this means we have a bug.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-05-31 17:13         ` Juri Linkov
@ 2023-06-01  3:16           ` Arthur Miller
  2023-06-01  6:35             ` Juri Linkov
  2023-06-01  6:31           ` Juri Linkov
  1 sibling, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-01  3:16 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Manuel Giraud, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>>> since it would be nice to have a general function
>>> that will delegate key presses to any window,
>>
>> I think it is similar, but still a different problem then remotely executing 
>> commands available in other buffer. To start with there is a nice trick
>> via pre/post command hooks, someone posted to me on Reddit:
>>
>> https://www.reddit.com/r/emacs/comments/x0r0pe/share_your_otherwindow_commands/
>
> A hook-based approach looks promising.
>
> When such simplest prototype that demonstrates this solution
> conceptually works well
>
>   (progn
>     (add-hook 'pre-command-hook (lambda () (other-window 1)))
>     (add-hook 'post-command-hook (lambda () (other-window -1))))
>
> then it could be extended by adding customization, etc.
>
>> It works quite well in some cases, but not in all. For example it does not work
>> with Info-menu when invoked from other buffer either.
>
> If the code above doesn't work in all cases, this means we have a bug.

You are conceptually definitely correct :), but I believe that we are in this case
treating symptoms instead of the cause.

The reason we would like to send input to other-window (or any window) is to be
able to invoke some command in that window. Why can't we just invoke the command
directly, via M-x, when anything bound to a key is an interactive command by
definition? The reason is that commands are written with (or without) some
assumptions. The most notorius here is that many commands are written to act in
current buffer on selected window. It is not an unreasonable assumption, I am
just saying that commands are ordinary lisp code that can do anything, and
if they are not written with assumption to be run in some other buffer or
window, than there can be problems.I am not sure I would call it a bug, this was
just a natural assumption to make commands act on selected window and
current-buffer, but in certain cases, it is indeed a limitation, or perhaps
better to say, a noise, since it causes this extra switching between windows.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-05-31 17:13         ` Juri Linkov
  2023-06-01  3:16           ` Arthur Miller
@ 2023-06-01  6:31           ` Juri Linkov
  1 sibling, 0 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-01  6:31 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Manuel Giraud, emacs-devel

>   (progn
>     (add-hook 'pre-command-hook (lambda () (other-window 1)))
>     (add-hook 'post-command-hook (lambda () (other-window -1))))

Sorry, this is wrong, it tried to run the same command
that was bound in the original window buffer.

This is the correct implementation that works in all cases:

#+begin_src emacs-lisp
(progn
  (add-hook 'pre-command-hook
            (lambda ()
              (other-window 1)
              (setq this-command (key-binding (this-command-keys)))))
  (add-hook 'post-command-hook (lambda () (other-window -1))))
#+end_src



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  3:16           ` Arthur Miller
@ 2023-06-01  6:35             ` Juri Linkov
  2023-06-01  7:05               ` Eli Zaretskii
  2023-06-01  8:50               ` Arthur Miller
  0 siblings, 2 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-01  6:35 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Manuel Giraud, emacs-devel

> Why can't we just invoke the command directly, via M-x, when anything
> bound to a key is an interactive command by definition? The reason is
> that commands are written with (or without) some assumptions.

Because in this case you need to modify all commands to switch windows
explicitly.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  6:35             ` Juri Linkov
@ 2023-06-01  7:05               ` Eli Zaretskii
  2023-06-01  7:20                 ` Juri Linkov
  2023-06-01  8:50               ` Arthur Miller
  1 sibling, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-01  7:05 UTC (permalink / raw)
  To: Juri Linkov; +Cc: arthur.miller, manuel, emacs-devel

> From: Juri Linkov <juri@linkov.net>
> Cc: Manuel Giraud <manuel@ledu-giraud.fr>,  emacs-devel@gnu.org
> Date: Thu, 01 Jun 2023 09:35:09 +0300
> 
> > Why can't we just invoke the command directly, via M-x, when anything
> > bound to a key is an interactive command by definition? The reason is
> > that commands are written with (or without) some assumptions.
> 
> Because in this case you need to modify all commands to switch windows
> explicitly.

What about using the "C-x 4" prefix?  We could define a new prefix map
bound to "C-x 4 h", then have all the bindings in *Help* be in the map
prefixed by "C-x 4 h".  For example, "C-x 4 h I" will run a command
which will produce the same effect as help-goto-lispref-info, using
the current *Help* window as its starting point.

We could support "C-x 5 h" prefixed key sequences in a similar manner,
for people who have Info buffers or *Help* buffers in another frame.

WDYT?



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

* Re: Control help- and Info-mode buffers from other buffers
  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:16                   ` Arthur Miller
  0 siblings, 2 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-01  7:20 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: arthur.miller, manuel, emacs-devel

> What about using the "C-x 4" prefix?  We could define a new prefix map
> bound to "C-x 4 h", then have all the bindings in *Help* be in the map
> prefixed by "C-x 4 h".  For example, "C-x 4 h I" will run a command
> which will produce the same effect as help-goto-lispref-info, using
> the current *Help* window as its starting point.
>
> We could support "C-x 5 h" prefixed key sequences in a similar manner,
> for people who have Info buffers or *Help* buffers in another frame.
>
> WDYT?

'C-x 4 h' and 'C-x 5 h' are quite mnemonic and convenient to type.
What I tried to do is to adapt this to windmove to support a window
in any direction, but in any case they all could use the same
internal general function.

So this allow to support more commands than the currently limited set of
scroll-other-window, scroll-other-window-down, recenter-other-window,
beginning-of-buffer-other-window, end-of-buffer-other-window.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  6:35             ` Juri Linkov
  2023-06-01  7:05               ` Eli Zaretskii
@ 2023-06-01  8:50               ` Arthur Miller
  2023-06-01 10:04                 ` Eli Zaretskii
  2023-06-01 16:39                 ` Juri Linkov
  1 sibling, 2 replies; 62+ messages in thread
From: Arthur Miller @ 2023-06-01  8:50 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Manuel Giraud, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> Why can't we just invoke the command directly, via M-x, when anything
>> bound to a key is an interactive command by definition? The reason is
>> that commands are written with (or without) some assumptions.
>
> Because in this case you need to modify all commands to switch windows
> explicitly.

It was a rhetorical question, not something I really was asking you, and I gave
the very same answer after that one :).

Anyway, pre/post hook hack is very useful, and works with many commands, but not
with all, so it is not 100% failsafe and general.Try to execute Info-mode
from other window but Info (shortcut 'm'). In my Emacs it does not work.

My point was that commands to be written with this in mind.

Another problem with the above is that this will work only for 'other-window'
for which Emacs has an algorithm to figure out which window it is. But what when
you have more then two windows, and wish to switch to a different window than
what Emacs considers as 'other-window'? You will have to prompt the user, to
choose one. The help or info buffers are seldom the 'other-buffer' so constanly
choosing the window would be just as annoying as constantly switching to and
from, in my opinion.

I would prefer if there was a code gen in form of a macro, as suggested, similar
to define-minor-mode, that does this switching on pre-defined prefixes so that
we get uniformity, and helps people write commands so they work from
anywhere. Since it is not possible to completely automate it, perhaps lisp
manual could mention how to write commands so they are callable from other
windows then just selected one.

I don't say you should not include the pre/post hack into Emacs if you want, it
is better then what already is there for the similar purpose (don't remember
longer the one included since I don't use it). I am just saying that we should
perhaps write better commands in the future, so we don't need the hack :).



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  7:20                 ` Juri Linkov
@ 2023-06-01  9:03                   ` Arthur Miller
  2023-06-01  9:55                     ` Eli Zaretskii
  2023-06-01  9:16                   ` Arthur Miller
  1 sibling, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-01  9:03 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Eli Zaretskii, manuel, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> What about using the "C-x 4" prefix?  We could define a new prefix map
>> bound to "C-x 4 h", then have all the bindings in *Help* be in the map
>> prefixed by "C-x 4 h".  For example, "C-x 4 h I" will run a command
>> which will produce the same effect as help-goto-lispref-info, using
>> the current *Help* window as its starting point.

If you are talking about help-map and help-mode-map, the help-map is already
bound to C-h, so you could then add help-mode-map on a 'h' key in help-mode as I
tested while I working with this. It gives you C-h h l for
help-goto-lispref-info which is one key shorter and it is easier to type then
C-x 4 h since you can just tapp 'h' twice instead of using another key.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  7:20                 ` Juri Linkov
  2023-06-01  9:03                   ` Arthur Miller
@ 2023-06-01  9:16                   ` Arthur Miller
  2023-06-01  9:58                     ` Eli Zaretskii
  1 sibling, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-01  9:16 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Eli Zaretskii, manuel, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> What about using the "C-x 4" prefix?  We could define a new prefix map
>> bound to "C-x 4 h", then have all the bindings in *Help* be in the map
>> prefixed by "C-x 4 h".  For example, "C-x 4 h I" will run a command
>> which will produce the same effect as help-goto-lispref-info, using
>> the current *Help* window as its starting point.
>>
>> We could support "C-x 5 h" prefixed key sequences in a similar manner,
>> for people who have Info buffers or *Help* buffers in another frame.
>>
>> WDYT?
>
> 'C-x 4 h' and 'C-x 5 h' are quite mnemonic and convenient to type.
> What I tried to do is to adapt this to windmove to support a window
> in any direction, but in any case they all could use the same
> internal general function.
>
> So this allow to support more commands than the currently limited set of
> scroll-other-window, scroll-other-window-down, recenter-other-window,
> beginning-of-buffer-other-window, end-of-buffer-other-window.

I forgott to type in previous mail:

In general, (I think) that allow to support commands that are available in
selected-window, to be executed in other-window, but does not have access to
commands defined in other-window, and naturally we can't executed commands from
selected-window if they are not supported in other-window. With other words, if
set of commands from selected window is S and set of commands in other-window is
O, than this will work in on the intersect of those commands, S∩O. Perhaps there
is more to it, but I think that is the minimal requirement (the commands has to
work on both buffers).

Potentially it can also result in many bug repports and /r/Emacs threads where
people wonder why it does not work some command Foo in other buffer.





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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  9:03                   ` Arthur Miller
@ 2023-06-01  9:55                     ` Eli Zaretskii
  2023-06-01 14:01                       ` Arthur Miller
  0 siblings, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-01  9:55 UTC (permalink / raw)
  To: Arthur Miller; +Cc: juri, manuel, emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Cc: Eli Zaretskii <eliz@gnu.org>,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
> Date: Thu, 01 Jun 2023 11:03:57 +0200
> 
> Juri Linkov <juri@linkov.net> writes:
> 
> >> What about using the "C-x 4" prefix?  We could define a new prefix map
> >> bound to "C-x 4 h", then have all the bindings in *Help* be in the map
> >> prefixed by "C-x 4 h".  For example, "C-x 4 h I" will run a command
> >> which will produce the same effect as help-goto-lispref-info, using
> >> the current *Help* window as its starting point.
> 
> If you are talking about help-map and help-mode-map, the help-map is already
> bound to C-h, so you could then add help-mode-map on a 'h' key in help-mode as I
> tested while I working with this. It gives you C-h h l for
> help-goto-lispref-info which is one key shorter and it is easier to type then
> C-x 4 h since you can just tapp 'h' twice instead of using another key.

C-h map is quite full, and "C-h h" is already taken, so this proposal
is a no-go.

I suggested "C-x 4" because it is a prefix we use for many "do stuff
in other window" commands, and this is a logical extension of that
repertoire.  "C-x 4" is also convenient to type, and people who, like
me, are used to invoke many "other-window" commands will get used to
it instantly.  Most importantly, "C-x 4 h" is free for grabbing.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  9:16                   ` Arthur Miller
@ 2023-06-01  9:58                     ` Eli Zaretskii
  2023-06-01 13:45                       ` Arthur Miller
  0 siblings, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-01  9:58 UTC (permalink / raw)
  To: Arthur Miller; +Cc: juri, manuel, emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Cc: Eli Zaretskii <eliz@gnu.org>,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
> Date: Thu, 01 Jun 2023 11:16:50 +0200
> 
> In general, (I think) that allow to support commands that are available in
> selected-window, to be executed in other-window, but does not have access to
> commands defined in other-window, and naturally we can't executed commands from
> selected-window if they are not supported in other-window. With other words, if
> set of commands from selected window is S and set of commands in other-window is
> O, than this will work in on the intersect of those commands, S∩O. Perhaps there
> is more to it, but I think that is the minimal requirement (the commands has to
> work on both buffers).

I'm sorry, I don't understand what you are trying to say here.  What
exactly is the minimum requirement, and why?

> Potentially it can also result in many bug repports and /r/Emacs threads where
> people wonder why it does not work some command Foo in other buffer.

If we have a good reason why it doesn't work, we will explain it.  And
if we don't have a good reason, we can add another binding.  Where's
the problem?



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

* Re: Control help- and Info-mode buffers from other buffers
  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
  1 sibling, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-01 10:04 UTC (permalink / raw)
  To: Arthur Miller; +Cc: juri, manuel, emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Cc: Manuel Giraud <manuel@ledu-giraud.fr>,  emacs-devel@gnu.org
> Date: Thu, 01 Jun 2023 10:50:36 +0200
> 
> Another problem with the above is that this will work only for 'other-window'
> for which Emacs has an algorithm to figure out which window it is. But what when
> you have more then two windows, and wish to switch to a different window than
> what Emacs considers as 'other-window'? You will have to prompt the user, to
> choose one.

Not necessarily: since the commands know they are about *Help* and
*info* buffers, they could find the "correct" window themselves.  (The
case of more than one *Help* window on the same frame is rare enough
to be ignored, I think.)



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01 10:04                 ` Eli Zaretskii
@ 2023-06-01 11:33                   ` Arthur Miller
  0 siblings, 0 replies; 62+ messages in thread
From: Arthur Miller @ 2023-06-01 11:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: juri, manuel, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Arthur Miller <arthur.miller@live.com>
>> Cc: Manuel Giraud <manuel@ledu-giraud.fr>,  emacs-devel@gnu.org
>> Date: Thu, 01 Jun 2023 10:50:36 +0200
>> 
>> Another problem with the above is that this will work only for 'other-window'
>> for which Emacs has an algorithm to figure out which window it is. But what when
>> you have more then two windows, and wish to switch to a different window than
>> what Emacs considers as 'other-window'? You will have to prompt the user, to
>> choose one.
>
> Not necessarily: since the commands know they are about *Help* and
> *info* buffers, they could find the "correct" window themselves.

Yes, that is what I was doing with the patch, making all help and info commands
awae of which window they should act on, and when I talk that commands should be
written so they aware of which window they run in. 

But as I understand Juri, he is trying to make a general approach that works in
all cases, with all commands. So is how I interpret his first message about
general method to send input to any window. Perhaps I misunderstand Juri, but my
answer was about that general case, when any commands could possibly be sent to
any window.




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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  9:58                     ` Eli Zaretskii
@ 2023-06-01 13:45                       ` Arthur Miller
  2023-06-01 16:19                         ` Eli Zaretskii
  0 siblings, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-01 13:45 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: juri, manuel, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Arthur Miller <arthur.miller@live.com>
>> Cc: Eli Zaretskii <eliz@gnu.org>,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
>> Date: Thu, 01 Jun 2023 11:16:50 +0200
>> 
>> In general, (I think) that allow to support commands that are available in
>> selected-window, to be executed in other-window, but does not have access to
>> commands defined in other-window, and naturally we can't executed commands from
>> selected-window if they are not supported in other-window. With other words, if
>> set of commands from selected window is S and set of commands in other-window is
>> O, than this will work in on the intersect of those commands, S∩O. Perhaps there
>> is more to it, but I think that is the minimal requirement (the commands has to
>> work on both buffers).
>
> I'm sorry, I don't understand what you are trying to say here.  What
> exactly is the minimum requirement, and why?

If I try to illustrate with an example: consider a two-window setup with scratch
buffer in first and info buffer in second window. If presses a key to execute
next command in other window and then 'm'the system will choose commands from
selected window (self-insert command in scratch buffer) and try to execute that
command in other window which is info buffer. Tbat is obviously wrong since user
probably meant to execute Info-menu, by default bound to 'm' in Info-mode.

I don't know if that can be fixed by perhaps manipulating unrread-key-events
list or in some other way, perhaps by putting input key back, or by some other
means. I haven't managed to get it correctly and have given up and instead tre
to address the problem in more fundamental way by making those functions aware
of the context they execute in. Also, even if problems could be fixed, I see no
point of using such method in this particular patch.

Sure, the hack has definitely a value on its own, and please, include it in
Emacs if you get some good understanding and robust error handling for it.

But for the suggested patch for help and info, I see no reason to use pre/post
hook to switch windows, I can just use with-selected-window which does 
exactly the same switching for me in 90% of cases, and manually fix the few
cases which needed some extra care.

>> Potentially it can also result in many bug repports and /r/Emacs threads where
>> people wonder why it does not work some command Foo in other buffer.
>
> If we have a good reason why it doesn't work, we will explain it.  And
> if we don't have a good reason, we can add another binding.  Where's
> the problem?

Yes, I understand that, and that is why I am also discussing it and trying to
bring to the table what is my understanding of the process involved. I don't
consider myself to understand everything going on in interaction between
commands and pre/post command hooks.

For example what happends if a command call
another command and it happen to have post hooks? Will it run after the second
command call internally (I think it will), which means it will switch back to
old window prematurely. 






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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  9:55                     ` Eli Zaretskii
@ 2023-06-01 14:01                       ` Arthur Miller
  0 siblings, 0 replies; 62+ messages in thread
From: Arthur Miller @ 2023-06-01 14:01 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: juri, manuel, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Arthur Miller <arthur.miller@live.com>
>> Cc: Eli Zaretskii <eliz@gnu.org>,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
>> Date: Thu, 01 Jun 2023 11:03:57 +0200
>> 
>> Juri Linkov <juri@linkov.net> writes:
>> 
>> >> What about using the "C-x 4" prefix?  We could define a new prefix map
>> >> bound to "C-x 4 h", then have all the bindings in *Help* be in the map
>> >> prefixed by "C-x 4 h".  For example, "C-x 4 h I" will run a command
>> >> which will produce the same effect as help-goto-lispref-info, using
>> >> the current *Help* window as its starting point.
>> 
>> If you are talking about help-map and help-mode-map, the help-map is already
>> bound to C-h, so you could then add help-mode-map on a 'h' key in help-mode as I
>> tested while I working with this. It gives you C-h h l for
>> help-goto-lispref-info which is one key shorter and it is easier to type then
>> C-x 4 h since you can just tapp 'h' twice instead of using another key.
>
> C-h map is quite full, and "C-h h" is already taken, so this proposal
> is a no-go.
>
> I suggested "C-x 4" because it is a prefix we use for many "do stuff
> in other window" commands, and this is a logical extension of that
> repertoire.  "C-x 4" is also convenient to type, and people who, like
> me, are used to invoke many "other-window" commands will get used to
> it instantly.  Most importantly, "C-x 4 h" is free for grabbing.

Yes, I understand that.

I personally find anything involving numbers to be slower to type then non-number
stuff, but might be me. However free for grabbing sounds like an unbeatable
argument in Emacs community, so sure; I said earlier I don't care personally, I
will anyway rebind it myself, so it was just a suggestion to rebind
hello-world-file to something else :).





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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01 13:45                       ` Arthur Miller
@ 2023-06-01 16:19                         ` Eli Zaretskii
  2023-06-02  1:26                           ` Arthur Miller
  0 siblings, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-01 16:19 UTC (permalink / raw)
  To: Arthur Miller; +Cc: juri, manuel, emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
> Date: Thu, 01 Jun 2023 15:45:53 +0200
> 
> If I try to illustrate with an example: consider a two-window setup with scratch
> buffer in first and info buffer in second window. If presses a key to execute
> next command in other window and then 'm'the system will choose commands from
> selected window (self-insert command in scratch buffer) and try to execute that
> command in other window which is info buffer. Tbat is obviously wrong since user
> probably meant to execute Info-menu, by default bound to 'm' in Info-mode.
> 
> I don't know if that can be fixed by perhaps manipulating unrread-key-events
> list or in some other way, perhaps by putting input key back, or by some other
> means. I haven't managed to get it correctly and have given up and instead tre
> to address the problem in more fundamental way by making those functions aware
> of the context they execute in. Also, even if problems could be fixed, I see no
> point of using such method in this particular patch.

I don't understand why you see such complexity.  All we need is a
command that will do

  (with-selected-window (get-buffer-window "*info*")
    BODY...

and then we need to bind it to, say, "C-x 4 h m".  Did I miss
something important?

> But for the suggested patch for help and info, I see no reason to use pre/post
> hook to switch windows, I can just use with-selected-window which does 
> exactly the same switching for me in 90% of cases, and manually fix the few
> cases which needed some extra care.

I didn't say anything about any hooks.

> For example what happends if a command call
> another command and it happen to have post hooks? Will it run after the second
> command call internally (I think it will), which means it will switch back to
> old window prematurely. 

Any post-command-hook should be prepared to deal with commands that
switch buffers and windows, so I see no new problems here.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01  8:50               ` Arthur Miller
  2023-06-01 10:04                 ` Eli Zaretskii
@ 2023-06-01 16:39                 ` Juri Linkov
  2023-06-01 19:15                   ` Eli Zaretskii
  2023-06-02  1:10                   ` Arthur Miller
  1 sibling, 2 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-01 16:39 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Manuel Giraud, emacs-devel

> Anyway, pre/post hook hack is very useful, and works with many commands, but not
> with all, so it is not 100% failsafe and general.Try to execute Info-mode
> from other window but Info (shortcut 'm'). In my Emacs it does not work.

I tried 'm' (Info-menu), and it works nicely without any problem.
As long as key prefixes are the same in both windows.

> Another problem with the above is that this will work only for 'other-window'
> for which Emacs has an algorithm to figure out which window it is. But what when
> you have more then two windows, and wish to switch to a different window than
> what Emacs considers as 'other-window'? You will have to prompt the user, to
> choose one. The help or info buffers are seldom the 'other-buffer' so constanly
> choosing the window would be just as annoying as constantly switching to and
> from, in my opinion.

Indeed, which window to use is a separate question.

For the existing commands scroll-other-window, scroll-other-window-down,
recenter-other-window, beginning-of-buffer-other-window,
end-of-buffer-other-window, the user option that defines which window to use
is 'other-window-scroll-default', and it can be customized
to any function, for example, a function that looks for
a window with a Help/Info buffer on the current frame,
or on any other frame.  Or to a function that uses
'get-mru-window' to get the most recently used/displayed window.
All this is customizable.

> I would prefer if there was a code gen in form of a macro, as suggested, similar
> to define-minor-mode, that does this switching on pre-defined prefixes so that
> we get uniformity, and helps people write commands so they work from
> anywhere. Since it is not possible to completely automate it, perhaps lisp
> manual could mention how to write commands so they are callable from other
> windows then just selected one.

The downside is that every command needs to be modified and its body
wrapped with a macro.  An alternative would be to put a new property
on the command symbol with a function that selects a window to redirect
input to.  Implementation-wise this is like repeat-mode works.
Additionally, this will allow making redirected keys repeatable
like Manuel asked to do.  Then:

Currently:
  C-x o SPC SPC SPC DEL C-x o

With a prefix before every key:
  C-h M-i SPC C-h M-i SPC C-h M-i SPC C-h M-i DEL
or with a shorter prefix key:
  M-i SPC M-i SPC M-i SPC M-i DEL

With repeatable key sequences:
  C-h M-i SPC SPC SPC DEL + some key to terminate the sequence
                            of redirecting input to another window

> I don't say you should not include the pre/post hack into Emacs if you want, it
> is better then what already is there for the similar purpose (don't remember
> longer the one included since I don't use it). I am just saying that we should
> perhaps write better commands in the future, so we don't need the hack :).

And I don't say that the pre/post approach is more reliable.
There are other alternatives for general code to select a window
before executing a command as opposed to modifying every command.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01 16:39                 ` Juri Linkov
@ 2023-06-01 19:15                   ` Eli Zaretskii
  2023-06-02  1:10                   ` Arthur Miller
  1 sibling, 0 replies; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-01 19:15 UTC (permalink / raw)
  To: Juri Linkov; +Cc: arthur.miller, manuel, emacs-devel

> From: Juri Linkov <juri@linkov.net>
> Cc: Manuel Giraud <manuel@ledu-giraud.fr>,  emacs-devel@gnu.org
> Date: Thu, 01 Jun 2023 19:39:58 +0300
> 
> > I would prefer if there was a code gen in form of a macro, as suggested, similar
> > to define-minor-mode, that does this switching on pre-defined prefixes so that
> > we get uniformity, and helps people write commands so they work from
> > anywhere. Since it is not possible to completely automate it, perhaps lisp
> > manual could mention how to write commands so they are callable from other
> > windows then just selected one.
> 
> The downside is that every command needs to be modified and its body
> wrapped with a macro.  An alternative would be to put a new property
> on the command symbol with a function that selects a window to redirect
> input to.  Implementation-wise this is like repeat-mode works.

I don't think we need to come up with a generic way of making any
command work on "the other window".  That would be over-engineering,
IMO.  This is only needed in cases when the user is unlikely to have
the window be the selected one.  The number of commands for which this
makes sense is not too large, so I'm not convinced a general-purpose
infrastructure for that is needed.  with-selected-window seems more
than enough to me.



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

* Re: Control help- and Info-mode buffers from other buffers
  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
  1 sibling, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-02  1:10 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Manuel Giraud, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> Anyway, pre/post hook hack is very useful, and works with many commands, but not
>> with all, so it is not 100% failsafe and general.Try to execute Info-mode
>> from other window but Info (shortcut 'm'). In my Emacs it does not work.
>
> I tried 'm' (Info-menu), and it works nicely without any problem.
> As long as key prefixes are the same in both windows.

Ok. Then perhaps I am doing something wrong? How do you run it, is the posted
code snippet all you use, or is there something else there? For me it does not
work at all.

>> Another problem with the above is that this will work only for 'other-window'
>> for which Emacs has an algorithm to figure out which window it is. But what when
>> you have more then two windows, and wish to switch to a different window than
>> what Emacs considers as 'other-window'? You will have to prompt the user, to
>> choose one. The help or info buffers are seldom the 'other-buffer' so constanly
>> choosing the window would be just as annoying as constantly switching to and
>> from, in my opinion.
>
> Indeed, which window to use is a separate question.
>
> For the existing commands scroll-other-window, scroll-other-window-down,
> recenter-other-window, beginning-of-buffer-other-window,
> end-of-buffer-other-window, the user option that defines which window to use
> is 'other-window-scroll-default', and it can be customized
> to any function, for example, a function that looks for
> a window with a Help/Info buffer on the current frame,
> or on any other frame.  Or to a function that uses
> 'get-mru-window' to get the most recently used/displayed window.
> All this is customizable.

Sure it is, but is isn't a customization problem. We wouldn't like to customize
the stuff before every run, right? In a case like this, where we wish to run in
a specific window like help, info or perhaps working-buffer window in case of
ielm, we do want to make some specific commands, which means we would like to
wrap that general do-in-X-window command. Otherwise it would be annoying to
every time have to choose help window.

>> I would prefer if there was a code gen in form of a macro, as suggested, similar
>> to define-minor-mode, that does this switching on pre-defined prefixes so that
>> we get uniformity, and helps people write commands so they work from
>> anywhere. Since it is not possible to completely automate it, perhaps lisp
>> manual could mention how to write commands so they are callable from other
>> windows then just selected one.
>
> The downside is that every command needs to be modified and its body
> wrapped with a macro.

And for some commands, which use more complicated interactive form, it has to be
done separately for the body of interactive form and for the functon body, since
you can't wrap interactive itself in anything (it has to be first form in
function body), so yes indeed.

It would be a ginormous janitor work to go through all Emacs commands and
re-write them. I don't think it is even possible. So no I don't suggest that
:). I suggest this only for writing new commands, and I give a rough sketch as
an illustration of what I men:

#+begin_src emacs-lisp
(defun test-command (arg &optional kill window)
  (interactive "P")
  (let* ((window-alist (mapcar (lambda (w) (cons (format "%s" w) w)) (window-list)))
         (window
          (cond
           ((equal arg '(4))
            (other-window-for-scrolling))
           ((equal arg '(16))
            (cdr (assoc (completing-read
                         "Window: " window-alist) window-alist)))
           (t (window-normalize-window window)))))
    (with-selected-window window
      (message "Did someting in window: %s" window))))
#+end_src

The let* wrapp could be generated on part of the user, in some way.

>                        An alternative would be to put a new property
> on the command symbol with a function that selects a window to redirect
> input to.  

How are that property and function meant to be implemented? By the end user, or
by the Emacs? Can end user just choose something like :run-in (one of nil, t,
foo-mode, bar-mode, (some-predicate-p) some-function, etc), where "run-in" is
the property you suggest, and the rest are constrains to choose from?

I don't tink it is too much different from what I suggest, tbh, since it will
anyway have to select somehow the window and that selection would probably be
steared by some argument to the command, but it will be coded differently.































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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-01 16:19                         ` Eli Zaretskii
@ 2023-06-02  1:26                           ` Arthur Miller
  2023-06-02  6:34                             ` Juri Linkov
  2023-06-02  7:11                             ` Eli Zaretskii
  0 siblings, 2 replies; 62+ messages in thread
From: Arthur Miller @ 2023-06-02  1:26 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: juri, manuel, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Arthur Miller <arthur.miller@live.com>
>> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
>> Date: Thu, 01 Jun 2023 15:45:53 +0200
>> 
>> If I try to illustrate with an example: consider a two-window setup with scratch
>> buffer in first and info buffer in second window. If presses a key to execute
>> next command in other window and then 'm'the system will choose commands from
>> selected window (self-insert command in scratch buffer) and try to execute that
>> command in other window which is info buffer. Tbat is obviously wrong since user
>> probably meant to execute Info-menu, by default bound to 'm' in Info-mode.
>> 
>> I don't know if that can be fixed by perhaps manipulating unrread-key-events
>> list or in some other way, perhaps by putting input key back, or by some other
>> means. I haven't managed to get it correctly and have given up and instead tre
>> to address the problem in more fundamental way by making those functions aware
>> of the context they execute in. Also, even if problems could be fixed, I see no
>> point of using such method in this particular patch.
>
> I don't understand why you see such complexity.  All we need is a
> command that will do
>
>   (with-selected-window (get-buffer-window "*info*")
>     BODY...
No you need a bit more than that sometimes. In case that interactive is doing
some prompting and buffer dependent setup, you will have to wrap interactive
body for itself, and function body for itself. See Info-mode or Info-find (I
think) in patch, but conceptually, yes that is about it. If you can live with
wrapping commands in with-selected-window. Due to fact that interactive form
must be the first form in a function body.

> and then we need to bind it to, say, "C-x 4 h m".  Did I miss
> something important?

Basically that these were two discussions, one where Juri asked me about my
experiences about running commands in other windows, and one about the
patch. Juri dislike the idea of wrapping all commands into with-selected-window
and is trying to find a way to transfer command execution to other window
without need to wrap commands explicitly as I understand him, which I hope he
will find.

The above is discussion about that general command that will send input
to other windows.

Btw, as I understand it, if commands are wrapped in with-selected-window then
yes, they would work with pre/post hack, but it is probably more user friendly
if instead put mode-map on the prefix key, since tools lke which-key will
display a nice pop-up of available stuff in the mode-map, which won't happen in
case of pre/post hook.

>> But for the suggested patch for help and info, I see no reason to use pre/post
>> hook to switch windows, I can just use with-selected-window which does 
>> exactly the same switching for me in 90% of cases, and manually fix the few
>> cases which needed some extra care.
>
> I didn't say anything about any hooks.

As I understand the confusion: two discussions in one. Perhaps I am just
confused myself :).







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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02  1:10                   ` Arthur Miller
@ 2023-06-02  6:32                     ` Juri Linkov
  2023-06-04 14:41                       ` Arthur Miller
  0 siblings, 1 reply; 62+ messages in thread
From: Juri Linkov @ 2023-06-02  6:32 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Manuel Giraud, emacs-devel

>>> Anyway, pre/post hook hack is very useful, and works with many commands, but not
>>> with all, so it is not 100% failsafe and general.Try to execute Info-mode
>>> from other window but Info (shortcut 'm'). In my Emacs it does not work.
>>
>> I tried 'm' (Info-menu), and it works nicely without any problem.
>> As long as key prefixes are the same in both windows.
>
> Ok. Then perhaps I am doing something wrong? How do you run it, is the posted
> code snippet all you use, or is there something else there? For me it does not
> work at all.

With this code snippet:

#+begin_src emacs-lisp
(progn
  (add-hook 'pre-command-hook
            (lambda ()
              (other-window 1)
              (setq this-command (key-binding (this-command-keys)))))
  (add-hook 'post-command-hook (lambda () (other-window -1))))
#+end_src

in 'emacs -Q' type: C-x 4 4 C-h i m emacs RET

and it opens the Emacs Manual.

Then 'SPC SPC SPC DEL' scrolls it from the *scratch* buffer.

>> For the existing commands scroll-other-window, scroll-other-window-down,
>> recenter-other-window, beginning-of-buffer-other-window,
>> end-of-buffer-other-window, the user option that defines which window to use
>> is 'other-window-scroll-default', and it can be customized
>> to any function, for example, a function that looks for
>> a window with a Help/Info buffer on the current frame,
>> or on any other frame.  Or to a function that uses
>> 'get-mru-window' to get the most recently used/displayed window.
>> All this is customizable.
>
> Sure it is, but is isn't a customization problem. We wouldn't like to customize
> the stuff before every run, right? In a case like this, where we wish to run in
> a specific window like help, info or perhaps working-buffer window in case of
> ielm, we do want to make some specific commands, which means we would like to
> wrap that general do-in-X-window command. Otherwise it would be annoying to
> every time have to choose help window.

You can't avoid the need of customization even when using with-selected-window.
Since you have already seen requests to support renamed Help/Info buffers
like "*info*<2>", Man-mode buffer names like "*Man ...*", support frames
using the argument ALL-FRAMES of 'get-buffer-window', ...

> It would be a ginormous janitor work to go through all Emacs commands and
> re-write them. I don't think it is even possible. So no I don't suggest that
> :). I suggest this only for writing new commands, and I give a rough sketch as
> an illustration of what I men:
>
> #+begin_src emacs-lisp
> (defun test-command (arg &optional kill window)
>   (interactive "P")
>   (let* ((window-alist (mapcar (lambda (w) (cons (format "%s" w) w)) (window-list)))
>          (window
>           (cond
>            ((equal arg '(4))
>             (other-window-for-scrolling))
>            ((equal arg '(16))
>             (cdr (assoc (completing-read
>                          "Window: " window-alist) window-alist)))
>            (t (window-normalize-window window)))))
>     (with-selected-window window
>       (message "Did someting in window: %s" window))))
> #+end_src
>
> The let* wrapp could be generated on part of the user, in some way.
>
>> An alternative would be to put a new property on the command symbol
>> with a function that selects a window to redirect input to.
>
> How are that property and function meant to be implemented? By the end
> user, or by the Emacs?

Help/Info commands could have this property by default, then users could
add support for more commands by adding such a property to command symbols.

> Can end user just choose something like :run-in (one of nil, t,
> foo-mode, bar-mode, (some-predicate-p) some-function, etc), where "run-in" is
> the property you suggest, and the rest are constrains to choose from?

The property could define a function that selects a window
like in your code above.

> I don't tink it is too much different from what I suggest, tbh, since it will
> anyway have to select somehow the window and that selection would probably be
> steared by some argument to the command, but it will be coded differently.

Indeed, the implementation that selects a window could be the same.



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

* Re: Control help- and Info-mode buffers from other buffers
  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  7:11                             ` Eli Zaretskii
  1 sibling, 2 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-02  6:34 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Eli Zaretskii, manuel, emacs-devel

> Basically that these were two discussions, one where Juri asked me about my
> experiences about running commands in other windows, and one about the
> patch. Juri dislike the idea of wrapping all commands into with-selected-window
> and is trying to find a way to transfer command execution to other window
> without need to wrap commands explicitly as I understand him, which I hope he
> will find.

If you ask me what would be an ideal UI, "UI of a dream", then
the answer is obvious.  Emacs already has such a rudimentary UI.
It works perfectly, but currently is limited to a tiny set of commands.

The key point of the existing "do-in-other-window" UI is that
no additional prefix keys are required, only a modifier key
to hold down while typing other-window navigation keys.

The current modifier key is 'M-'.  So while holding down the ALT key
you can use the navigation keys 'PgUp', 'PgDn', 'Home', 'End'
that browse the buffer in another window.  While doing this,
a natural wish appears to enable more navigation keys.
Why not enable arrow keys as well?  Why only 'M-PgUp' is supported,
but not 'M-up'?  Maybe the answer is that then some keys would conflict
with default keybindings, for example, with 'M-left'.

Such kind of problems found solutions in windmove.  For its
numerous keys it's possible to choose a dedicated modifier,
or a combination of modifiers.

So in the perfect UI, you could just hold down a modifier,
then type the usual keys that you would type if another window
was selected.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02  1:26                           ` Arthur Miller
  2023-06-02  6:34                             ` Juri Linkov
@ 2023-06-02  7:11                             ` Eli Zaretskii
  2023-06-02 15:09                               ` Arthur Miller
  1 sibling, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-02  7:11 UTC (permalink / raw)
  To: Arthur Miller; +Cc: juri, manuel, emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
> Date: Fri, 02 Jun 2023 03:26:26 +0200
> 
> > I don't understand why you see such complexity.  All we need is a
> > command that will do
> >
> >   (with-selected-window (get-buffer-window "*info*")
> >     BODY...
> No you need a bit more than that sometimes. In case that interactive is doing
> some prompting and buffer dependent setup, you will have to wrap interactive
> body for itself, and function body for itself. See Info-mode or Info-find (I
> think) in patch, but conceptually, yes that is about it. If you can live with
> wrapping commands in with-selected-window. Due to fact that interactive form
> must be the first form in a function body.

You also have call-interactively, which can work around this minor
obstacle.

> > and then we need to bind it to, say, "C-x 4 h m".  Did I miss
> > something important?
> 
> Basically that these were two discussions, one where Juri asked me about my
> experiences about running commands in other windows, and one about the
> patch. Juri dislike the idea of wrapping all commands into with-selected-window
> and is trying to find a way to transfer command execution to other window
> without need to wrap commands explicitly as I understand him, which I hope he
> will find.

Well, I see no reason to dislike what Juri dislikes in this case.  So
now you get to choose whose preferences you want to follow ;-)

> The above is discussion about that general command that will send input
> to other windows.

As I said elsewhere in this thread, trying to make this more general
than it needs to be is over-engineering.  Not all commands can
naturally be used with the "other-window prefix", and we are talking
about help-related commands here.  For help-related commands, it is
natural to work in another window while having the help displayed
nearby, and it is therefore natural to control that non-selected help
window without having to select it first.  So that is what we should
do, IMO.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02  7:11                             ` Eli Zaretskii
@ 2023-06-02 15:09                               ` Arthur Miller
  2023-06-02 15:16                                 ` Eli Zaretskii
  2023-06-02 16:13                                 ` Juri Linkov
  0 siblings, 2 replies; 62+ messages in thread
From: Arthur Miller @ 2023-06-02 15:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: juri, manuel, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Arthur Miller <arthur.miller@live.com>
>> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
>> Date: Fri, 02 Jun 2023 03:26:26 +0200
>> 
>> > I don't understand why you see such complexity.  All we need is a
>> > command that will do
>> >
>> >   (with-selected-window (get-buffer-window "*info*")
>> >     BODY...
>> No you need a bit more than that sometimes. In case that interactive is doing
>> some prompting and buffer dependent setup, you will have to wrap interactive
>> body for itself, and function body for itself. See Info-mode or Info-find (I
>> think) in patch, but conceptually, yes that is about it. If you can live with
>> wrapping commands in with-selected-window. Due to fact that interactive form
>> must be the first form in a function body.
>
> You also have call-interactively, which can work around this minor
> obstacle.

Can it? I am talking about interactive "marker" when in defun impelementation; not
calling interactive functions. 

>> > and then we need to bind it to, say, "C-x 4 h m".  Did I miss
>> > something important?
>> 
>> Basically that these were two discussions, one where Juri asked me about my
>> experiences about running commands in other windows, and one about the
>> patch. Juri dislike the idea of wrapping all commands into with-selected-window
>> and is trying to find a way to transfer command execution to other window
>> without need to wrap commands explicitly as I understand him, which I hope he
>> will find.
>
> Well, I see no reason to dislike what Juri dislikes in this case.  So
> now you get to choose whose preferences you want to follow ;-)

I hope I am not misunderstood here. I certainly don't dislike what Juri does; on
contrary. I made that Reddit thread because I very much was looking for
something similar, and I am using pre/post hack myself. However I see some
fundamental issue in how Emacs works, which I am not sure can be easily overcom,
but if Juri can fix them I will certainly be a happy user :).

>> The above is discussion about that general command that will send input
>> to other windows.
>
> As I said elsewhere in this thread, trying to make this more general
> than it needs to be is over-engineering.  Not all commands can
> naturally be used with the "other-window prefix", and we are talking
> about help-related commands here.  For help-related commands, it is
> natural to work in another window while having the help displayed
> nearby, and it is therefore natural to control that non-selected help
> window without having to select it first.  So that is what we should
> do, IMO.

We share similar opinion. As I have mentioned elsewhere, even if pre/post-hack
could be used as a general solution, one would still like to make particular
specialization for certain modes like Help, Info and perhaps a handfull of
others, so in that case I can just give up on generality and manually wrap stuff
as I did.

As just a small update, I was able to string-replace all get-buffer-window
according to Drews helpful tip, so now everything seems to work accross the
frames too. However I haven't had time to test each and every function so they
really work, so I will send in a patch probably tomorrow or day after when I
have had time to go through all to ensure they really do work. I have also
removed the prefix-key helper. Theat removed those 5 lines of :set method from
Helm. I guess you or someone else can put in something similar later on, or just
defvar the prefix in source. Probably not meany people use Customize to redefine
their kes anyway.





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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02  6:34                             ` Juri Linkov
@ 2023-06-02 15:11                               ` Arthur Miller
  2023-06-02 15:29                               ` Yuri Khan
  1 sibling, 0 replies; 62+ messages in thread
From: Arthur Miller @ 2023-06-02 15:11 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Eli Zaretskii, manuel, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> Basically that these were two discussions, one where Juri asked me about my
>> experiences about running commands in other windows, and one about the
>> patch. Juri dislike the idea of wrapping all commands into with-selected-window
>> and is trying to find a way to transfer command execution to other window
>> without need to wrap commands explicitly as I understand him, which I hope he
>> will find.
>
> If you ask me what would be an ideal UI, "UI of a dream", then
> the answer is obvious.  Emacs already has such a rudimentary UI.
> It works perfectly, but currently is limited to a tiny set of commands.
>
> The key point of the existing "do-in-other-window" UI is that
> no additional prefix keys are required, only a modifier key
> to hold down while typing other-window navigation keys.
>
> The current modifier key is 'M-'.  So while holding down the ALT key
> you can use the navigation keys 'PgUp', 'PgDn', 'Home', 'End'
> that browse the buffer in another window.  While doing this,
> a natural wish appears to enable more navigation keys.
> Why not enable arrow keys as well?  Why only 'M-PgUp' is supported,
> but not 'M-up'?  Maybe the answer is that then some keys would conflict
> with default keybindings, for example, with 'M-left'.
>
> Such kind of problems found solutions in windmove.  For its
> numerous keys it's possible to choose a dedicated modifier,
> or a combination of modifiers.
>
> So in the perfect UI, you could just hold down a modifier,
> then type the usual keys that you would type if another window
> was selected.

We share a lot of thoughts in common, but I think there are some fundamental
issues you are fighting against. I'll come to this another time. Just letting
you know that I have seen it.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02 15:09                               ` Arthur Miller
@ 2023-06-02 15:16                                 ` Eli Zaretskii
  2023-06-03 13:53                                   ` Arthur Miller
  2023-06-02 16:13                                 ` Juri Linkov
  1 sibling, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-02 15:16 UTC (permalink / raw)
  To: Arthur Miller; +Cc: juri, manuel, emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
> Date: Fri, 02 Jun 2023 17:09:36 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> >   (with-selected-window (get-buffer-window "*info*")
> >> >     BODY...
> >> No you need a bit more than that sometimes. In case that interactive is doing
> >> some prompting and buffer dependent setup, you will have to wrap interactive
> >> body for itself, and function body for itself. See Info-mode or Info-find (I
> >> think) in patch, but conceptually, yes that is about it. If you can live with
> >> wrapping commands in with-selected-window. Due to fact that interactive form
> >> must be the first form in a function body.
> >
> > You also have call-interactively, which can work around this minor
> > obstacle.
> 
> Can it? I am talking about interactive "marker" when in defun impelementation; not
> calling interactive functions. 

Maybe I don't understand the problem you have in mind.

In any case, whatever the problem is, I'm sure we already have a
solution for it in gazillion other similar cases.



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

* Re: Control help- and Info-mode buffers from other buffers
  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
  1 sibling, 1 reply; 62+ messages in thread
From: Yuri Khan @ 2023-06-02 15:29 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Arthur Miller, Eli Zaretskii, manuel, emacs-devel

On Fri, 2 Jun 2023 at 14:06, Juri Linkov <juri@linkov.net> wrote:

> So in the perfect UI, you could just hold down a modifier,
> then type the usual keys that you would type if another window
> was selected.

You basically want a foot pedal that moves focus to “the” other window
when depressed, and back when released.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02 15:09                               ` Arthur Miller
  2023-06-02 15:16                                 ` Eli Zaretskii
@ 2023-06-02 16:13                                 ` Juri Linkov
  2023-06-03 13:49                                   ` Manuel Giraud via Emacs development discussions.
  2023-06-04  7:48                                   ` Juri Linkov
  1 sibling, 2 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-02 16:13 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Eli Zaretskii, manuel, emacs-devel

> I hope I am not misunderstood here. I certainly don't dislike what Juri does; on
> contrary. I made that Reddit thread because I very much was looking for
> something similar, and I am using pre/post hack myself. However I see some
> fundamental issue in how Emacs works, which I am not sure can be easily overcom,
> but if Juri can fix them I will certainly be a happy user :).

It seems these fundamental issues can't be fixed without changing core functions.
The problem is that another window's buffer should be selected before reading
a key sequence in it and executing the bound command.  So the only reliable
alternative is to have global keymaps with tons of commands that use with-selected-window.
To make them at least more general that just Help/Info specific, a better pattern
would be to base these commands on 'scroll-other-window', e.g.

  (defun forward-char-other-window (&optional n)
    (interactive "P")
    (with-selected-window (other-window-for-scrolling)
      (forward-char n)))

  (defun backward-char-other-window (&optional n)
    (interactive "P")
    (with-selected-window (other-window-for-scrolling)
      (backward-char n)))

  (defun forward-word-other-window (&optional n)
    (interactive "P")
    (with-selected-window (other-window-for-scrolling)
      (forward-word n)))

  (defun backward-word-other-window (&optional n)
    (interactive "P")
    (with-selected-window (other-window-for-scrolling)
      (backward-word n)))

  ...



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02 15:29                               ` Yuri Khan
@ 2023-06-02 16:32                                 ` Juri Linkov
  2023-06-04 14:09                                   ` Arthur Miller
  0 siblings, 1 reply; 62+ messages in thread
From: Juri Linkov @ 2023-06-02 16:32 UTC (permalink / raw)
  To: Yuri Khan; +Cc: Arthur Miller, Eli Zaretskii, manuel, emacs-devel

>> So in the perfect UI, you could just hold down a modifier,
>> then type the usual keys that you would type if another window
>> was selected.
>
> You basically want a foot pedal that moves focus to “the” other window
> when depressed, and back when released.

That legendary foot pedal would be ideal.  But using windmove is also not bad.
When 's-<down>' moves to another window and 's-<up>' moves back, then
a key sequence is quite short: 's-<down> SPC SPC SPC DEL s-<up>'.



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

* Re: Control help- and Info-mode buffers from other buffers
  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  7:48                                   ` Juri Linkov
  1 sibling, 1 reply; 62+ messages in thread
From: Manuel Giraud via Emacs development discussions. @ 2023-06-03 13:49 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Arthur Miller, Eli Zaretskii, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> I hope I am not misunderstood here. I certainly don't dislike what Juri does; on
>> contrary. I made that Reddit thread because I very much was looking for
>> something similar, and I am using pre/post hack myself. However I see some
>> fundamental issue in how Emacs works, which I am not sure can be easily overcom,
>> but if Juri can fix them I will certainly be a happy user :).
>
> It seems these fundamental issues can't be fixed without changing core functions.
> The problem is that another window's buffer should be selected before reading
> a key sequence in it and executing the bound command.  So the only reliable
> alternative is to have global keymaps with tons of commands that use with-selected-window.
> To make them at least more general that just Help/Info specific, a better pattern
> would be to base these commands on 'scroll-other-window', e.g.
>
>   (defun forward-char-other-window (&optional n)
>     (interactive "P")
>     (with-selected-window (other-window-for-scrolling)
>       (forward-char n)))

Hi,

Maybe I'm missing something but I thought that Arthur proposition was
more specific to "help" buffers.  So maybe following this pattern it
could be something like this:

  (defun scroll-other-help (&optional lines)
    (interactive "P")
    (with-selected-window (other-help-window)
      (scroll-up-command n)))

… but then, we'd have to imagine what 'other-help-window' is.
-- 
Manuel Giraud



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02 15:16                                 ` Eli Zaretskii
@ 2023-06-03 13:53                                   ` Arthur Miller
  2023-06-03 14:04                                     ` Eli Zaretskii
  2023-06-04  7:52                                     ` Juri Linkov
  0 siblings, 2 replies; 62+ messages in thread
From: Arthur Miller @ 2023-06-03 13:53 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: juri, manuel, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Arthur Miller <arthur.miller@live.com>
>> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
>> Date: Fri, 02 Jun 2023 17:09:36 +0200
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> >> >   (with-selected-window (get-buffer-window "*info*")
>> >> >     BODY...
>> >> No you need a bit more than that sometimes. In case that interactive is doing
>> >> some prompting and buffer dependent setup, you will have to wrap interactive
>> >> body for itself, and function body for itself. See Info-mode or Info-find (I
>> >> think) in patch, but conceptually, yes that is about it. If you can live with
>> >> wrapping commands in with-selected-window. Due to fact that interactive form
>> >> must be the first form in a function body.
>> >
>> > You also have call-interactively, which can work around this minor
>> > obstacle.
>> 
>> Can it? I am talking about interactive "marker" when in defun impelementation; not
>> calling interactive functions. 
>
> Maybe I don't understand the problem you have in mind.
>
> In any case, whatever the problem is, I'm sure we already have a
> solution for it in gazillion other similar cases.

I like the anser :-), but unfortunately I am affraid I will have to say it is
one of those "fundamental problems". But perhaps I just don't know how to
do it better; see this:

#+begin_src emacs-lisp
(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.
Completion is allowed, and the default menu item is the one point is on.
If FORK is non-nil (interactively with a prefix arg), show the node in
a new Info buffer.  If FORK is a string, it is the name to use for the
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)
         (mouse-autoselect-window nil)
         (info-window (get-buffer-window "*info*" t)))
     (unless (window-live-p info-window)
       (user-error "There is no visible Info buffer."))
     (setq Info-jump (selected-window))
     (select-window info-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.
  (let ((info-window (get-buffer-window "*info*" t))
        (mouse-autoselect-window nil))
    (select-window info-window)
    (Info-goto-node (Info-extract-menu-item menu-item)
                    (and fork
                         (if (stringp fork) fork menu-item))))
  (select-window Info-jump))
#+end_src

The interactive form has to be the very first form in a function body, which
makes it impossible to just wrap the entire function  into
with-selected-window. In this case I save selected-window in global var
(Info-jump) and select that window back at the very end of the function. It is
the same effect as if being wrapped into with-selected-window, but unfortunately
it uses a global variable. I don't know how to do it otherwise, if there is a
better way, pleae let me know, I would like to learn how to do it in a better
way if there is one.

However, that is just a side note as answer to your remark, but unfortunately I
have found that there is a bigger problem. I am not sure if it is Emacs or my
X11 window manager, but I do suspect the WM in this case.

I have both in my WM and in Emacs that focus should follow mouse. Now what
happens is that, despite the function jumpiing to correct Emacs window on
another frame, due to cursor being in old frame, all input goes to the old
frame, so at least this particular function does not work with multiple
frames. According to the docs for select-window, both frame to which the window
belongs, and the buffer displaying are made selected/current, so it is probably
my WM spooking. I will try to test with some other WM and to boot into Windows
and test there, but on systems where focus follows the mouse, I guess there is
nothing to do? I suppose the same issue will happen with few other functions
that prompt user for the input via minibuffer. It works well when Info window is
on the same frame of course.

I do have another question too: what is a good strategy if there are multiple info
windows open? Prompt user to select with a completing read, or just leave as-is,
i.e. return the first info window?

I have tested all help-mode functions with multiple windows, and there are no
problems there.

thanks for help



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-03 13:53                                   ` Arthur Miller
@ 2023-06-03 14:04                                     ` Eli Zaretskii
  2023-06-03 15:06                                       ` Arthur Miller
  2023-06-04  7:52                                     ` Juri Linkov
  1 sibling, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-03 14:04 UTC (permalink / raw)
  To: Arthur Miller; +Cc: juri, manuel, emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
> Date: Sat, 03 Jun 2023 15:53:22 +0200
> 
> The interactive form has to be the very first form in a function body, which
> makes it impossible to just wrap the entire function  into
> with-selected-window.

I still don't understand why you need the entire function to be
wrapped in with-selected-window.

> However, that is just a side note as answer to your remark, but unfortunately I
> have found that there is a bigger problem. I am not sure if it is Emacs or my
> X11 window manager, but I do suspect the WM in this case.
> 
> I have both in my WM and in Emacs that focus should follow mouse. Now what
> happens is that, despite the function jumpiing to correct Emacs window on
> another frame, due to cursor being in old frame, all input goes to the old
> frame, so at least this particular function does not work with multiple
> frames.

I don't understand: I thought we were talking about causing the other
window to do something without the user selecting that window.  So why
does it matter where input goes? what input do you want to go to the
non-selected window or frame?

For example, the command "M-PgUp" scrolls "the other" window without
selecting it, and I don't want in this case to have input switched to
that other window.  Same will be my expectation when there will be a
command to show me the source of a function whose doc string is
displayed in the *Help* buffer shown in the other window: I want just
to see the source in that other window, I don't want to start typing
there.

What am I missing or misunderstanding?  Why are you suddenly talking
about redirecting input?

> I do have another question too: what is a good strategy if there are multiple info
> windows open? Prompt user to select with a completing read, or just leave as-is,
> i.e. return the first info window?

You are talking about a situation where multiple *Help* windows are
shown?



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-03 14:04                                     ` Eli Zaretskii
@ 2023-06-03 15:06                                       ` Arthur Miller
  2023-06-03 15:15                                         ` Eli Zaretskii
  0 siblings, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-03 15:06 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: juri, manuel, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Arthur Miller <arthur.miller@live.com>
>> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
>> Date: Sat, 03 Jun 2023 15:53:22 +0200
>> 
>> The interactive form has to be the very first form in a function body, which
>> makes it impossible to just wrap the entire function  into
>> with-selected-window.
>
> I still don't understand why you need the entire function to be
> wrapped in with-selected-window.

Because part in the interactive form would like to do some work in the info buffer
too. Since we have called function from some other buffer it simply wont
work before switch to info buffer. 

>> However, that is just a side note as answer to your remark, but unfortunately I
>> have found that there is a bigger problem. I am not sure if it is Emacs or my
>> X11 window manager, but I do suspect the WM in this case.
>> 
>> I have both in my WM and in Emacs that focus should follow mouse. Now what
>> happens is that, despite the function jumpiing to correct Emacs window on
>> another frame, due to cursor being in old frame, all input goes to the old
>> frame, so at least this particular function does not work with multiple
>> frames.
>
> I don't understand: I thought we were talking about causing the other
> window to do something without the user selecting that window.  So why
> does it matter where input goes? what input do you want to go to the
> non-selected window or frame?

Yes we are; but some functions prompts user for additional input, and if the
info is in another frame but the one with the focus, than minibuffer does not
get the focus, and the input instead goes into the frame where mouse
cursor/point is. See more further down.

> For example, the command "M-PgUp" scrolls "the other" window without
> selecting it, and I don't want in this case to have input switched to
> that other window.  Same will be my expectation when there will be a
> command to show me the source of a function whose doc string is
> displayed in the *Help* buffer shown in the other window: I want just
> to see the source in that other window, I don't want to start typing
> there.
>
> What am I missing or misunderstanding?  Why are you suddenly talking
> about redirecting input?

As above, some functions, like Info-menu, asks use interactively for additional
input, which we type in minibuffer. But since the old frame is selected it is
not possible to type in info frames minibuffer without moving cursor over to
that frame, which defeats the purpose of the patch. Can I somehow tell Emacs to
prompt in minibuffer in current frame? I haven't done this before, so I am
familiar what to look for here.

Perhaps it is enough to do it in info buffer which I can do in with-info-buffer,
now when I think of it; I am not sure if I have tried to be honest. But anyway,
as a general case, the interactive form part has to be take care of separately
from the rest of the function. Just using the buffer instead of switching the
window perhaps solves problem of frame focus when prompted in minibuffer. Sorry
thinking loud :).

>> I do have another question too: what is a good strategy if there are multiple info
>> windows open? Prompt user to select with a completing read, or just leave as-is,
>> i.e. return the first info window?
>
> You are talking about a situation where multiple *Help* windows are
> shown?

Not *Help*; I am asking about multiple *info* windows.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-03 15:06                                       ` Arthur Miller
@ 2023-06-03 15:15                                         ` Eli Zaretskii
  2023-06-04 14:19                                           ` Arthur Miller
  0 siblings, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-03 15:15 UTC (permalink / raw)
  To: Arthur Miller; +Cc: juri, manuel, emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
> Date: Sat, 03 Jun 2023 17:06:24 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> From: Arthur Miller <arthur.miller@live.com>
> >> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
> >> Date: Sat, 03 Jun 2023 15:53:22 +0200
> >> 
> >> The interactive form has to be the very first form in a function body, which
> >> makes it impossible to just wrap the entire function  into
> >> with-selected-window.
> >
> > I still don't understand why you need the entire function to be
> > wrapped in with-selected-window.
> 
> Because part in the interactive form would like to do some work in the info buffer
> too. Since we have called function from some other buffer it simply wont
> work before switch to info buffer. 

Then use with-selected-window inside the interactive form.

> > I don't understand: I thought we were talking about causing the other
> > window to do something without the user selecting that window.  So why
> > does it matter where input goes? what input do you want to go to the
> > non-selected window or frame?
> 
> Yes we are; but some functions prompts user for additional input, and if the
> info is in another frame but the one with the focus, than minibuffer does not
> get the focus, and the input instead goes into the frame where mouse
> cursor/point is. See more further down.

I don't understand: prompts are in the minibuffer, so are you saying
that there's some situation in Emacs when a Lisp program prompts the
user, but the minibuffer with the prompts doesn't get input focus?
That'd be a terrible bug in Emacs in general, not related to the issue
we are discussing.  Please report such a bug ASAP!

> Not *Help*; I am asking about multiple *info* windows.

A prefix arg should be able to solve at least some such problems.



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

* Re: Control help- and Info-mode buffers from other buffers
  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:38                                       ` Manuel Giraud via Emacs development discussions.
  0 siblings, 2 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-04  7:44 UTC (permalink / raw)
  To: Manuel Giraud; +Cc: Arthur Miller, Eli Zaretskii, emacs-devel

> Maybe I'm missing something but I thought that Arthur proposition was
> more specific to "help" buffers.  So maybe following this pattern it
> could be something like this:
>
>   (defun scroll-other-help (&optional lines)
>     (interactive "P")
>     (with-selected-window (other-help-window)
>       (scroll-up-command n)))
>
> … but then, we'd have to imagine what 'other-help-window' is.

But scrolling commands are not specific to Help buffers.  For example,
scrolling commands that I miss the most to scroll another window are
'scroll-up-line' and 'scroll-down-line' (and 'recenter-other-window'
was added recently with an awkward keybinding 'M-C-S-l' for the lack
of better variants).  It would be unfortunate to add such wrappers
'scroll-down-line-other-window' and 'scroll-up-line-other-window'
that can't be bound to a keybinding by default.

However, for Help buffers the situation is slightly better
since we can bind Help-specific wrappers in the Help keymap, e.g.:

  'C-h 4 s' to run 'help-view-source' in the Help buffer.
  'C-h 4 i' to run 'help-goto-info' in the Help buffer.

These commands could use 'other-help-window' returning
the most recently displayed Help window buffer.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02 16:13                                 ` Juri Linkov
  2023-06-03 13:49                                   ` Manuel Giraud via Emacs development discussions.
@ 2023-06-04  7:48                                   ` Juri Linkov
  1 sibling, 0 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-04  7:48 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Eli Zaretskii, manuel, emacs-devel

> The problem is that another window's buffer should be selected before
> reading a key sequence in it and executing the bound command.

Another alternate that we currently are trying to avoid
is a pattern used in e.g. 'project-switch-project':

#+begin_src emacs-lisp
(defun run-command-other-window (window)
  (interactive (list (other-window-for-scrolling)))
  (with-selected-window window
    (let* ((keys (read-key-sequence "Run in another window: "))
           (command (key-binding keys)))
      (call-interactively command))))

(keymap-global-set "M-i" 'run-command-other-window)
#+end_src

Then 'M-i m' will read a menu in the adjacent Info window,
and visit the selected Info node.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-03 13:53                                   ` Arthur Miller
  2023-06-03 14:04                                     ` Eli Zaretskii
@ 2023-06-04  7:52                                     ` Juri Linkov
  2023-06-04 14:04                                       ` Arthur Miller
  1 sibling, 1 reply; 62+ messages in thread
From: Juri Linkov @ 2023-06-04  7:52 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Eli Zaretskii, manuel, emacs-devel

> The interactive form has to be the very first form in a function body, which
> makes it impossible to just wrap the entire function  into
> with-selected-window. In this case I save selected-window in global var
> (Info-jump) and select that window back at the very end of the function. It is
> the same effect as if being wrapped into with-selected-window, but unfortunately
> it uses a global variable. I don't know how to do it otherwise, if there is a
> better way, pleae let me know, I would like to learn how to do it in a better
> way if there is one.

Another way is to add a new argument WINDOW to all commands.
Then the interactive form could send the window value to the body.

But when an interactive selection of a window by the user is not involved,
then you can just call the same function like 'other-window-for-scrolling'
twice: in the interactive form and in the body it should return the same.

> I have both in my WM and in Emacs that focus should follow mouse. Now what
> happens is that, despite the function jumpiing to correct Emacs window on
> another frame, due to cursor being in old frame, all input goes to the old
> frame, so at least this particular function does not work with multiple
> frames. According to the docs for select-window, both frame to which the window
> belongs, and the buffer displaying are made selected/current, so it is probably
> my WM spooking. I will try to test with some other WM and to boot into Windows
> and test there, but on systems where focus follows the mouse, I guess there is
> nothing to do? I suppose the same issue will happen with few other functions
> that prompt user for the input via minibuffer. It works well when Info window is
> on the same frame of course.

'other-frame' explicitly calls 'select-frame-set-input-focus' at the end.

> I do have another question too: what is a good strategy if there are multiple info
> windows open? Prompt user to select with a completing read, or just leave as-is,
> i.e. return the first info window?

I think a good default would be to use the most recently used window.



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

* Re: Control help- and Info-mode buffers from other buffers
  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:38                                       ` Manuel Giraud via Emacs development discussions.
  1 sibling, 1 reply; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-04  8:50 UTC (permalink / raw)
  To: Juri Linkov; +Cc: manuel, arthur.miller, emacs-devel

> From: Juri Linkov <juri@linkov.net>
> Cc: Arthur Miller <arthur.miller@live.com>,  Eli Zaretskii <eliz@gnu.org>,
>   emacs-devel@gnu.org
> Date: Sun, 04 Jun 2023 10:44:28 +0300
> 
> However, for Help buffers the situation is slightly better
> since we can bind Help-specific wrappers in the Help keymap, e.g.:
> 
>   'C-h 4 s' to run 'help-view-source' in the Help buffer.
>   'C-h 4 i' to run 'help-goto-info' in the Help buffer.

Please use the "C-x 4 h" prefix for "other-window help" commands, not
just "C-x 4".  Otherwise we will run out of key sequences too quickly.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-04  7:44                                     ` Juri Linkov
  2023-06-04  8:50                                       ` Eli Zaretskii
@ 2023-06-04 13:38                                       ` Manuel Giraud via Emacs development discussions.
  1 sibling, 0 replies; 62+ messages in thread
From: Manuel Giraud via Emacs development discussions. @ 2023-06-04 13:38 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Arthur Miller, Eli Zaretskii, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> Maybe I'm missing something but I thought that Arthur proposition was
>> more specific to "help" buffers.  So maybe following this pattern it
>> could be something like this:
>>
>>   (defun scroll-other-help (&optional lines)
>>     (interactive "P")
>>     (with-selected-window (other-help-window)
>>       (scroll-up-command n)))
>>
>> … but then, we'd have to imagine what 'other-help-window' is.
>
> But scrolling commands are not specific to Help buffers.

Yes of course, but I thought that this thread was really about having
helpers to scroll (and more as it was it Arthur's patch) "help" buffers
(for a certain definition of "help" buffers) currently displayed.  These
would be specialized commands for sure but I think that would be useful.
-- 
Manuel Giraud



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

* RE: [External] : Re: Control help- and Info-mode buffers from other buffers
  2023-06-04  8:50                                       ` Eli Zaretskii
@ 2023-06-04 13:40                                         ` Drew Adams
  2023-06-04 13:53                                           ` Arthur Miller
  2023-06-04 14:20                                           ` Eli Zaretskii
  0 siblings, 2 replies; 62+ messages in thread
From: Drew Adams @ 2023-06-04 13:40 UTC (permalink / raw)
  To: Eli Zaretskii, Juri Linkov
  Cc: manuel@ledu-giraud.fr, arthur.miller@live.com,
	emacs-devel@gnu.org

> > we can bind Help-specific wrappers in the Help keymap, e.g.:
> >   'C-h 4 s' to run 'help-view-source' in the Help buffer.
> >   'C-h 4 i' to run 'help-goto-info' in the Help buffer.
> 
> Please use the "C-x 4 h" prefix for "other-window help" commands, not
> just "C-x 4".  Otherwise we will run out of key sequences too quickly.

Dunno if this is what you meant, Eli, but I
initially thought that Juri was suggesting
`C-x 4 s' and `C-x 4 i', and for that I
would have said what you said.  I strongly
oppose gratuitous binding of `C-x 4'.

Then I noticed that Juri's suggesting the
Help-specific prefix `C-h 4'.




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

* Re: [External] : Re: Control help- and Info-mode buffers from other buffers
  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
  1 sibling, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-04 13:53 UTC (permalink / raw)
  To: Drew Adams
  Cc: Eli Zaretskii, Juri Linkov, manuel@ledu-giraud.fr,
	emacs-devel@gnu.org

Drew Adams <drew.adams@oracle.com> writes:

>> > we can bind Help-specific wrappers in the Help keymap, e.g.:
>> >   'C-h 4 s' to run 'help-view-source' in the Help buffer.
>> >   'C-h 4 i' to run 'help-goto-info' in the Help buffer.
>> 
>> Please use the "C-x 4 h" prefix for "other-window help" commands, not
>> just "C-x 4".  Otherwise we will run out of key sequences too quickly.
>
> Dunno if this is what you meant, Eli, but I
> initially thought that Juri was suggesting
> `C-x 4 s' and `C-x 4 i', and for that I
> would have said what you said.  I strongly
> oppose gratuitous binding of `C-x 4'.
>
> Then I noticed that Juri's suggesting the
> Help-specific prefix `C-h 4'.

I think Eli suggested specifically in some previous email to bind C-x 4 h to
help-mode-map, so that all help-mode commands are available as 5 or 6 keys
combinations, like C-x 4 h s for view-source.

I don't think scroll command is implemented as help specific commands; you have
end/beginning of the buffer, next/previous page, as well as next/previous link
(button). Do you really need scroll command too? I mean we mostly just read
help.

When it comes to some potential general key for doing stuff in "other-window"
via pre/post hook, only one is needed, since it will do stuff in any
"other-window" not just help or info; however, some windows are usually *not*
other-window, or very seldom, like those special buffers. With a "remote"
control like wrapping their commands in with-seleceted-window and putting their
mode-map on a prefix-key, which becomes a "buffer remote control", those special
buffers can be excluded from "other-window" consideration totally, so they are
never considered the "other-window" at all. If it is useful or not, I guess is a
personal preference.



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

* RE: [External] : Re: Control help- and Info-mode buffers from other buffers
  2023-06-04 13:53                                           ` Arthur Miller
@ 2023-06-04 14:00                                             ` Drew Adams
  0 siblings, 0 replies; 62+ messages in thread
From: Drew Adams @ 2023-06-04 14:00 UTC (permalink / raw)
  To: Arthur Miller
  Cc: Eli Zaretskii, Juri Linkov, manuel@ledu-giraud.fr,
	emacs-devel@gnu.org

> >> > we can bind Help-specific wrappers in the Help keymap, e.g.:
> >> >   'C-h 4 s' to run 'help-view-source' in the Help buffer.
> >> >   'C-h 4 i' to run 'help-goto-info' in the Help buffer.
> >>
> >> Please use the "C-x 4 h" prefix for "other-window help" commands, not
> >> just "C-x 4".  Otherwise we will run out of key sequences too quickly.
> >
> > Dunno if this is what you meant, Eli, but I
> > initially thought that Juri was suggesting
> > `C-x 4 s' and `C-x 4 i', and for that I
> > would have said what you said.  I strongly
> > oppose gratuitous binding of `C-x 4'.
> >
> > Then I noticed that Juri's suggesting the
> > Help-specific prefix `C-h 4'.
> 
> I think Eli suggested specifically in some previous email to bind C-x 4 h
> to help-mode-map, so that all help-mode commands are available as 5 or 6 keys
> combinations, like C-x 4 h s for view-source.

FWIW, that suggestion from Eli is a fine one.

I was responding only to the fact that Eli
talked only about `C-x 4' and Juri wrote
only about `C-h 4'.  I thought maybe Eli
didn't notice the `C-h'.

My concern, which I thought was also what
Eli was expressing, was not to gratuitously
add to the `C-x 4' prefix.  (IMO, `C-x 4 h'
as a prefix for help commands that use the
other window is fine - not gratuitous waste
of a `C-x 4' prefix.)
__

The rest of your msg doesn't seem to be a
reply to mine (?).

To be clear, I'm not requesting anything about
scrolling other windows.  I rarely, if ever,
have any need to scroll other windows.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-04  7:52                                     ` Juri Linkov
@ 2023-06-04 14:04                                       ` Arthur Miller
  2023-06-04 16:50                                         ` Juri Linkov
  0 siblings, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-04 14:04 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Eli Zaretskii, manuel, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> The interactive form has to be the very first form in a function body, which
>> makes it impossible to just wrap the entire function  into
>> with-selected-window. In this case I save selected-window in global var
>> (Info-jump) and select that window back at the very end of the function. It is
>> the same effect as if being wrapped into with-selected-window, but unfortunately
>> it uses a global variable. I don't know how to do it otherwise, if there is a
>> better way, pleae let me know, I would like to learn how to do it in a better
>> way if there is one.
>
> Another way is to add a new argument WINDOW to all commands.
> Then the interactive form could send the window value to the body.

Sorry I wasn't answered to your last few mails; I am just busy and would to get
this done and over. Yeah I would myself pretty much like to have that pedal to
modify all commands like a piano player; but I am really bad with it, I forgett the
pedal all the time when I play. But I would gladly start a business and sell
Emacs pedals if you are interested!

If you think of the bit of code I sent in as test-command; I would like to get
that *effect*, not necessary to really paste in all that code in each and every
command. Could it be possibly to create a function, that is called by each
commands and that arranges to execute the command in correct context
(other-window or user selected window)? I mean like you suggested, some sort of
parameter, keyword etc? If you think of your idea, which seemed more flexible to
me, but to achieve that effect, and if that would in some easy to user macro a
lá define-minor-mode or use-package style.

> But when an interactive selection of a window by the user is not involved,
> then you can just call the same function like 'other-window-for-scrolling'
> twice: in the interactive form and in the body it should return the same.
>
>> I have both in my WM and in Emacs that focus should follow mouse. Now what
>> happens is that, despite the function jumpiing to correct Emacs window on
>> another frame, due to cursor being in old frame, all input goes to the old
>> frame, so at least this particular function does not work with multiple
>> frames. According to the docs for select-window, both frame to which the window
>> belongs, and the buffer displaying are made selected/current, so it is probably
>> my WM spooking. I will try to test with some other WM and to boot into Windows
>> and test there, but on systems where focus follows the mouse, I guess there is
>> nothing to do? I suppose the same issue will happen with few other functions
>> that prompt user for the input via minibuffer. It works well when Info window is
>> on the same frame of course.
>
> 'other-frame' explicitly calls 'select-frame-set-input-focus' at the end.
I have just found a solution that will display menu in users selected frame, so
all good :-).

#+begin_src emacs-lisp
(interactive
   (let (;; If point is within a menu item, use that item as the default
         (default nil)
         (p (point))
         beg
         (case-fold-search t)
         (mouse-autoselect-window nil)
         (info-window (get-buffer-window "*info*" t)))
     (unless (window-live-p info-window)
       (user-error "There is no visible Info buffer."))
     (with-current-buffer (window-buffer info-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)
#+end_src

As I got the idea yesterday while writing about it; It is possible to just work
in info-buffer, there is no need to also switch to window. The above interacitve
form is from Info-menu; and now I get the prompt displayed in the selected frame
not one with the *info* buffer and I can remove the ugly global var I used for
"the communication" too :).

>> I do have another question too: what is a good strategy if there are multiple info
>> windows open? Prompt user to select with a completing read, or just leave as-is,
>> i.e. return the first info window?
>
> I think a good default would be to use the most recently used window.

Isn't that what would Emacs does per automatic, I mean what get-buffer or
get-buffer-window chooses?



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02 16:32                                 ` Juri Linkov
@ 2023-06-04 14:09                                   ` Arthur Miller
  0 siblings, 0 replies; 62+ messages in thread
From: Arthur Miller @ 2023-06-04 14:09 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Yuri Khan, Eli Zaretskii, manuel, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>>> So in the perfect UI, you could just hold down a modifier,
>>> then type the usual keys that you would type if another window
>>> was selected.
>>
>> You basically want a foot pedal that moves focus to “the” other window
>> when depressed, and back when released.
>
> That legendary foot pedal would be ideal.  But using windmove is also not bad.
> When 's-<down>' moves to another window and 's-<up>' moves back, then
> a key sequence is quite short: 's-<down> SPC SPC SPC DEL s-<up>'.

I have in my Emacs define prefix + jkli as move to window left, down, right,
up. These are like "wasd" keys used in computer games, I used the same analogy,
just shifted it to left hand instead. I do use prefix + wasd too "send buffer"
from selected window to up, left, right, down in cases where Emacs opens a
buffer in my selected window but I didn't want it, or I want certain buffer in
some particular window.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-03 15:15                                         ` Eli Zaretskii
@ 2023-06-04 14:19                                           ` Arthur Miller
  2023-06-04 14:33                                             ` Eli Zaretskii
  0 siblings, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-04 14:19 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: juri, manuel, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Arthur Miller <arthur.miller@live.com>
>> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
>> Date: Sat, 03 Jun 2023 17:06:24 +0200
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> >> From: Arthur Miller <arthur.miller@live.com>
>> >> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
>> >> Date: Sat, 03 Jun 2023 15:53:22 +0200
>> >> 
>> >> The interactive form has to be the very first form in a function body, which
>> >> makes it impossible to just wrap the entire function  into
>> >> with-selected-window.
>> >
>> > I still don't understand why you need the entire function to be
>> > wrapped in with-selected-window.
>> 
>> Because part in the interactive form would like to do some work in the info buffer
>> too. Since we have called function from some other buffer it simply wont
>> work before switch to info buffer. 
>
> Then use with-selected-window inside the interactive form.
>
>> > I don't understand: I thought we were talking about causing the other
>> > window to do something without the user selecting that window.  So why
>> > does it matter where input goes? what input do you want to go to the
>> > non-selected window or frame?
>> 
>> Yes we are; but some functions prompts user for additional input, and if the
>> info is in another frame but the one with the focus, than minibuffer does not
>> get the focus, and the input instead goes into the frame where mouse
>> cursor/point is. See more further down.
>
> I don't understand: prompts are in the minibuffer, so are you saying
> that there's some situation in Emacs when a Lisp program prompts the
> user, but the minibuffer with the prompts doesn't get input focus?
> That'd be a terrible bug in Emacs in general, not related to the issue
> we are discussing.  Please report such a bug ASAP!

Yes, there is definitely such a situation, in my personal copy of Emacs and Arch
Linux at least. But I also said that I believe it is probably due to my X11
window manager :-)

Anyway, I have found a way to do it without switching; as said, I just needed
the data, so with-current-buffer solves it. Now I get the prompt in the selected
frame, not in one with the info buffer. All good.

>> Not *Help*; I am asking about multiple *info* windows.
>
> A prefix arg should be able to solve at least some such problems.

Are you sure you need a prefix? I can just get list of info (and help buffers to
make Drew happy) and prompt user via completing read to pick one.

Since we are aiming at Emacs 30, and there is still no Emacs 29 released, I
guess there is no rush, so I'll take a bit more time to experiment. I'll come
back to the last when I done :).



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

* Re: [External] : Re: Control help- and Info-mode buffers from other buffers
  2023-06-04 13:40                                         ` [External] : " Drew Adams
  2023-06-04 13:53                                           ` Arthur Miller
@ 2023-06-04 14:20                                           ` Eli Zaretskii
  1 sibling, 0 replies; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-04 14:20 UTC (permalink / raw)
  To: Drew Adams; +Cc: juri, manuel, arthur.miller, emacs-devel

> From: Drew Adams <drew.adams@oracle.com>
> CC: "manuel@ledu-giraud.fr" <manuel@ledu-giraud.fr>,
>         "arthur.miller@live.com"
> 	<arthur.miller@live.com>,
>         "emacs-devel@gnu.org" <emacs-devel@gnu.org>
> Date: Sun, 4 Jun 2023 13:40:41 +0000
> 
> Then I noticed that Juri's suggesting the
> Help-specific prefix `C-h 4'.

Right, but "C-h 4" makes less sense than "C-x 4 h", IMO.  If nothing
else, it will make the display of "C-h C-h" harder to get right.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-04 14:19                                           ` Arthur Miller
@ 2023-06-04 14:33                                             ` Eli Zaretskii
  0 siblings, 0 replies; 62+ messages in thread
From: Eli Zaretskii @ 2023-06-04 14:33 UTC (permalink / raw)
  To: Arthur Miller; +Cc: juri, manuel, emacs-devel

> From: Arthur Miller <arthur.miller@live.com>
> Cc: juri@linkov.net,  manuel@ledu-giraud.fr,  emacs-devel@gnu.org
> Date: Sun, 04 Jun 2023 16:19:20 +0200
> 
> >> Not *Help*; I am asking about multiple *info* windows.
> >
> > A prefix arg should be able to solve at least some such problems.
> 
> Are you sure you need a prefix? I can just get list of info (and help buffers to
> make Drew happy) and prompt user via completing read to pick one.

The point is to let the user specify the correct window up front,
without bothering him/her with annoying prompts.  So a prefix argument
of N means operate on the window showing the buffer *info*<N>.



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-02  6:32                     ` Juri Linkov
@ 2023-06-04 14:41                       ` Arthur Miller
  2023-06-04 16:54                         ` Juri Linkov
  0 siblings, 1 reply; 62+ messages in thread
From: Arthur Miller @ 2023-06-04 14:41 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Manuel Giraud, emacs-devel

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

Juri Linkov <juri@linkov.net> writes:

>>>> Anyway, pre/post hook hack is very useful, and works with many commands, but not
>>>> with all, so it is not 100% failsafe and general.Try to execute Info-mode
>>>> from other window but Info (shortcut 'm'). In my Emacs it does not work.
>>>
>>> I tried 'm' (Info-menu), and it works nicely without any problem.
>>> As long as key prefixes are the same in both windows.
>>
>> Ok. Then perhaps I am doing something wrong? How do you run it, is the posted
>> code snippet all you use, or is there something else there? For me it does not
>> work at all.
>
> With this code snippet:
>
> #+begin_src emacs-lisp
> (progn
>   (add-hook 'pre-command-hook
>             (lambda ()
>               (other-window 1)
>               (setq this-command (key-binding (this-command-keys)))))
>   (add-hook 'post-command-hook (lambda () (other-window -1))))
> #+end_src
>
> in 'emacs -Q' type: C-x 4 4 C-h i m emacs RET
>
> and it opens the Emacs Manual.

I tested now, and it did work. But I ended up with two info window? :)
I have started with two-window setup, info and scratch, left and right and I
think pre-commands created new window instead of selected the other window. You
can see in the screenshot, but other than that it worked.


[-- Attachment #2: three-window-help.png --]
[-- Type: image/png, Size: 881695 bytes --]

[-- Attachment #3: Type: text/plain, Size: 5474 bytes --]



> Then 'SPC SPC SPC DEL' scrolls it from the *scratch* buffer.

These are quite involved shortcuts. Think of people memorizing all that, and
typing all that. In case one would repeat commands, isn't it just less key
tapping to jumpt to window, do what is needed, and jump back. If we say
Info-mode-map is on a key (my peronal M-i), for me it becomes M-i j,
sequence of a one- or two-key combinations, and 'j' to jump back, regardless of
what Emacs considers other window. Even with repeat-mode I don't see if saves
much compared to just simply switching to the window? Or do I miss and
misuinderstand? I don't use repeat-mode, so I am not super sure about this.

I use pre/post hook as from that Reddit discussion, a lot, to find-file in other
window, to switch to previous/next buffer in other-window and similar, but these
are all "one-timers" relatively speaking. If I would to do something in
other-window, I would switch to that window. Might be just me and my workflow,
but I don't think it is important to stay in the same buffer at any price at all
times. To me doing stuff in other window, remotely in help, info, etc (I am
thinking of doing something similar with ielm and "working-buffer too") is just
a helper. If it becomes more cumbersome to execute stuff there, then it defeats
the purpose. The purpose is to make usage less cumbersome and minmize switching
between the windows which implicitly minimizes key pressing. That is just my
personal preferance and motivation; I understand that other people have other
worklfows, use-cases etc.

>>> For the existing commands scroll-other-window, scroll-other-window-down,
>>> recenter-other-window, beginning-of-buffer-other-window,
>>> end-of-buffer-other-window, the user option that defines which window to use
>>> is 'other-window-scroll-default', and it can be customized
>>> to any function, for example, a function that looks for
>>> a window with a Help/Info buffer on the current frame,
>>> or on any other frame.  Or to a function that uses
>>> 'get-mru-window' to get the most recently used/displayed window.
>>> All this is customizable.
>>
>> Sure it is, but is isn't a customization problem. We wouldn't like to customize
>> the stuff before every run, right? In a case like this, where we wish to run in
>> a specific window like help, info or perhaps working-buffer window in case of
>> ielm, we do want to make some specific commands, which means we would like to
>> wrap that general do-in-X-window command. Otherwise it would be annoying to
>> every time have to choose help window.
>
> You can't avoid the need of customization even when using with-selected-window.
> Since you have already seen requests to support renamed Help/Info buffers
> like "*info*<2>", Man-mode buffer names like "*Man ...*", support frames
> using the argument ALL-FRAMES of 'get-buffer-window', ...
>
>> It would be a ginormous janitor work to go through all Emacs commands and
>> re-write them. I don't think it is even possible. So no I don't suggest that
>> :). I suggest this only for writing new commands, and I give a rough sketch as
>> an illustration of what I men:
>>
>> #+begin_src emacs-lisp
>> (defun test-command (arg &optional kill window)
>>   (interactive "P")
>>   (let* ((window-alist (mapcar (lambda (w) (cons (format "%s" w) w)) (window-list)))
>>          (window
>>           (cond
>>            ((equal arg '(4))
>>             (other-window-for-scrolling))
>>            ((equal arg '(16))
>>             (cdr (assoc (completing-read
>>                          "Window: " window-alist) window-alist)))
>>            (t (window-normalize-window window)))))
>>     (with-selected-window window
>>       (message "Did someting in window: %s" window))))
>> #+end_src
>>
>> The let* wrapp could be generated on part of the user, in some way.
>>
>>> An alternative would be to put a new property on the command symbol
>>> with a function that selects a window to redirect input to.
>>
>> How are that property and function meant to be implemented? By the end
>> user, or by the Emacs?
>
> Help/Info commands could have this property by default, then users could
> add support for more commands by adding such a property to command symbols.
>
>> Can end user just choose something like :run-in (one of nil, t,
>> foo-mode, bar-mode, (some-predicate-p) some-function, etc), where "run-in" is
>> the property you suggest, and the rest are constrains to choose from?
>
> The property could define a function that selects a window
> like in your code above.
>
>> I don't tink it is too much different from what I suggest, tbh, since it will
>> anyway have to select somehow the window and that selection would probably be
>> steared by some argument to the command, but it will be coded differently.
>
> Indeed, the implementation that selects a window could be the same.

Could that function/implementation be made so that a highly hypothetical macro
say "define-command", calls it somehow instead of actually generating the code
in the user command? Otherwise it would be a lot of code duplication, which is
why you don't like with-selected-window I guess? I would like the effect as in
the above little piece of code, but not the exact implementation. And I would
like if C-u and C-u C-u where standardized to use this, instead of every
function usnig them in any way they like and prefixes being used wildly
differently across commands. I understand it is probably a controversial
suggestion :).

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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-04 14:04                                       ` Arthur Miller
@ 2023-06-04 16:50                                         ` Juri Linkov
  0 siblings, 0 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-04 16:50 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Eli Zaretskii, manuel, emacs-devel

>> I think a good default would be to use the most recently used window.
>
> Isn't that what would Emacs does per automatic, I mean what get-buffer or
> get-buffer-window chooses?

The docstring of 'get-buffer-window' says nothing about this,
only the Elisp manual provides the details:

 -- Function: get-buffer-window &optional buffer-or-name all-frames
     This function returns the first window displaying BUFFER-OR-NAME in
     the cyclic ordering of windows, starting from the selected window
     (*note Cyclic Window Ordering::).

Then to return the most recently used window better to use
'get-buffer-window-list' and sort the list by recency.

> As I got the idea yesterday while writing about it; It is possible to just work
> in info-buffer, there is no need to also switch to window. The above interacitve
> form is from Info-menu; and now I get the prompt displayed in the selected frame
> not one with the *info* buffer and I can remove the ugly global var I used for
> "the communication" too :).

So you are using 'with-current-buffer'.  But what if the same Info buffer
is displayed in two windows?  What point will you get after visiting
the Info buffer with 'with-current-buffer'?  It will use the window point
from the first Info window or from the second Info window?  The preference
will be according to the cyclic ordering of windows, or the most recently
used window?



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

* Re: Control help- and Info-mode buffers from other buffers
  2023-06-04 14:41                       ` Arthur Miller
@ 2023-06-04 16:54                         ` Juri Linkov
  0 siblings, 0 replies; 62+ messages in thread
From: Juri Linkov @ 2023-06-04 16:54 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Manuel Giraud, emacs-devel

>> Then 'SPC SPC SPC DEL' scrolls it from the *scratch* buffer.
>
> These are quite involved shortcuts. Think of people memorizing all that, and
> typing all that. In case one would repeat commands, isn't it just less key
> tapping to jumpt to window, do what is needed, and jump back. If we say
> Info-mode-map is on a key (my peronal M-i), for me it becomes M-i j,
> sequence of a one- or two-key combinations, and 'j' to jump back, regardless of
> what Emacs considers other window. Even with repeat-mode I don't see if saves
> much compared to just simply switching to the window? Or do I miss and
> misuinderstand?

This depends on the people's workflow.  Do most people need just to type
a single key in another window most of the time?  Then a global keymap
could save from typing a key to switch to another window.

> Could that function/implementation be made so that a highly hypothetical macro
> say "define-command", calls it somehow instead of actually generating the code
> in the user command? Otherwise it would be a lot of code duplication, which is
> why you don't like with-selected-window I guess? I would like the effect as in
> the above little piece of code, but not the exact implementation.

Indeed, code duplication would be unfortunate.  Ideally, all commands
should stay unchanged.  But since pre/post hook hack is unreliable,
this means creating a lot of wrappers like this

  (defun Info-menu-other-window (menu-item &optional fork)
    (interactive (with-selected-window (other-window-for-info)
                   (Info-menu-read-args)))
    (with-selected-window (other-window-for-info)
      (Info-menu menu-item fork)))



^ permalink raw reply	[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).