* Info-mode patch @ 2023-06-26 16:09 Arthur Miller 2023-06-26 17:56 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-06-26 16:09 UTC (permalink / raw) To: emacs-devel [-- Attachment #1: Type: text/plain, Size: 5668 bytes --] After some re-writes and tests with multiple frames and windows; it is possible to make all commands be callable from other windows, with relatively little effort, but it is not just to wrap them in with-selected-window. Care has to be taken to ensure all prompts are presented to the user on the selected frame, so they don't interfere with the window manager if user has focus follow mouse enabled. Some commands call themselves recursively, or they call other info commands internally. Those commands now take an optional 'window' parameter to avoid prompting user multiple times in what would be percieved as a single command from the user perspective. In general, I have tried to minimze prompting as much as possible; hopefully user is prompted only when there is ambiguity about which window to act on. If user is already in an info window, all commands act by default on that window, so user can use single keys to invoke info commands. This is the same behavior as pre-patch. If there are multiple info windows, then the user can press C-u to act on another info window. If there are more then 2 other info windows, then the user is prompted to choose one, otherwise the "other info window" is selected automatically (no prompting). With numeric prefix, C-n, if there is *info*<n>, that window is chosen. If there are multiple windows with the same number, say two *info*<1> windows, then the user is prompted which one to choose. To minimize both prompting, and typing of the prefix; it is also possible to have multiple info windows, and to create a "connection" to a particular info window. Consider info buffers *info*<1> and *info*<2>, and say user works with files "foo" and "bar". It is possible to press C-u C-u before any Info command and choose *info*<1> in foo, and C-u C-u some-info-command and select *info*<2> in bar, to create a connection for foo and bar. After the connection is created the user can just press Info-mode prefix key to execute info commands in foo or bar and not be prompted which info window to choose, and not having to type C-1 or C-2 either. To override the default "link", again user can press C-u to execute in other info window. Problems: 1. Some commands are already using nummeric prefix, which clashes with using numeric prefix to choose an info window. Examples: info-next-reference info-prev-reference When called from other info window, with C-u prefix, the reference jumps four references instead of one. That because a count is expected in prefix arg, so when C-u is seen, Emacs jumps 4 refereces forward. I am not sure how, or if it is possible to fix that one, so I have left it as-is (user can still switch to the buffer and press TAB in which case it works normally). 2. Due the fact that interactive form has to be the very first form in a command, we can't just wrap all commands into with-selected-frame, especially when a command is called from Lisp. To avoid unnecessary extra prompts, I need a way to communicate that user has already selected a window. I have tried to declare an optional variable in function declaration, but interactive form does not seem to have access to those? For example like this: #+begin_src emacs-lisp (defun info-finder (&optional keywords window) (interactive (let ((w (or window (info-window)))) ... #+end_src but Emacs complains that window is an undefined variable when in interactive call. Perhaps I am just not familiar enough with interactive forms, but as I understand I would have to make it a non-optional argument in the function signature. But that would mess signatures for functions and potentially break 3rd party applications and peoples setups, so I don't think it is an option. But if I just misunderstand how to write interactive forms, and someone has a better way, I am happy to learn it. My option, was to use either a global variable, or closure, or as I did, to stash the variable into the symbol plist. I tried with closures, but then I got some cconv--helper-something as unknown symbol when I was building Emacs. If I forward declare it in loadup.el, then it works fine. I haven't looked into cconv.el why is that, but I choose to skipp that, and used the plists instead. 3. Tests I did attempt to write few; I wanted mostly to test all commands if called from lisp (non-interactively). However, since most of the functionality require live buffers and visible windows, I am not really sure how to make a test that work in batch, without popping and hiding stuff on the screen. Also mouse cursor position seem to mess up bit with focus when I split windows from lisp and try to manipulate visible bufers, so I have gave up on automated tests. I have factored out everything about selecting window to act on, into one a bit more general function called 'find-window-for-help' and one, more Info specific, 'info-window'. I am not really happy how I have implemented those two, I think they look too messy, but they seem to work for now, and I am already dizzy from testing all kind of setups and selections, so I they'll have to do for now. If someone would prefer different strategy for the interaction, for example no prompting at all, than info-window is the place to change that, since all other commands call that one to obtain a window to act on. I have tested each and every command in all those possible combinations, and paths I have described above, but I would not be surprised if I have still missed some (or many? :)). Anyway, here is a patch; this time only for Info mode, but it is easily to produce one for help-mode if this one works in regard to the user interaction, and how it is implemented. [-- Attachment #2: 0001-Use-Info-mode-commands-from-any-buffer.patch --] [-- Type: text/x-patch, Size: 76772 bytes --] From 5e64e0ff8fcb22414aae5acb69aebc58f7658887 Mon Sep 17 00:00:00 2001 From: Arthur Miller <arthur.miller@live.com> Date: Mon, 26 Jun 2023 16:58:55 +0200 Subject: [PATCH] Use Info-mode commands from any buffer Allow commands acting on Info-mode to be called from other buffers. * lisp/info.el (Info-virtual-file-p): * lisp/info.el (Info-menu): * lisp/info.el (Info-next): * lisp/info.el (Info-prev): * lisp/info.el (Info-up): * lisp/info.el (Info-history): * lisp/info.el (Info-history-back): * lisp/info.el (Info-history-forward): * lisp/info.el (Info-directory): * lisp/info.el (Info-toc-insert): * lisp/info.el (Info-toc): * lisp/info.el (Info-extract-menu-item): * lisp/info.el (Info-nth-menu-item): * lisp/info.el (Info-top-node): * lisp/info.el (Info-final-node): * lisp/info.el (Info-forward-node): * lisp/info.el (Info-backward-node): * lisp/info.el (Info-next-menu-item): * lisp/info.el (Info-last-menu-item): * lisp/info.el (Info-next-preorder): * lisp/info.el (Info-last-preorder): * lisp/info.el (Info-scroll-up): * lisp/info.el (Info-scroll-down): * lisp/info.el (Info-mouse-scroll-up): * lisp/info.el (Info-next-reference): * lisp/info.el (Info-prev-reference): * lisp/info.el (Info-index): * lisp/info.el (Info-index-next): * lisp/info.el (Info-find-index-name): * lisp/info.el (Info-virtual-index): * lisp/info.el (Info-apropos): * lisp/info.el (Info-finder): * lisp/info.el (Info-undefined): * lisp/info.el (Info-follow-nearest-node): * lisp/info.el (Info-summary): * lisp/info.el (Info-copy-current-node-name): Info-mode commands adapted to be called from any buffer. * lisp/info.el (Info-describe-mode): * lisp/info.el (Info-quit-window): * lisp/info.el (Info-beginning-of-buffer): * lisp/info.el (Info-end-of-buffer): New commands. Do what they wrapped counterparts without 'Info-' prefix do, but specifically in Info buffer. * lisp/info.el (Info-link): New symbol used to establish a connection between a buffer and an Info buffer. * lisp/info.el (Info-mode-map): * lisp/info.el (Info-mode-menu): Update bindings to reflect the new commands for previously generic commands. * lisp/window.el (find-window-for-help): New function. Used to obtain a window for Info- or help-mode. --- lisp/info.el | 1320 +++++++++++++++++++++++++++--------------------- lisp/window.el | 62 +++ 2 files changed, 794 insertions(+), 588 deletions(-) diff --git a/lisp/info.el b/lisp/info.el index 035dff66e75..f53fccb6d75 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -400,6 +400,54 @@ Info-virtual-nodes (defvar-local Info-current-node-virtual nil "Non-nil if the current Info node is virtual.") +(defvar-local Info-link nil + "A connection to an Info-mode buffer.") + +(defun info-window (&optional only-selected-frame) + "Return an info window from the list of all windows. + +If the current buffer is already an info buffer return selected +window, else find an info window from the list of visible info buffers. +When ONLY-SELECTED-FRAME is true, consider window list only on +selected frame. + +When invoked with a numeric prefix N return info window named *info*<N>. +When called with a non-numeric prefix, C-u C-u, create a connection to an Info +window that will be used as the default for further choice in case of multiple +info windows. If the connection is set, C-u prefix can bes used to acto on other +info window but the connected one. + +This function memoizes its result in symbols value slot. The last +result is not preserved between function calls." + (let* ((all-frames (not only-selected-frame)) + window) + (cond ((equal current-prefix-arg '(16)) ; always ask + (setq Info-link nil + current-prefix-arg nil + window (find-window-for-help 'Info-mode all-frames)) + (when window (setq Info-link window))) + ((equal current-prefix-arg '(4)) ; do in other info window + (setq current-prefix-arg nil + window (find-window-for-help + 'Info-mode all-frames + (unless (eq major-mode 'Info-mode) + Info-link)))) + ((and (not current-prefix-arg) (eq major-mode 'Info-mode)) + (setq window (selected-window))) + (t + (setq window (if (and Info-link + (window-live-p Info-link) + (not (numberp current-prefix-arg))) + Info-link + (find-window-for-help 'Info-mode all-frames))))) + (unless window + (user-error + (if (numberp current-prefix-arg) + (format "No info buffer named *info*<%s> found." + current-prefix-arg) + "There are no visible info buffers."))) + window)) + (defun Info-virtual-file-p (filename) "Check if Info file FILENAME is virtual." (Info-virtual-fun 'find-file filename nil)) @@ -787,7 +835,7 @@ info-setup (if (and (zerop (buffer-size)) (null Info-history)) ;; If we just created the Info buffer, go to the directory. - (Info-directory)))) + (Info-directory (selected-window))))) ;;;###autoload (defun info-emacs-manual () @@ -831,12 +879,12 @@ info-standalone ;; The return value is the value of point at the beginning of matching ;; REGEXP, if the function succeeds, nil otherwise. (defun Info-node-at-bob-matching (regexp) - (and (bobp) ; are we at beginning of buffer? - (looking-at "\^_") ; does it begin with node delimiter? + (and (bobp) ; are we at beginning of buffer? + (looking-at "\^_") ; does it begin with node delimiter? (let (beg) (forward-line 1) (setq beg (point)) - (forward-line 1) ; does the line after delimiter match REGEXP? + (forward-line 1) ; does the line after delimiter match REGEXP? (re-search-backward regexp beg t)))) (defun Info-find-file (filename &optional noerror no-pop-to-dir) @@ -2269,89 +2317,84 @@ Info-following-node-name (and (looking-at (Info-following-node-name-re)) (match-string-no-properties 1))) -(defun Info-next () +(defun Info-next (&optional window) "Go to the \"next\" node, staying on the same hierarchical level. This command doesn't descend into sub-nodes, like \\<Info-mode-map>\\[Info-forward-node] does." (interactive nil Info-mode) - ;; In case another window is currently selected - (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (with-selected-window (or window (info-window)) (Info-goto-node (Info-extract-pointer "next")))) -(defun Info-prev () +(defun Info-prev (&optional window) "Go to the \"previous\" node, staying on the same hierarchical level. This command doesn't go up to the parent node, like \\<Info-mode-map>\\[Info-backward-node] does." (interactive nil Info-mode) - ;; In case another window is currently selected - (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (with-selected-window (or window (info-window)) (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")))) -(defun Info-up (&optional same-file) +(defun Info-up (&optional same-file window) "Go to the superior node of this node. If SAME-FILE is non-nil, do not move to a different Info file." (interactive nil Info-mode) - ;; In case another window is currently selected - (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (with-selected-window (or window (info-window)) (let ((old-node Info-current-node) - (old-file Info-current-file) - (node (Info-extract-pointer "up")) p) + (old-file Info-current-file) + (node (Info-extract-pointer "up")) p) (and same-file - (string-match "^(" node) - (error "Up node is in another Info file")) + (string-match "^(" node) + (error "Up node is in another Info file")) (Info-goto-node node) (setq p (point)) (goto-char (point-min)) (if (and (stringp old-file) - (search-forward "\n* Menu:" nil t) - (re-search-forward - (if (string-equal old-node "Top") - (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")") - (concat "\n\\* +\\(" (regexp-quote old-node) - ":\\|[^:]+: +" (regexp-quote old-node) "\\)")) - nil t)) - (progn (beginning-of-line) (if (looking-at "^\\* ") (forward-char 2))) - (goto-char p) - (Info-restore-point Info-history)))) - ;; If scroll-conservatively is non-zero and less than 101, display - ;; as much of the superior node above the target line as possible. - (when (< 0 scroll-conservatively 101) - (recenter))) + (search-forward "\n* Menu:" nil t) + (re-search-forward + (if (string-equal old-node "Top") + (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")") + (concat "\n\\* +\\(" (regexp-quote old-node) + ":\\|[^:]+: +" (regexp-quote old-node) "\\)")) + nil t)) + (progn (beginning-of-line) (if (looking-at "^\\* ") (forward-char 2))) + (goto-char p) + (Info-restore-point Info-history))) + ;; If scroll-conservatively is non-zero and less than 101, display + ;; as much of the superior node above the target line as possible. + (when (< 0 scroll-conservatively 101) (recenter)))) (defun Info-history-back () "Go back in the history to the last node visited." (interactive nil Info-mode) - (or Info-history - (user-error "This is the first Info node you looked at")) - (let ((history-forward - (cons (list Info-current-file Info-current-node (point)) - Info-history-forward)) - filename nodename opoint) - (setq filename (car (car Info-history))) - (setq nodename (car (cdr (car Info-history)))) - (setq opoint (car (cdr (cdr (car Info-history))))) - (setq Info-history (cdr Info-history)) - (Info-find-node filename nodename) - (setq Info-history (cdr Info-history)) - (setq Info-history-forward history-forward) - (goto-char opoint))) + (with-selected-window (info-window) + (or Info-history + (user-error "This is the first Info node you looked at")) + (let ((history-forward + (cons (list Info-current-file Info-current-node (point)) + Info-history-forward)) + filename nodename opoint) + (setq filename (car (car Info-history))) + (setq nodename (car (cdr (car Info-history)))) + (setq opoint (car (cdr (cdr (car Info-history))))) + (setq Info-history (cdr Info-history)) + (Info-find-node filename nodename) + (setq Info-history (cdr Info-history)) + (setq Info-history-forward history-forward) + (goto-char opoint)))) (defalias 'Info-last 'Info-history-back) (defun Info-history-forward () "Go forward in the history of visited nodes." (interactive nil Info-mode) - (or Info-history-forward - (user-error "This is the last Info node you looked at")) - (let ((history-forward (cdr Info-history-forward)) - filename nodename opoint) - (setq filename (car (car Info-history-forward))) - (setq nodename (car (cdr (car Info-history-forward)))) - (setq opoint (car (cdr (cdr (car Info-history-forward))))) - (Info-find-node filename nodename) - (setq Info-history-forward history-forward) - (goto-char opoint))) + (with-selected-window (info-window) + (or Info-history-forward + (user-error "This is the last Info node you looked at")) + (let ((history-forward (cdr Info-history-forward)) + filename nodename opoint) + (setq filename (car (car Info-history-forward))) + (setq nodename (car (cdr (car Info-history-forward)))) + (setq opoint (car (cdr (cdr (car Info-history-forward))))) + (Info-find-node filename nodename) + (setq Info-history-forward history-forward) + (goto-char opoint)))) \f (add-to-list 'Info-virtual-files '("\\`dir\\'" @@ -2374,10 +2417,11 @@ Info-directory-find-node (Info-insert-dir)) ;;;###autoload -(defun Info-directory () +(defun Info-directory (&optional window) "Go to the Info directory node." (interactive) - (Info-find-node "dir" "top")) + (with-selected-window (or window (info-window)) + (Info-find-node "dir" "top"))) \f (add-to-list 'Info-virtual-files '("\\`\\*History\\*\\'" @@ -2416,9 +2460,10 @@ Info-history-find-node (defun Info-history () "Go to a node with a menu of visited nodes." (interactive nil Info-mode) - (Info-find-node "*History*" "Top") - (Info-next-reference) - (Info-next-reference)) + (with-selected-window (info-window) + (Info-find-node "*History*" "Top") + (Info-next-reference) + (Info-next-reference))) \f (add-to-list 'Info-virtual-nodes '("\\`\\*TOC\\*\\'" @@ -2453,12 +2498,13 @@ Info-toc "Go to a node with table of contents of the current Info file. Table of contents is created from the tree structure of menus." (interactive nil Info-mode) - (Info-find-node Info-current-file "*TOC*") - (let ((prev-node (nth 1 (car Info-history))) p) - (goto-char (point-min)) - (if (setq p (search-forward (concat "*Note " prev-node ":") nil t)) - (setq p (- p (length prev-node) 2))) - (goto-char (or p (point-min))))) + (with-selected-window (info-window) + (Info-find-node Info-current-file "*TOC*") + (let ((prev-node (nth 1 (car Info-history))) p) + (goto-char (point-min)) + (if (setq p (search-forward (concat "*Note " prev-node ":") nil t)) + (setq p (- p (length prev-node) 2))) + (goto-char (or p (point-min)))))) (defun Info-toc-insert (nodes node-list level curr-file) "Insert table of contents with references to nodes." @@ -2577,91 +2623,97 @@ Info-follow-reference (interactive (let ((completion-ignore-case t) (case-fold-search t) + (window (info-window)) completions default alt-default (start-point (point)) str i bol eol) - (save-excursion - ;; Store end and beginning of line. - (setq eol (line-end-position) - bol (line-beginning-position)) - (goto-char (point-min)) - (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t) - (setq str (match-string-no-properties 1)) - ;; See if this one should be the default. - (and (null default) - (<= (match-beginning 0) start-point) - (<= start-point (point)) - (setq default t)) - ;; See if this one should be the alternate default. - (and (null alt-default) - (and (<= bol (match-beginning 0)) - (<= (point) eol)) - (setq alt-default t)) - (setq i 0) - (while (setq i (string-match "[ \n\t]+" str i)) - (setq str (concat (substring str 0 i) " " - (substring str (match-end 0)))) - (setq i (1+ i))) - ;; Record as a completion and perhaps as default. - (if (eq default t) (setq default str)) - (if (eq alt-default t) (setq alt-default str)) - ;; Don't add this string if it's a duplicate. - (or (assoc-string str completions t) - (push str completions))) - (setq completions (nreverse completions))) - ;; If no good default was found, try an alternate. - (or default - (setq default alt-default)) - ;; If only one cross-reference found, then make it default. - (if (eq (length completions) 1) - (setq default (car completions))) - (if completions - (let ((input (completing-read (format-prompt "Follow reference named" - default) - completions nil t))) - (list (if (equal input "") - default input) - current-prefix-arg)) - (user-error "No cross-references in this node"))) + (put 'Info-follow-reference :selected-window window) + (with-current-buffer (window-buffer window) + (save-excursion + ;; Store end and beginning of line. + (setq eol (line-end-position) + bol (line-beginning-position)) + (goto-char (point-min)) + (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t) + (setq str (match-string-no-properties 1)) + ;; See if this one should be the default. + (and (null default) + (<= (match-beginning 0) start-point) + (<= start-point (point)) + (setq default t)) + ;; See if this one should be the alternate default. + (and (null alt-default) + (and (<= bol (match-beginning 0)) + (<= (point) eol)) + (setq alt-default t)) + (setq i 0) + (while (setq i (string-match "[ \n\t]+" str i)) + (setq str (concat (substring str 0 i) " " + (substring str (match-end 0)))) + (setq i (1+ i))) + ;; Record as a completion and perhaps as default. + (if (eq default t) (setq default str)) + (if (eq alt-default t) (setq alt-default str)) + ;; Don't add this string if it's a duplicate. + (or (assoc-string str completions t) + (push str completions))) + (setq completions (nreverse completions))) + ;; If no good default was found, try an alternate. + (or default + (setq default alt-default)) + ;; If only one cross-reference found, then make it default. + (if (eq (length completions) 1) + (setq default (car completions))) + (if completions + (let ((input (completing-read (format-prompt "Follow reference named" + default) + completions nil t))) + (list (if (equal input "") + default input) + current-prefix-arg)) + (user-error "No cross-references in this node")))) Info-mode) - - (unless footnotename - (error "No reference was specified")) - - (let (target i (str (concat "\\*note " (regexp-quote footnotename))) - (case-fold-search t)) - (while (setq i (string-search " " str i)) - (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i)))) - (setq i (+ i 6))) - (save-excursion - ;; Move point to the beginning of reference if point is on reference - (or (looking-at "\\*note[ \n\t]+") - (and (looking-back "\\*note[ \n\t]+" - (save-excursion (skip-chars-backward " \n\t") - (line-beginning-position))) - (goto-char (match-beginning 0))) - (if (and (save-excursion - (goto-char (+ (point) 5)) ; skip a possible *note - (re-search-backward "\\*note[ \n\t]+" nil t) - (looking-at str)) - (<= (point) (match-end 0))) - (goto-char (match-beginning 0)))) - ;; Go to the reference closest to point - (let ((next-ref (save-excursion (and (re-search-forward str nil t) - (+ (match-beginning 0) 5)))) - (prev-ref (save-excursion (and (re-search-backward str nil t) - (+ (match-beginning 0) 5))))) - (goto-char (cond ((and next-ref prev-ref) - (if (< (abs (- next-ref (point))) - (abs (- prev-ref (point)))) - next-ref prev-ref)) - ((or next-ref prev-ref)) - ((user-error "No cross-reference named %s" - footnotename)))) - (setq target (Info-extract-menu-node-name t)))) - (while (setq i (string-match "[ \t\n]+" target i)) - (setq target (concat (substring target 0 i) " " - (substring target (match-end 0)))) - (setq i (+ i 1))) - (Info-goto-node target fork))) + (let ((window (if (called-interactively-p 'any) + (get 'Info-follow-reference :selected-window) + (info-window)))) + (with-selected-window window + (unless footnotename + (error "No reference was specified")) + (let (target i (str (concat "\\*note " (regexp-quote footnotename))) + (case-fold-search t)) + (while (setq i (string-search " " str i)) + (setq str (concat (substring str 0 i) + "[ \t\n]+" (substring str (1+ i)))) + (setq i (+ i 6))) + (save-excursion + ;; Move point to the beginning of reference if point is on reference + (or (looking-at "\\*note[ \n\t]+") + (and (looking-back "\\*note[ \n\t]+" + (save-excursion (skip-chars-backward " \n\t") + (line-beginning-position))) + (goto-char (match-beginning 0))) + (if (and (save-excursion + (goto-char (+ (point) 5)) ; skip a possible *note + (re-search-backward "\\*note[ \n\t]+" nil t) + (looking-at str)) + (<= (point) (match-end 0))) + (goto-char (match-beginning 0)))) + ;; Go to the reference closest to point + (let ((next-ref (save-excursion (and (re-search-forward str nil t) + (+ (match-beginning 0) 5)))) + (prev-ref (save-excursion (and (re-search-backward str nil t) + (+ (match-beginning 0) 5))))) + (goto-char (cond ((and next-ref prev-ref) + (if (< (abs (- next-ref (point))) + (abs (- prev-ref (point)))) + next-ref prev-ref)) + ((or next-ref prev-ref)) + ((user-error "No cross-reference named %s" + footnotename)))) + (setq target (Info-extract-menu-node-name t)))) + (while (setq i (string-match "[ \t\n]+" target i)) + (setq target (concat (substring target 0 i) " " + (substring target (match-end 0)))) + (setq i (+ i 1))) + (Info-goto-node target fork))))) (defconst Info-menu-entry-name-re "\\(?:[^:]\\|:[^:,.;() \t\n]\\)*" ;; We allow newline because this is also used in Info-follow-reference, @@ -2788,7 +2840,6 @@ Info-complete-menu-item Info-complete-nodes))) (complete-with-action action completions string predicate)))))))) - (defun Info-menu (menu-item &optional fork) "Go to the node pointed to by the menu item named (or abbreviated) MENU-ITEM. The menu item should one of those listed in the current node's menu. @@ -2798,38 +2849,43 @@ Info-menu new buffer." (interactive (let (;; If point is within a menu item, use that item as the default - (default nil) - (p (point)) - beg - (case-fold-search t)) - (save-excursion - (goto-char (point-min)) - (if (not (search-forward "\n* menu:" nil t)) - (user-error "No menu in this node")) - (setq beg (point)) - (and (< (point) p) - (save-excursion - (goto-char p) - (end-of-line) - (if (re-search-backward (concat "\n\\* +\\(" - Info-menu-entry-name-re - "\\):") - beg t) - (setq default (match-string-no-properties 1)))))) - (let ((item nil)) - (while (null item) - (setq item (let ((completion-ignore-case t) - (Info-complete-menu-buffer (current-buffer))) - (completing-read (format-prompt "Menu item" default) - #'Info-complete-menu-item nil t nil nil - default)))) - (list item current-prefix-arg))) + (default nil) + (p (point)) + beg + (case-fold-search t) + (window (info-window))) + (with-current-buffer (window-buffer window) + (put 'Info-menu :selected-window window) + (save-excursion + (goto-char (point-min)) + (if (not (search-forward "\n* menu:" nil t)) + (user-error "No menu in this node")) + (setq beg (point)) + (and (< (point) p) + (save-excursion + (goto-char p) + (end-of-line) + (if (re-search-backward (concat "\n\\* +\\(" + Info-menu-entry-name-re + "\\):") + beg t) + (setq default (match-string-no-properties 1)))))) + (let ((item nil)) + (while (null item) + (setq item (let ((completion-ignore-case t) + (Info-complete-menu-buffer (current-buffer))) + (completing-read (format-prompt "Menu item" default) + #'Info-complete-menu-item nil t nil nil + default)))) + (list item current-prefix-arg)))) Info-mode) - ;; there is a problem here in that if several menu items have the same - ;; name you can only go to the node of the first with this command. - (Info-goto-node (Info-extract-menu-item menu-item) - (and fork - (if (stringp fork) fork menu-item)))) + (let ((window (if (called-interactively-p 'any) + (get 'Info-menu :selected-window) + (info-window)))) + (with-selected-window window + (Info-goto-node (Info-extract-menu-item menu-item) + (and fork + (if (stringp fork) fork menu-item)))))) (defun Info-extract-menu-item (menu-item) (setq menu-item (regexp-quote menu-item)) @@ -2869,32 +2925,35 @@ Info-nth-menu-item "Go to the node of the Nth menu item. N is the digit argument used to invoke this command." (interactive nil Info-mode) - (Info-goto-node - (Info-extract-menu-counting - (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0)))) + (with-selected-window (info-window) + (Info-goto-node + (Info-extract-menu-counting + (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0))))) (defun Info-top-node () "Go to the Top node of this file." (interactive nil Info-mode) - (Info-goto-node "Top")) + (with-selected-window (info-window) + (Info-goto-node "Top"))) (defun Info-final-node () "Go to the final node in this file." (interactive nil Info-mode) - (Info-goto-node "Top") - (let ((Info-history nil) - (case-fold-search t)) - ;; Go to the last node in the menu of Top. But don't delve into - ;; detailed node listings. - (Info-goto-node (Info-extract-menu-counting nil t)) - ;; If the last node in the menu is not last in pointer structure, - ;; move forward (but not down- or upward - see bug#1116) until we - ;; can't go any farther. - (while (Info-forward-node t t t) nil) - ;; Then keep moving down to last subnode, unless we reach an index. - (while (and (not (Info-index-node)) - (save-excursion (search-forward "\n* Menu:" nil t))) - (Info-goto-node (Info-extract-menu-counting nil))))) + (with-selected-window (info-window) + (Info-goto-node "Top") + (let ((Info-history nil) + (case-fold-search t)) + ;; Go to the last node in the menu of Top. But don't delve into + ;; detailed node listings. + (Info-goto-node (Info-extract-menu-counting nil t)) + ;; If the last node in the menu is not last in pointer structure, + ;; move forward (but not down- or upward - see bug#1116) until we + ;; can't go any farther. + (while (Info-forward-node t t t) nil) + ;; Then keep moving down to last subnode, unless we reach an index. + (while (and (not (Info-index-node)) + (save-excursion (search-forward "\n* Menu:" nil t))) + (Info-goto-node (Info-extract-menu-counting nil)))))) (defun Info-forward-node (&optional not-down not-up no-error) "Go forward one node, considering all nodes as forming one sequence. @@ -2905,160 +2964,166 @@ Info-forward-node NOT-UP non-nil means don't go to parent nodes, and NO-ERROR non-nil means don't signal a user-error if there's no node to go to." (interactive nil Info-mode) - (goto-char (point-min)) - (forward-line 1) - (let ((case-fold-search t)) - ;; three possibilities, in order of priority: - ;; 1. next node is in a menu in this node (but not in an index) - ;; 2. next node is next at same level - ;; 3. next node is up and next - (cond ((and (not not-down) - (save-excursion (search-forward "\n* menu:" nil t)) - (not (Info-index-node))) - (Info-goto-node (Info-extract-menu-counting 1)) - t) - ((save-excursion (search-backward "next:" nil t)) - (Info-next) - t) - ((and (not not-up) - (save-excursion (search-backward "up:" nil t)) - ;; Use string-equal, not equal, to ignore text props. - (not (string-equal (downcase (Info-extract-pointer "up")) - "top"))) - (let ((old-node Info-current-node)) - (Info-up) - (let ((old-history Info-history) - success) - (unwind-protect - (setq success (Info-forward-node t nil no-error)) - (or success (Info-goto-node old-node))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history))))) - (no-error nil) - (t (user-error "No pointer forward from this node"))))) + (with-selected-window (info-window) + (goto-char (point-min)) + (forward-line 1) + (let ((case-fold-search t)) + ;; three possibilities, in order of priority: + ;; 1. next node is in a menu in this node (but not in an index) + ;; 2. next node is next at same level + ;; 3. next node is up and next + (cond ((and (not not-down) + (save-excursion (search-forward "\n* menu:" nil t)) + (not (Info-index-node))) + (Info-goto-node (Info-extract-menu-counting 1)) + t) + ((save-excursion (search-backward "next:" nil t)) + (Info-next (selected-window)) + t) + ((and (not not-up) + (save-excursion (search-backward "up:" nil t)) + ;; Use string-equal, not equal, to ignore text props. + (not (string-equal (downcase (Info-extract-pointer "up")) + "top"))) + (let ((old-node Info-current-node)) + (Info-up nil (selected-window)) + (let ((old-history Info-history) + success) + (unwind-protect + (setq success (Info-forward-node t nil no-error)) + (or success (Info-goto-node old-node))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history))))) + (no-error nil) + (t (user-error "No pointer forward from this node")))))) (defun Info-backward-node () "Go backward one node, considering all nodes as forming one sequence. If the current node has a \"previous\" node, go to it, descending into its last sub-node, if any; otherwise go \"up\" to the parent node." (interactive nil Info-mode) - (let ((prevnode (Info-extract-pointer "prev[ious]*" t)) - (upnode (Info-extract-pointer "up" t)) - (case-fold-search t)) - (cond ((and upnode (string-search "(" upnode)) - (user-error "First node in file")) - ((and upnode (or (null prevnode) - ;; Use string-equal, not equal, - ;; to ignore text properties. - (string-equal (downcase prevnode) - (downcase upnode)))) - (Info-up)) - (prevnode - ;; If we move back at the same level, - ;; go down to find the last subnode*. - (Info-prev) - (let ((old-history Info-history)) - (while (and (not (Info-index-node)) - (save-excursion (search-forward "\n* Menu:" nil t))) - (Info-goto-node (Info-extract-menu-counting nil))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history)))) - (t - (user-error "No pointer backward from this node"))))) + (with-selected-window (info-window) + (let ((prevnode (Info-extract-pointer "prev[ious]*" t)) + (upnode (Info-extract-pointer "up" t)) + (case-fold-search t)) + (cond ((and upnode (string-search "(" upnode)) + (user-error "First node in file")) + ((and upnode (or (null prevnode) + ;; Use string-equal, not equal, + ;; to ignore text properties. + (string-equal (downcase prevnode) + (downcase upnode)))) + (Info-up nil (selected-window))) + (prevnode + ;; If we move back at the same level, + ;; go down to find the last subnode*. + (Info-prev (selected-window)) + (let ((old-history Info-history)) + (while (and (not (Info-index-node)) + (save-excursion (search-forward "\n* Menu:" nil t))) + (Info-goto-node (Info-extract-menu-counting nil))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history)))) + (t + (user-error "No pointer backward from this node")))))) (define-obsolete-function-alias 'Info-exit #'quit-window "27.1") -(defun Info-next-menu-item () +(defun Info-next-menu-item (&optional window) "Go to the node of the next menu item." (interactive nil Info-mode) - ;; Bind this in case the user sets it to nil. - (let* ((case-fold-search t) - (node - (save-excursion - (forward-line -1) - (search-forward "\n* menu:" nil t) - (and (search-forward "\n* " nil t) - (Info-extract-menu-node-name))))) - (if node (Info-goto-node node) - (user-error "No more items in menu")))) + (with-selected-window (or window (info-window)) + ;; Bind this in case the user sets it to nil. + (let* ((case-fold-search t) + (node + (save-excursion + (forward-line -1) + (search-forward "\n* menu:" nil t) + (and (search-forward "\n* " nil t) + (Info-extract-menu-node-name))))) + (if node (Info-goto-node node) + (user-error "No more items in menu"))))) (defun Info-last-menu-item () "Go to the node of the previous menu item." (interactive nil Info-mode) - (save-excursion - (forward-line 1) - ;; Bind this in case the user sets it to nil. - (let* ((case-fold-search t) - (beg (save-excursion - (and (search-backward "\n* menu:" nil t) - (point))))) - (or (and beg (search-backward "\n* " beg t)) - (user-error "No previous items in menu"))) - (Info-goto-node (save-excursion - (goto-char (match-end 0)) - (Info-extract-menu-node-name))))) + (with-selected-window (info-window) + (save-excursion + (forward-line 1) + ;; Bind this in case the user sets it to nil. + (let* ((case-fold-search t) + (beg (save-excursion + (and (search-backward "\n* menu:" nil t) + (point))))) + (or (and beg (search-backward "\n* " beg t)) + (user-error "No previous items in menu"))) + (Info-goto-node (save-excursion + (goto-char (match-end 0)) + (Info-extract-menu-node-name)))))) (defmacro Info-no-error (&rest body) `(condition-case nil (progn ,@body t) (error nil))) -(defun Info-next-preorder () +(defun Info-next-preorder (&optional window) "Go to the next subnode or the next node, or go up a level." (interactive nil Info-mode) - (cond ((Info-no-error (Info-next-menu-item))) - ((Info-no-error (Info-next))) - ((Info-no-error (Info-up t)) - ;; Since we have already gone thru all the items in this menu, - ;; go up to the end of this node. - (goto-char (point-max)) - ;; Since logically we are done with the node with that menu, - ;; move on from it. But don't add intermediate nodes - ;; to the history on recursive calls. - (let ((old-history Info-history)) - (Info-next-preorder) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history)))) - (t - (user-error "No more nodes")))) - -(defun Info-last-preorder () + (with-selected-window (or window (info-window)) + (cond ((Info-no-error (Info-next-menu-item (selected-window)))) + ((Info-no-error (Info-next (selected-window)))) + ((Info-no-error (Info-up t)) + ;; Since we have already gone thru all the items in this menu, + ;; go up to the end of this node. + (goto-char (point-max)) + ;; Since logically we are done with the node with that menu, + ;; move on from it. But don't add intermediate nodes + ;; to the history on recursive calls. + (let ((old-history Info-history)) + (Info-next-preorder (selected-window)) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history)))) + (t + (user-error "No more nodes"))))) + +(defun Info-last-preorder (&optional window) "Go to the last node, popping up a level if there is none." (interactive nil Info-mode) - (cond ((and Info-scroll-prefer-subnodes - (Info-no-error - (Info-last-menu-item) - ;; If we go down a menu item, go to the end of the node - ;; so we can scroll back through it. - (goto-char (point-max)))) - ;; Keep going down, as long as there are nested menu nodes. - (let ((old-history Info-history)) - (while (Info-no-error - (Info-last-menu-item) - ;; If we go down a menu item, go to the end of the node - ;; so we can scroll back through it. - (goto-char (point-max)))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history))) - (recenter -1)) - ((and (Info-no-error (Info-extract-pointer "prev")) - (not (equal (Info-extract-pointer "up") - (Info-extract-pointer "prev")))) - (Info-no-error (Info-prev)) - (goto-char (point-max)) - (let ((old-history Info-history)) - (while (Info-no-error - (Info-last-menu-item) - ;; If we go down a menu item, go to the end of the node - ;; so we can scroll back through it. - (goto-char (point-max)))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history))) - (recenter -1)) - ((Info-no-error (Info-up t)) - (goto-char (point-min)) - (let ((case-fold-search t)) - (or (search-forward "\n* Menu:" nil t) - (goto-char (point-max))))) - (t (user-error "No previous nodes")))) + (with-selected-window (or window (info-window)) + (cond ((and Info-scroll-prefer-subnodes + (Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the node + ;; so we can scroll back through it. + (goto-char (point-max)))) + ;; Keep going down, as long as there are nested menu nodes. + (let ((old-history Info-history)) + (while (Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the node + ;; so we can scroll back through it. + (goto-char (point-max)))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history))) + (recenter -1)) + ((and (Info-no-error (Info-extract-pointer "prev")) + (not (equal (Info-extract-pointer "up") + (Info-extract-pointer "prev")))) + (Info-no-error (Info-prev (selected-window))) + (goto-char (point-max)) + (let ((old-history Info-history)) + (while (Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the node + ;; so we can scroll back through it. + (goto-char (point-max)))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history))) + (recenter -1)) + ((Info-no-error (Info-up t)) + (goto-char (point-min)) + (let ((case-fold-search t)) + (or (search-forward "\n* Menu:" nil t) + (goto-char (point-max))))) + (t (user-error "No previous nodes"))))) (defun Info-scroll-up () "Scroll one screenful forward in Info, considering all nodes as one sequence. @@ -3073,25 +3138,25 @@ Info-scroll-up the menu of a node, it moves to subnode indicated by the following menu item. (That case won't normally result from this command, but can happen in other ways.)" - (interactive nil Info-mode) - (if (or (< (window-start) (point-min)) - (> (window-start) (point-max))) - (set-window-start (selected-window) (point))) - (let* ((case-fold-search t) - (virtual-end (save-excursion - (goto-char (point-min)) - (if (and Info-scroll-prefer-subnodes - (search-forward "\n* Menu:" nil t)) - (point) - (point-max))))) - (if (or (< virtual-end (window-start)) - (pos-visible-in-window-p virtual-end)) - (cond - (Info-scroll-prefer-subnodes (Info-next-preorder)) - ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1)))) - (t (Info-next-preorder))) - (scroll-up)))) + (with-selected-window (info-window) + (if (or (< (window-start) (point-min)) + (> (window-start) (point-max))) + (set-window-start (selected-window) (point))) + (let* ((case-fold-search t) + (virtual-end (save-excursion + (goto-char (point-min)) + (if (and Info-scroll-prefer-subnodes + (search-forward "\n* Menu:" nil t)) + (point) + (point-max))))) + (if (or (< virtual-end (window-start)) + (pos-visible-in-window-p virtual-end)) + (cond + (Info-scroll-prefer-subnodes (Info-next-preorder (selected-window))) + ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1)))) + (t (Info-next-preorder (selected-window)))) + (scroll-up))))) (defun Info-mouse-scroll-up (e) "Scroll one screenful forward in Info, using the mouse. @@ -3109,21 +3174,22 @@ Info-scroll-down beginning of a node, that goes to the previous node or back up to the parent node." (interactive nil Info-mode) - (if (or (< (window-start) (point-min)) - (> (window-start) (point-max))) - (set-window-start (selected-window) (point))) - (let* ((case-fold-search t) - (current-point (point)) - (virtual-end - (and Info-scroll-prefer-subnodes - (save-excursion - (setq current-point (line-beginning-position)) - (goto-char (point-min)) - (search-forward "\n* Menu:" current-point t))))) - (if (or virtual-end - (pos-visible-in-window-p (point-min) nil t)) - (Info-last-preorder) - (scroll-down)))) + (with-selected-window (info-window) + (if (or (< (window-start) (point-min)) + (> (window-start) (point-max))) + (set-window-start (selected-window) (point))) + (let* ((case-fold-search t) + (current-point (point)) + (virtual-end + (and Info-scroll-prefer-subnodes + (save-excursion + (setq current-point (line-beginning-position)) + (goto-char (point-min)) + (search-forward "\n* Menu:" current-point t))))) + (if (or virtual-end + (pos-visible-in-window-p (point-min) nil t)) + (Info-last-preorder (selected-window)) + (scroll-down))))) (defun Info-mouse-scroll-down (e) "Scroll one screenful backward in Info, using the mouse. @@ -3170,60 +3236,62 @@ Info-prev-reference-or-link (goto-char plink)) (if pxref (goto-char (or (match-beginning 1) (match-beginning 0))))))) -(defun Info-next-reference (&optional recur count) +(defun Info-next-reference (&optional recur count window) "Move cursor to the next cross-reference or menu item in the node. If COUNT is non-nil (interactively with a prefix arg), jump over COUNT cross-references." (interactive "i\np" Info-mode) - (unless count - (setq count 1)) - (if (< count 0) - (Info-prev-reference recur (- count)) - (while (unless (zerop count) (setq count (1- count))) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") - (old-pt (point)) - (case-fold-search t)) - (or (eobp) (forward-char 1)) - (or (Info-next-reference-or-link pat 'link) - (progn - (goto-char (point-min)) - (or (Info-next-reference-or-link pat 'link) - (progn - (goto-char old-pt) - (user-error "No cross references in this node"))))) - (if (looking-at "\\* Menu:") - (if recur - (user-error "No cross references in this node") - (Info-next-reference t)) - (if (looking-at "^\\* ") - (forward-char 2))))))) - -(defun Info-prev-reference (&optional recur count) + (with-selected-window (or window (info-window)) + (unless count + (setq count 1)) + (if (< count 0) + (Info-prev-reference recur (- count) (selected-window)) + (while (unless (zerop count) (setq count (1- count))) + (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") + (old-pt (point)) + (case-fold-search t)) + (or (eobp) (forward-char 1)) + (or (Info-next-reference-or-link pat 'link) + (progn + (goto-char (point-min)) + (or (Info-next-reference-or-link pat 'link) + (progn + (goto-char old-pt) + (user-error "No cross references in this node"))))) + (if (looking-at "\\* Menu:") + (if recur + (user-error "No cross references in this node") + (Info-next-reference t nil (selected-window))) + (if (looking-at "^\\* ") + (forward-char 2)))))))) + +(defun Info-prev-reference (&optional recur count window) "Move cursor to the previous cross-reference or menu item in the node. If COUNT is non-nil (interactively with a prefix arg), jump over COUNT cross-references." (interactive "i\np" Info-mode) - (unless count - (setq count 1)) - (if (< count 0) - (Info-next-reference recur (- count)) - (while (unless (zerop count) (setq count (1- count))) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") - (old-pt (point)) - (case-fold-search t)) - (or (Info-prev-reference-or-link pat 'link) - (progn - (goto-char (point-max)) - (or (Info-prev-reference-or-link pat 'link) - (progn - (goto-char old-pt) - (user-error "No cross references in this node"))))) - (if (looking-at "\\* Menu:") - (if recur - (user-error "No cross references in this node") - (Info-prev-reference t)) - (if (looking-at "^\\* ") - (forward-char 2))))))) + (with-selected-window (or window (info-window)) + (unless count + (setq count 1)) + (if (< count 0) + (Info-next-reference recur (- count) (selected-window)) + (while (unless (zerop count) (setq count (1- count))) + (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") + (old-pt (point)) + (case-fold-search t)) + (or (Info-prev-reference-or-link pat 'link) + (progn + (goto-char (point-max)) + (or (Info-prev-reference-or-link pat 'link) + (progn + (goto-char old-pt) + (user-error "No cross references in this node"))))) + (if (looking-at "\\* Menu:") + (if recur + (user-error "No cross references in this node") + (Info-prev-reference t nil (selected-window))) + (if (looking-at "^\\* ") + (forward-char 2)))))))) \f (defun Info-index-nodes (&optional file) "Return a list of names of all index nodes in Info FILE. @@ -3337,72 +3405,83 @@ info--ensure-not-in-directory-node "type \\[Info-menu] to select a manual"))))) ;;;###autoload -(defun Info-index (topic) +(defun Info-index (topic &optional window) "Look up a string TOPIC in the index for this manual and go to that entry. If there are no exact matches to the specified topic, this chooses the first match which is a case-insensitive substring of a topic. Use the \\<Info-mode-map>\\[Info-index-next] command to see the other matches. Give an empty topic name to go to the Index node itself." (interactive - (list - (let ((completion-ignore-case t) - (Info-complete-menu-buffer (clone-buffer)) - (Info-complete-nodes (Info-index-nodes)) - (Info-history-list nil)) + (let ((frame-to-prompt-in (selected-frame)) + (window (info-window))) + (put 'Info-index :selected-window window) + (with-selected-window window + (list + (let ((completion-ignore-case t) + (Info-complete-menu-buffer (clone-buffer)) + (Info-complete-nodes (Info-index-nodes)) + (Info-history-list nil)) + (info--ensure-not-in-directory-node) + (unwind-protect + (with-current-buffer Info-complete-menu-buffer + (Info-goto-index) + (with-selected-frame frame-to-prompt-in + (completing-read "Index topic: " + #'Info-complete-menu-item))) + (kill-buffer Info-complete-menu-buffer)))))) + Info-mode) + (let ((window (or window (if (called-interactively-p 'any) + (get 'Info-index :selected-window) + (info-window))))) + (with-selected-window window (info--ensure-not-in-directory-node) - (unwind-protect - (with-current-buffer Info-complete-menu-buffer - (Info-goto-index) - (completing-read "Index topic: " #'Info-complete-menu-item)) - (kill-buffer Info-complete-menu-buffer))))) - (info--ensure-not-in-directory-node) - ;; Strip leading colon in topic; index format does not allow them. - (if (and (stringp topic) - (> (length topic) 0) - (= (aref topic 0) ?:)) - (setq topic (substring topic 1))) - (let ((orignode Info-current-node) - (pattern (format "\n\\* +\\([^\n]*\\(%s\\)[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" - (regexp-quote topic))) - node (nodes (Info-index-nodes)) - (ohist-list Info-history-list) - (case-fold-search t)) - (Info-goto-index) - (or (equal topic "") - (let ((matches nil) - (exact nil) - ;; We bind Info-history to nil for internal node-switches so - ;; that we don't put junk in the history. In the first - ;; Info-goto-index call, above, we do update the history - ;; because that is what the user's previous node choice into it. - (Info-history nil) - found) - (while - (progn - (goto-char (point-min)) - (while (re-search-forward pattern nil t) - (let ((entry (match-string-no-properties 1)) - (nodename (match-string-no-properties 3)) - (line (string-to-number (concat "0" (match-string 4))))) - (add-text-properties - (- (match-beginning 2) (match-beginning 1)) - (- (match-end 2) (match-beginning 1)) - '(face info-index-match) entry) - (push (list entry nodename Info-current-node line) matches))) - (setq nodes (cdr nodes) node (car nodes))) - (Info-goto-node node)) - (or matches - (progn - (Info-goto-node orignode) - (user-error "No `%s' in index" topic))) - ;; Here it is a feature that assoc is case-sensitive. - (while (setq found (assoc topic matches)) - (setq exact (cons found exact) - matches (delq found matches))) - (setq Info-history-list ohist-list) - (setq Info-index-alternatives (nconc exact (nreverse matches)) - Info--current-index-alternative 0) - (Info-index-next 0))))) + ;; Strip leading colon in topic; index format does not allow them. + (if (and (stringp topic) + (> (length topic) 0) + (= (aref topic 0) ?:)) + (setq topic (substring topic 1))) + (let ((orignode Info-current-node) + (pattern (format "\n\\* +\\([^\n]*\\(%s\\)[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" + (regexp-quote topic))) + node (nodes (Info-index-nodes)) + (ohist-list Info-history-list) + (case-fold-search t)) + (Info-goto-index) + (or (equal topic "") + (let ((matches nil) + (exact nil) + ;; We bind Info-history to nil for internal node-switches so + ;; that we don't put junk in the history. In the first + ;; Info-goto-index call, above, we do update the history + ;; because that is what the user's previous node choice into it. + (Info-history nil) + found) + (while + (progn + (goto-char (point-min)) + (while (re-search-forward pattern nil t) + (let ((entry (match-string-no-properties 1)) + (nodename (match-string-no-properties 3)) + (line (string-to-number (concat "0" (match-string 4))))) + (add-text-properties + (- (match-beginning 2) (match-beginning 1)) + (- (match-end 2) (match-beginning 1)) + '(face info-index-match) entry) + (push (list entry nodename Info-current-node line) matches))) + (setq nodes (cdr nodes) node (car nodes))) + (Info-goto-node node)) + (or matches + (progn + (Info-goto-node orignode) + (user-error "No `%s' in index" topic))) + ;; Here it is a feature that assoc is case-sensitive. + (while (setq found (assoc topic matches)) + (setq exact (cons found exact) + matches (delq found matches))) + (setq Info-history-list ohist-list) + (setq Info-index-alternatives (nconc exact (nreverse matches)) + Info--current-index-alternative 0) + (Info-index-next 0))))))) (defun Info-index-next (num) "Go to the next matching index item from the last \\<Info-mode-map>\\[Info-index] command. @@ -3411,45 +3490,46 @@ Info-index-next Also see the `Info-warn-on-index-alternatives-wrap' user option." (interactive "p" Info-mode) - (unless Info-index-alternatives - (user-error "No previous `i' command")) - (let ((index (+ Info--current-index-alternative num)) - (total (length Info-index-alternatives)) - (next-key (key-description (where-is-internal - 'Info-index-next overriding-local-map t)))) - (if (and Info-warn-on-index-alternatives-wrap - (> total 1) - (cond - ((< index 0) - (setq Info--current-index-alternative (- total 2)) - (message - "No previous matches, use `%s' to continue from end of list" - next-key) - t) - ((>= index total) - (setq Info--current-index-alternative -1) - (message - "No previous matches, use `%s' to continue from start of list" - next-key) - t))) - () ; Do nothing - (setq index (mod index total) - Info--current-index-alternative index) - (let ((entry (nth index Info-index-alternatives))) - (Info-goto-node (nth 1 entry)) - (if (> (nth 3 entry) 0) - ;; Forward 2 lines less because `Info-find-node-2' initially - ;; puts point to the 2nd line. - (forward-line (- (nth 3 entry) 2)) - (forward-line 3) ; don't search in headers - (Info-find-index-name (car entry))) - (message "Found `%s' in %s. %s" - (car entry) - (nth 2 entry) - (if (> total 1) - (format-message - "(%s total; use `%s' for next)" total next-key) - "(Only match)")))))) + (with-selected-window (info-window) + (unless Info-index-alternatives + (user-error "No previous `i' command")) + (let ((index (+ Info--current-index-alternative num)) + (total (length Info-index-alternatives)) + (next-key (key-description (where-is-internal + 'Info-index-next overriding-local-map t)))) + (if (and Info-warn-on-index-alternatives-wrap + (> total 1) + (cond + ((< index 0) + (setq Info--current-index-alternative (- total 2)) + (message + "No previous matches, use `%s' to continue from end of list" + next-key) + t) + ((>= index total) + (setq Info--current-index-alternative -1) + (message + "No previous matches, use `%s' to continue from start of list" + next-key) + t))) + () ; Do nothing + (setq index (mod index total) + Info--current-index-alternative index) + (let ((entry (nth index Info-index-alternatives))) + (Info-goto-node (nth 1 entry)) + (if (> (nth 3 entry) 0) + ;; Forward 2 lines less because `Info-find-node-2' initially + ;; puts point to the 2nd line. + (forward-line (- (nth 3 entry) 2)) + (forward-line 3) ; don't search in headers + (Info-find-index-name (car entry))) + (message "Found `%s' in %s. %s" + (car entry) + (nth 2 entry) + (if (> total 1) + (format-message + "(%s total; use `%s' for next)" total next-key) + "(Only match)"))))))) (defun Info-find-index-name (name) "Move point to the place within the current node where NAME is defined." @@ -3533,33 +3613,44 @@ Info-virtual-index search results." ;; `interactive' is a copy from `Info-index' (interactive - (list - (let ((completion-ignore-case t) - (Info-complete-menu-buffer (clone-buffer)) - (Info-complete-nodes (Info-index-nodes)) - (Info-history-list nil)) - (info--ensure-not-in-directory-node) - (unwind-protect - (with-current-buffer Info-complete-menu-buffer - (Info-goto-index) - (completing-read "Index topic: " #'Info-complete-menu-item)) - (kill-buffer Info-complete-menu-buffer)))) + (let ((frame-to-prompt-in (selected-frame)) + (window (info-window))) + (put 'Info-virtual-index :selected-window window) + (with-selected-window window + (list + (let ((completion-ignore-case t) + (Info-complete-menu-buffer (clone-buffer)) + (Info-complete-nodes (Info-index-nodes)) + (Info-history-list nil)) + (info--ensure-not-in-directory-node) + (unwind-protect + (with-current-buffer Info-complete-menu-buffer + (Info-goto-index) + (with-selected-frame frame-to-prompt-in + (completing-read "Index topic: " + #'Info-complete-menu-item))) + (kill-buffer Info-complete-menu-buffer)))))) Info-mode) (if (equal topic "") (Info-find-node Info-current-file "*Index*") - (unless (assoc (cons Info-current-file topic) Info-virtual-index-nodes) - (let ((orignode Info-current-node) - (ohist-list Info-history-list)) - ;; Reuse `Info-index' to set `Info-index-alternatives'. - (Info-index topic) - (push (cons (cons Info-current-file topic) Info-index-alternatives) - Info-virtual-index-nodes) - ;; Clean up unnecessary side-effects of `Info-index'. - (setq Info-history-list ohist-list) - (Info-goto-node orignode) - (message ""))) - (Info-find-node Info-current-file - (format "*Index for ‘%s’*" topic)))) + (let ((window (if (called-interactively-p 'any) + (get 'Info-virtual-index :selected-window) + (info-window)))) + (with-selected-window window + (unless (assoc (cons Info-current-file topic) Info-virtual-index-nodes) + (let ((orignode Info-current-node) + (ohist-list Info-history-list)) + ;; Reuse `Info-index' to set `Info-index-alternatives'. + (Info-index topic) + (push (cons (cons Info-current-file topic) Info-index-alternatives) + Info-virtual-index-nodes) + ;; Clean up unnecessary side-effects of `Info-index'. + (setq Info-history-list ohist-list) + (Info-goto-node orignode) + (message ""))) + (Info-find-node Info-current-file + (format "*Index for ‘%s’*" topic)) + (put 'Info-virtual-index :selected-window nil))))) \f (add-to-list 'Info-virtual-files '("\\`\\*Apropos\\*\\'" @@ -3694,20 +3785,25 @@ info-apropos "Search indices of all known Info files on your system for STRING. If REGEXP (interactively, the prefix), use a regexp match. -Display a menu of the possible matches." +Display a menu of the possible matches in selected window. If given +numeric prefix argument display results in window *info*<N>. With +non-numeric argument prompt user for info buffer to display results in." (interactive "sIndex apropos: \nP") - (if (equal string "") - (Info-find-node Info-apropos-file "Top") - (let ((nodes Info-apropos-nodes) - nodename) - (while (and nodes (not (equal string (nth 1 (car nodes))))) - (setq nodes (cdr nodes))) - (if nodes - (Info-find-node Info-apropos-file (car (car nodes)) nil nil t) - (setq nodename (format "Index for ‘%s’" string)) - (push (list nodename string (Info-apropos-matches string regexp)) - Info-apropos-nodes) - (Info-find-node Info-apropos-file nodename))))) + (with-selected-window (if current-prefix-arg + (info-window) + (selected-window)) + (if (equal string "") + (Info-find-node Info-apropos-file "Top") + (let ((nodes Info-apropos-nodes) + nodename) + (while (and nodes (not (equal string (nth 1 (car nodes))))) + (setq nodes (cdr nodes))) + (if nodes + (Info-find-node Info-apropos-file (car (car nodes)) nil nil t) + (setq nodename (format "Index for ‘%s’" string)) + (push (list nodename string (Info-apropos-matches string regexp)) + Info-apropos-nodes) + (Info-find-node Info-apropos-file nodename)))))) \f (add-to-list 'Info-virtual-files '("\\`\\*Finder.*\\*\\'" @@ -3836,23 +3932,34 @@ Info-finder-find-node ;;;###autoload (defun info-finder (&optional keywords) "Display descriptions of the keywords in the Finder virtual manual. -In interactive use, a prefix argument directs this command to read +In interactive use, a non-numeric prefix argument directs this command to read a list of keywords separated by comma. After that, it displays a node -with a list of packages that contain all specified keywords." +with a list of packages that contain all specified keywords. Numeric +prefix argument will choose window with name *info*<N> if such window +exists otherwise it will prompt the user to choose a window." (interactive - (when current-prefix-arg - (require 'finder) - (list - (completing-read-multiple - "Keywords (separated by comma): " - (mapcar #'symbol-name (mapcar #'car (append finder-known-keywords - (finder-unknown-keywords)))) - nil t)))) + (let ((window (info-window))) + (put 'info-finder :selected-window window) + (when (and current-prefix-arg + (not (numberp current-prefix-arg))) + (require 'finder) + (let ((current-prefix-arg nil)) + (with-current-buffer (window-buffer window) + (list + (completing-read-multiple + "Keywords (separated by comma): " + (mapcar #'symbol-name (mapcar #'car (append finder-known-keywords + (finder-unknown-keywords)))) + nil t))))))) + (setq current-prefix-arg nil) (require 'finder) - (if keywords - (Info-find-node Info-finder-file (mapconcat 'identity keywords ", ")) - (Info-find-node Info-finder-file "Top"))) - + (let ((window (if (called-interactively-p 'any) + (get 'info-finder :selected-window) + (info-window)))) + (with-selected-window window + (if keywords + (Info-find-node Info-finder-file (mapconcat 'identity keywords ", ")) + (Info-find-node Info-finder-file "Top"))))) \f (defun Info-undefined () "Make command be undefined in Info." @@ -3937,7 +4044,7 @@ Info-mouse-follow-nearest-node (mouse-set-point click) (and (not (Info-follow-nearest-node)) (save-excursion (forward-line 1) (eobp)) - (Info-next-preorder))) + (Info-next-preorder (selected-window)))) (defun Info-follow-nearest-node (&optional fork) "Follow a node reference near point. @@ -4030,15 +4137,15 @@ Info-mouse-follow-link (Info-goto-node link-args)) ;; These special values of the `link-args' property are used ;; for navigation; see `Info-fontify-node'. - ((eq link-args 'prev) (Info-prev)) - ((eq link-args 'next) (Info-next)) - ((eq link-args 'up) (Info-up))))) + ((eq link-args 'prev) (Info-prev (selected-window))) + ((eq link-args 'next) (Info-next (selected-window))) + ((eq link-args 'up) (Info-up (selected-window)))))) \f (defvar Info-mode-map (let ((map (make-keymap))) (suppress-keymap map) - (define-key map "." 'beginning-of-buffer) + (define-key map "." 'Info-beginning-of-buffer) (define-key map " " 'Info-scroll-up) (define-key map [?\S-\ ] 'Info-scroll-down) (define-key map "\C-m" 'Info-follow-nearest-node) @@ -4060,10 +4167,10 @@ Info-mode-map (define-key map "[" 'Info-backward-node) (define-key map "<" 'Info-top-node) (define-key map ">" 'Info-final-node) - (define-key map "b" 'beginning-of-buffer) + (define-key map "b" 'Info-beginning-of-buffer) (put 'beginning-of-buffer :advertised-binding "b") (define-key map "d" 'Info-directory) - (define-key map "e" 'end-of-buffer) + (define-key map "e" 'Info-end-of-buffer) (define-key map "f" 'Info-follow-reference) (define-key map "g" 'Info-goto-node) (define-key map "G" 'Info-goto-node-web) @@ -4071,7 +4178,7 @@ Info-mode-map ;; This is for compatibility with standalone info (>~ version 5.2). ;; Though for some time, standalone info had H and h reversed. ;; See <https://debbugs.gnu.org/16455>. - (define-key map "H" 'describe-mode) + (define-key map "H" 'Info-describe-mode) (define-key map "i" 'Info-index) (define-key map "I" 'Info-virtual-index) (define-key map "l" 'Info-history-back) @@ -4079,7 +4186,7 @@ Info-mode-map (define-key map "m" 'Info-menu) (define-key map "n" 'Info-next) (define-key map "p" 'Info-prev) - (define-key map "q" 'quit-window) + (define-key map "q" 'Info-quit-window) (define-key map "r" 'Info-history-forward) (define-key map "s" 'Info-search) (define-key map "S" 'Info-search-case-sensitively) @@ -4104,7 +4211,6 @@ Info-mode-map map) "Keymap containing Info commands.") - (defun Info-check-pointer (item) "Non-nil if ITEM is present in this node." (condition-case nil @@ -4125,7 +4231,7 @@ Info-check-pointer :help "Go backward one node, considering all as a sequence"] ["Forward" Info-forward-node :help "Go forward one node, considering all as a sequence"] - ["Beginning" beginning-of-buffer + ["Beginning" Info-beginning-of-buffer :help "Go to beginning of this node"] ["Top" Info-top-node :help "Go to top node of file"] @@ -4323,20 +4429,21 @@ Info-copy-current-node-name The name of the Info file is prepended to the node name in parentheses. With a zero prefix arg, put the name inside a function call to `info'." (interactive "P" Info-mode) - (unless Info-current-node - (user-error "No current Info node")) - (let ((node (if (stringp Info-current-file) - (concat "(" (file-name-sans-extension - (file-name-nondirectory Info-current-file)) - ") " - Info-current-node)))) - (if (zerop (prefix-numeric-value arg)) - (setq node (concat "(info \"" node "\")"))) - (unless (stringp Info-current-file) - (setq node (format "(Info-find-node '%S '%S)" - Info-current-file Info-current-node))) - (kill-new node) - (message "%s" node))) + (with-selected-window (info-window) + (unless Info-current-node + (user-error "No current Info node")) + (let ((node (if (stringp Info-current-file) + (concat "(" (file-name-sans-extension + (file-name-nondirectory Info-current-file)) + ") " + Info-current-node)))) + (if (zerop (prefix-numeric-value arg)) + (setq node (concat "(info \"" node "\")"))) + (unless (stringp Info-current-file) + (setq node (format "(Info-find-node '%S '%S)" + Info-current-file Info-current-node))) + (kill-new node) + (message "%s" node)))) \f ;; Info mode is suitable only for specially formatted data. @@ -5230,8 +5337,7 @@ Info-speedbar-browser ;; Make sure that speedbar is active (speedbar-frame-mode 1) ;; Now, throw us into Info mode on speedbar. - (speedbar-change-initial-expansion-list "Info") - ) + (speedbar-change-initial-expansion-list "Info")) ;; speedbar loads dframe at runtime. (declare-function dframe-select-attached-frame "dframe" (&optional frame)) @@ -5509,6 +5615,44 @@ info--manual-names (apply-partially #'Info-read-node-name-2 Info-directory-list (mapcar #'car Info-suffix-list)))))))) +\f +;;; General buffer manipulation support +;; commands from special-mode wrapped to work on Info-mode only + +(defun Info-beginning-of-buffer () + "Move point to the beginning of *info* buffer." + (interactive) + (with-selected-window (info-window) + (goto-char (point-min)))) + +(defun Info-end-of-buffer () + "Move point to the beginning of *info* buffer." + (interactive) + (with-selected-window (info-window) + (goto-char (point-max)))) + +(defun Info-describe-mode () + "As `describe-mode' but for Info-mode only." + (interactive) + ;; I guess we are good if there is any buffer in Info-mode so + ;; we do a special here to prevent prompting + ;; If there are no info buffer, use (info-window) to signal + ;; the error; this to not duplicate the error message in two places + (catch 'found + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (eq major-mode 'Info-mode) + (describe-mode) + (throw 'found buffer)))) + (throw 'found + (with-temp-buffer + (Info-mode) + (describe-mode))))) + +(defun Info-quit-window () + (interactive) + (with-selected-window (info-window) + (quit-window nil (selected-window)))) (provide 'info) diff --git a/lisp/window.el b/lisp/window.el index 5964fe37ee6..91c764c2e54 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -27,6 +27,8 @@ ;;; Code: +(eval-when-compile (require 'cl-lib)) + (defun internal--before-save-selected-window () (cons (selected-window) ;; We save and restore all frames' selected windows, because @@ -10741,6 +10743,66 @@ window-prefix-map "0" #'delete-windows-on) (define-key ctl-x-map "w" window-prefix-map) +\f +;; help function to find an Info- or help-mode window +(defun find-window-for-help (mode &optional all-frames exclude) + "Find window displaing buffer with major-mode MODE. + +With numeric argument N, return window containing <N> in the name otherwise nil. +With non-numeric prefix, prompt user to select one of the buffers matching +MODE. + +Meaning of ALL-FRAMES is same as in `window-list-1'. +EXCLIDE is an Info window not to be considered as a candidate." + (cl-labels + ((window-candidates (mode) + (let (windows) + (walk-windows + (lambda (w) + (with-current-buffer (window-buffer w) + (and (eq major-mode mode) + (not (eq exclude w)) + (not (eq (selected-window) w)) + (push (cons (prin1-to-string w) w) windows)))) + nil all-frames) + windows)) + (get-window-from-user (window-list) + (cdr + (assoc + (completing-read "Act on window: " window-list) window-list))) + (cull (list pred) + (let (new) + (dolist (elt list) + (when (funcall pred elt) + (push elt new))) + new)) + (get-numbered-window (window-list N) + (let ((numbered-window-list + (cull window-list + (lambda (w) + (string-match-p (format "<%s>" N) (car w)))))) + (cond + ((= (length numbered-window-list) 0) nil) + ((= (length numbered-window-list) 1) + (cdar numbered-window-list)) + ((> (length numbered-window-list) 1) + (get-window-from-user numbered-window-list)))))) + (let ((window-list (remq (selected-window) (window-candidates mode)))) + (when (> (length window-list) 0) + (let ((window + (cond + ((numberp current-prefix-arg) + (let ((window + (get-numbered-window window-list current-prefix-arg))) + (if window + window + (when (> (length window-list) 0) ;; misstyped number arg? + (get-window-from-user window-list))))) + ((> (length window-list) 1) + (get-window-from-user window-list)) + (t (cdar window-list))))) + window))))) + (provide 'window) ;;; window.el ends here -- 2.40.0 ^ permalink raw reply related [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-26 16:09 Info-mode patch Arthur Miller @ 2023-06-26 17:56 ` Juri Linkov 2023-06-26 20:17 ` Arthur Miller 2023-06-27 11:45 ` Info-mode patch Eli Zaretskii 0 siblings, 2 replies; 53+ messages in thread From: Juri Linkov @ 2023-06-26 17:56 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel > -(defun Info-directory () > +(defun Info-directory (&optional window) > "Go to the Info directory node." > (interactive) > - (Info-find-node "dir" "top")) > + (with-selected-window (or window (info-window)) > + (Info-find-node "dir" "top"))) I wonder why you modified a lot of commands instead of implementing a non-intrusive approach of creating standard wrapper commands like (defun Info-directory-other-window (&optional window) "Go to the Info directory node in another window." (interactive) (with-selected-window (or window (info-window)) (Info-directory))) ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-26 17:56 ` Juri Linkov @ 2023-06-26 20:17 ` Arthur Miller 2023-06-27 6:32 ` Juri Linkov 2023-06-27 11:45 ` Info-mode patch Eli Zaretskii 1 sibling, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-06-26 20:17 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >> -(defun Info-directory () >> +(defun Info-directory (&optional window) >> "Go to the Info directory node." >> (interactive) >> - (Info-find-node "dir" "top")) >> + (with-selected-window (or window (info-window)) >> + (Info-find-node "dir" "top"))) > > I wonder why you modified a lot of commands instead of implementing > a non-intrusive approach of creating standard wrapper commands like > > (defun Info-directory-other-window (&optional window) > "Go to the Info directory node in another window." > (interactive) > (with-selected-window (or window (info-window)) > (Info-directory))) Can you control on which frame the input goes when prompted by original function with a wrapper approach? I changed quite few prompts a wrapped stuff with both with-current-buffer, and with-selected-frame to achive that. I don't think I could do that if I wrapped stuff. But what do I know, perhaps there is some way? Also I didn't wanted to change API. Not that I believe that there are many 3rd party packages for Info and Help modes, and that people actually customize those, but anyway. Wrappers would be a new API, which all require at least some documentation etc. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-26 20:17 ` Arthur Miller @ 2023-06-27 6:32 ` Juri Linkov 2023-06-27 7:54 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-06-27 6:32 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel > Also I didn't wanted to change API. Not that I believe that there are many 3rd > party packages for Info and Help modes, and that people actually customize > those, but anyway. Wrappers would be a new API, which all require at least some > documentation etc. Actually adding new arguments to existing functions counts as changing API. Whereas not changing existing functions means keeping the current API unchanged. Since you need to bind commands to new keys anyway, there is no difference whether you bind old commands or new other-window commands. > Can you control on which frame the input goes when prompted by original > function with a wrapper approach? I changed quite few prompts a wrapped stuff > with both with-current-buffer, and with-selected-frame to achive that. I don't > think I could do that if I wrapped stuff. But what do I know, perhaps there is > some way? I see that you added with-selected-frame to Info-index. But it seems this is not enough because with-selected-frame still fails to switch focus to another frame. You need also to use select-frame-set-input-focus. Since neither with-selected-window nor with-selected-frame can switch focus, we could add a new macro like #+begin_src emacs-lisp (defmacro with-selected-window-frame (window &rest body) `(let ((frame (window-frame ,window))) (unless (eq frame (selected-frame)) (select-frame frame 'norecord) (select-frame-set-input-focus frame 'norecord)) (with-selected-window ,window ,@body))) #+end_src Then everything works nicely with #+begin_src emacs-lisp (defun Info-index-other-window (topic) (interactive (with-selected-window-frame (info-window) (eval (cadr (interactive-form 'Info-index))))) (with-selected-window-frame (info-window) (Info-index topic))) #+end_src ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-27 6:32 ` Juri Linkov @ 2023-06-27 7:54 ` Arthur Miller 2023-06-27 18:11 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-06-27 7:54 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >> Also I didn't wanted to change API. Not that I believe that there are many 3rd >> party packages for Info and Help modes, and that people actually customize >> those, but anyway. Wrappers would be a new API, which all require at least some >> documentation etc. > > Actually adding new arguments to existing functions counts as changing API. Yes, but I have added just an optional argument at the end which shouldn't break anything, right? I ment to document 'window' in the function doc but I forgott, remembered after your mail last night :). > Whereas not changing existing functions means keeping the current API unchanged. > Since you need to bind commands to new keys anyway, there is no difference > whether you bind old commands or new other-window commands. I have introduced four new commands where I couldn't do otherwise. But I didn't want to duplicate all commands by wrapping. There is problem of documentation, people wondering why they are duplicate similar functions, more commands in Emacs, and in non-trivial examples it is not possible to just "wrap" stuff in with-selected-window anyway. I think doing it properly from the beginning and adapting stuff under the hood without changing the API or visible commands is a better alternative. >> Can you control on which frame the input goes when prompted by original >> function with a wrapper approach? I changed quite few prompts a wrapped stuff >> with both with-current-buffer, and with-selected-frame to achive that. I don't >> think I could do that if I wrapped stuff. But what do I know, perhaps there is >> some way? > > I see that you added with-selected-frame to Info-index. > But it seems this is not enough because with-selected-frame > still fails to switch focus to another frame. You need also > to use select-frame-set-input-focus. Where it fails? For me it prompts me on correct frame. I didn't want to switch focus on the info frame. I am aware of select-frame-set-input-focus, have used it in some test actually. The reasoning was like this: There are (possibly) two frames: "the user frame", one whith some other buffer and possibly on other frame than info, and "the info frame". We need to switch potential prompts that ask user for the input from info frame to the user frame, and execute the effects of the command on info buffer. We don't want to switch frames and user selected window, that is the point of the patch. If user wish to switch to info frame, they can do so already, as they would do pre-patch. At least that was my reasoning for the patch. > Since neither with-selected-window nor with-selected-frame > can switch focus, we could add a new macro like > > #+begin_src emacs-lisp > (defmacro with-selected-window-frame (window &rest body) > `(let ((frame (window-frame ,window))) > (unless (eq frame (selected-frame)) > (select-frame frame 'norecord) > (select-frame-set-input-focus frame 'norecord)) > (with-selected-window ,window > ,@body))) > #+end_src > > > Then everything works nicely with Have you tested *everything*? Interactively and from lisp? > #+begin_src emacs-lisp > (defun Info-index-other-window (topic) > (interactive (with-selected-window-frame (info-window) > (eval (cadr (interactive-form 'Info-index))))) > (with-selected-window-frame (info-window) > (Info-index topic))) > #+end_src That would be a different behaviour for *all commands* if you would to apply that macro to all commands consistently, but I don't think that is what you would want. In the end, I think you still have to go through each and every command and do something different on the basis if you want to switch window focus or not. Otherwise, if you gonna leave focus in info window, then just introduce a simple way to switch to info window, as I did with the "Info-jump" which I have removed in this patch. I think the reasons to jump over are less due to being able to create "connection" to an info window and use that as the default, without the need to prompt the user or type the numeric prefix. It also works with window created via "C-1/u m" which creates names based on chosen node for visit, for example *info-Elisp* instead of *info<N>. Notice that select-frame-set-input-focus won't move the mouse cursor, which means that mouse will possible (not necessary) be left on the old frame. If a user has, like I did, enabled "focus follow cursor" than depending on the window manager user has, Emacs will be fighting for focus with the wm. Not all managers respect X11 hints and requests. Perhaps, you could save the frame that has the mouse cursor underneath and restore it afterwards too when needed, or warp mouse just somewhere into the info frame, and restore the position afterwards, but I wouldn't bother, that sounds like more troubles then worth. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-27 7:54 ` Arthur Miller @ 2023-06-27 18:11 ` Juri Linkov 2023-06-27 23:09 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-06-27 18:11 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >> But it seems this is not enough because with-selected-frame >> still fails to switch focus to another frame. You need also >> to use select-frame-set-input-focus. > Where it fails? For me it prompts me on correct frame. I didn't want to switch > focus on the info frame. I am aware of select-frame-set-input-focus, have used > it in some test actually. Probably the behaviour depends on the window manager. With my window manager with-selected-frame displays the prompt in another frame, but input is inserted into the original buffer. Maybe we should have a new option whether to use select-frame-set-input-focus? > Have you tested *everything*? Interactively and from lisp? I see no problems with this both interactively and from lisp: #+begin_src emacs-lisp (defmacro with-selected-window-frame (window &rest body) `(let ((old-frame (selected-frame)) (frame (window-frame ,window))) (unless (eq frame old-frame) (select-frame frame 'norecord) (select-frame-set-input-focus frame 'norecord)) (prog1 (with-selected-window ,window ,@body) (select-frame old-frame 'norecord) (select-frame-set-input-focus old-frame 'norecord)))) (defun Info-index-other-window (topic &optional window) (interactive (with-selected-window-frame (info-window) (append (eval (cadr (interactive-form 'Info-index))) (list (selected-window))))) (with-selected-window (or window (info-window)) (Info-index topic))) #+end_src You can't avoid adding the window argument. Otherwise, you need to invent such hacks as sending the window selected by the user to the command body via a symbol property. But in the wrapper command like above there is no problem of adding the window argument to the new command. Maybe it's possible even to write a macro that will generate such wrapper commands automatically from existing commands. It seems you assume that all commands should take a window. But there are no such assumption for most commands that work only in the selected window. OTOH, only -other-window commands switch the window. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-27 18:11 ` Juri Linkov @ 2023-06-27 23:09 ` Arthur Miller 2023-06-28 6:50 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-06-27 23:09 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>> But it seems this is not enough because with-selected-frame >>> still fails to switch focus to another frame. You need also >>> to use select-frame-set-input-focus. >> Where it fails? For me it prompts me on correct frame. I didn't want to switch >> focus on the info frame. I am aware of select-frame-set-input-focus, have used >> it in some test actually. > > Probably the behaviour depends on the window manager. Yes, I am quite sure it isn't "probably" but "surely" :), as I wrote earlier by the way. > With my window manager with-selected-frame displays > the prompt in another frame, but input is inserted > into the original buffer. Maybe we should have > a new option whether to use select-frame-set-input-focus? I am not sure I understand what you mean with input being inserted in the original buffer. >> Have you tested *everything*? Interactively and from lisp? > > I see no problems with this both interactively and from lisp: If you don't care to ask the user which window to choose when ambigous, then you don't have to care about this at all. If you don't want to take care of multiple windows with possibly ambigous names, user misstyping a name and possibly irregular name, then you don't need to prompt the user at all, just take the first info buffer you find or force user to *always* select manually a window and you are all good. But in my opinion it is not hard to have it slightly more polished and automated as I did. > #+begin_src emacs-lisp > (defmacro with-selected-window-frame (window &rest body) > `(let ((old-frame (selected-frame)) > (frame (window-frame ,window))) > (unless (eq frame old-frame) > (select-frame frame 'norecord) > (select-frame-set-input-focus frame 'norecord)) > (prog1 (with-selected-window ,window > ,@body) > (select-frame old-frame 'norecord) > (select-frame-set-input-focus old-frame 'norecord)))) > > (defun Info-index-other-window (topic &optional window) > (interactive > (with-selected-window-frame (info-window) > (append (eval (cadr (interactive-form 'Info-index))) > (list (selected-window))))) > (with-selected-window (or window (info-window)) > (Info-index topic))) > #+end_src > > You can't avoid adding the window argument. Otherwise, you need > to invent such hacks as sending the window selected by the user > to the command body via a symbol property. > But in the wrapper command like above there is no problem > of adding the window argument to the new command. I wasn't familiar with interactive form enough and didn't know how to connect the optional argument from within interactive form. Symbol-plist was just workaround for lack of a better knowledge :). I stated that explicitly in the mail with the patch, but you perhaps didn't have time to read the mail? Anyway, I understand now how is it done: (append (eval (cadr (interactive-form 'Info-index))) (list (selected-window))) I have to return a list of all arguments from interactive form; it was that simple. Thanks for the example. > Maybe it's possible even to write a macro that will generate > such wrapper commands automatically from existing commands. > > It seems you assume that all commands should take a window. Why would I assume that? I wrote explicitly which commands were extended with an optional window argument and why. I don't feel for repeating entire text here, if you have interest, please take a look at the original email, you don't seem to have read it all. That mail answered your opening question, but I didn't want to point it out earlier to not sound impolite, instead I tried to clarify the problems and choices further. > But there are no such assumption for most commands that work > only in the selected window. I am not sure what you are trying to say here. If we have several live buffers and wish to act on one, then the user has to choose one somehow, no? We can either try to automate stuff as I have tried in this patch, by prompting the user in ambigous case when system can't figure it on its own, or we can have a dumb system that forces user to *always* select a window prior to acting on a buffer. If you take a look at the original mail you will understand which commands has got the optional window argument and why, but if you don't prompt the user, than you don't need that argument at all. There is also a problem of prompting and input focus. As I wrote in response to Eli, each command is its own little program, and can prompt user for whatever reason whenever. Thus each command should be written with the assumption that input should be presented to user on the frame where user types and with assumption that user is not executing it from the buffer/window on which it should act. You can achieve all this with tools already in Emacs, no need to introduce any new concepts or macros, and it will also work regardless of the window manager (I think). It is just that the old commands are not written with those assumptions, so I rewrote Info commands in this patch. I am not sure you can achive that with wrapping, but perhaps there is a way, you can try. In my opinion wrapping is OK if we for some reason can't alter the code, but in the case of Info and help mode, I don't see such reason, especially since it is possible to do everything backwards compatible. On the negative side, wrapping introduces double set of commands, so what are you saving? You are wrapping code from "outside", while I have done it from "inside", but overall, the principle is the same. On a plus side for wrappers is that you can actually write a codegen for wrappers, hopefully in form of a macro and it will work for other modes. So it is not only negatives, but you could also have both approaches at the same time too :). ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-27 23:09 ` Arthur Miller @ 2023-06-28 6:50 ` Juri Linkov 2023-06-28 21:52 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-06-28 6:50 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >>>> But it seems this is not enough because with-selected-frame >>>> still fails to switch focus to another frame. You need also >>>> to use select-frame-set-input-focus. >>> Where it fails? For me it prompts me on correct frame. I didn't want to switch >>> focus on the info frame. I am aware of select-frame-set-input-focus, have used >>> it in some test actually. >> >> Probably the behaviour depends on the window manager. > > Yes, I am quite sure it isn't "probably" but "surely" :), as I wrote earlier by > the way. > >> With my window manager with-selected-frame displays >> the prompt in another frame, but input is inserted >> into the original buffer. Maybe we should have >> a new option whether to use select-frame-set-input-focus? > > I am not sure I understand what you mean with input being inserted in the > original buffer. For example, the original buffer is *scratch*. The minibuffer pops up in another frame. I start typing. But letters get inserted to *scratch*, not to the minibuffer. This is because focus is not switched to another frame. >>> Have you tested *everything*? Interactively and from lisp? >> >> I see no problems with this both interactively and from lisp: > > If you don't care to ask the user which window to choose when ambigous, then you > don't have to care about this at all. If you don't want to take care of > multiple windows with possibly ambigous names, user misstyping a name and > possibly irregular name, then you don't need to prompt the user at all, just > take the first info buffer you find or force user to *always* select manually a > window and you are all good. But in my opinion it is not hard to have it slightly > more polished and automated as I did. I don't know what do you mean by the words "don't care" since this implementation still uses your function 'info-window' that asks the user which window to choose. Please look carefully at Info-index-other-window: >> #+begin_src emacs-lisp >> (defmacro with-selected-window-frame (window &rest body) >> `(let ((old-frame (selected-frame)) >> (frame (window-frame ,window))) >> (unless (eq frame old-frame) >> (select-frame frame 'norecord) >> (select-frame-set-input-focus frame 'norecord)) >> (prog1 (with-selected-window ,window >> ,@body) >> (select-frame old-frame 'norecord) >> (select-frame-set-input-focus old-frame 'norecord)))) >> >> (defun Info-index-other-window (topic &optional window) >> (interactive >> (with-selected-window-frame (info-window) >> (append (eval (cadr (interactive-form 'Info-index))) >> (list (selected-window))))) >> (with-selected-window (or window (info-window)) >> (Info-index topic))) >> #+end_src >> >> You can't avoid adding the window argument. Otherwise, you need >> to invent such hacks as sending the window selected by the user >> to the command body via a symbol property. >> >> But in the wrapper command like above there is no problem >> of adding the window argument to the new command. > > I wasn't familiar with interactive form enough and didn't know how to connect > the optional argument from within interactive form. Symbol-plist was just > workaround for lack of a better knowledge :). I stated that explicitly in the > mail with the patch, but you perhaps didn't have time to read the mail? I read all your mails carefully, and noted that there is no better alternative than adding the window argument. > Anyway, I understand now how is it done: > > (append (eval (cadr (interactive-form 'Info-index))) > (list (selected-window))) > > I have to return a list of all arguments from interactive form; it was that > simple. Thanks for the example. Indeed, this is only an example. More handling may be needed for the return value of interactive-form. >> Maybe it's possible even to write a macro that will generate >> such wrapper commands automatically from existing commands. >> >> It seems you assume that all commands should take a window. > > Why would I assume that? I wrote explicitly which commands were extended with > an optional window argument and why. I don't feel for repeating entire text here, > if you have interest, please take a look at the original email, you don't seem > to have read it all. That mail answered your opening question, but I didn't want > to point it out earlier to not sound impolite, instead I tried to clarify the > problems and choices further. I was referring to your words from another mail that I read carefully: > What I am trying to say is that command has to be written with the assumptions > that user can call it from any window and Emacs frame, and that all prompting > should be done in the frame at which user types, so I have reworked info commands > to work with those assumptions. I don't agree that all commands should be written with the assumption that they can be called from any window. Only commands with global keybindings usually prepare the buffer with the predefined name and mode, then mode-local keybindings and mode-local commands assume that they are operating in the right buffer. >> But there are no such assumption for most commands that work >> only in the selected window. > > I am not sure what you are trying to say here. If we have several live buffers > and wish to act on one, then the user has to choose one somehow, no? We can > either try to automate stuff as I have tried in this patch, by prompting the > user in ambigous case when system can't figure it on its own, or we can have a > dumb system that forces user to *always* select a window prior to acting on a > buffer. If you take a look at the original mail you will understand which commands > has got the optional window argument and why, but if you don't prompt the user, > than you don't need that argument at all. Only commands with global keybindings need a way to select the right buffer. The best example of such command is the entry point to the Info browser the command 'info' bound globally to 'C-h i'. This command allows the user to select the right buffer "*info*<N>" by using the prefix arg. What you are trying to do is to copy this logic to all Info-local commands. And here comes the disagreement about the ways of doing this. I know that you already said about this, so hereby I confirm that I already have seen your opinion: > Making a wrapper just to put a call to a command into with-selected-window, > instead of wrapping the body of that command with the same macro, is not so much > better; just different. Sure "less intrusive" on a command itself, but there is > a new command and then we have two where one can do the jobb. Replying to your opinion, I expressed a preference to avoid massive changes in the existing functions. > There is also a problem of prompting and input focus. As I wrote in response to > Eli, each command is its own little program, and can prompt user for whatever > reason whenever. Thus each command should be written with the assumption that > input should be presented to user on the frame where user types and with > assumption that user is not executing it from the buffer/window on which it > should act. You can achieve all this with tools already in Emacs, no need to > introduce any new concepts or macros, and it will also work regardless of the > window manager (I think). I have no idea how this would work for window managers that don't switch focus to the frame with the active minibuffer. > It is just that the old commands are not written with those assumptions, so I > rewrote Info commands in this patch. I am not sure you can achive that > with wrapping, but perhaps there is a way, you can try. Do you see some needs that can't be achieved with wrapping commands? > In my opinion wrapping is OK if we for some reason can't alter the code, but in > the case of Info and help mode, I don't see such reason, especially since it is > possible to do everything backwards compatible. On the negative side, > wrapping introduces double set of commands, so what are you saving? You are > wrapping code from "outside", while I have done it from "inside", but overall, > the principle is the same. On a plus side for wrappers is that you can actually > write a codegen for wrappers, hopefully in form of a macro and it will work for > other modes. So it is not only negatives, but you could also have both > approaches at the same time too :). Indeed, duplication of the command set is a drawback of wrapping commands. OTOH, wrapping commands could be created mechanically, and they are very small. So we have to decide what is worse: duplication of the command set, or altering all existing commands by adding a new window argument to all of them and making them more complicated. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-28 6:50 ` Juri Linkov @ 2023-06-28 21:52 ` Arthur Miller 2023-06-29 6:44 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-06-28 21:52 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 15717 bytes --] Juri Linkov <juri@linkov.net> writes: >>>>> But it seems this is not enough because with-selected-frame >>>>> still fails to switch focus to another frame. You need also >>>>> to use select-frame-set-input-focus. >>>> Where it fails? For me it prompts me on correct frame. I didn't want to switch >>>> focus on the info frame. I am aware of select-frame-set-input-focus, have used >>>> it in some test actually. >>> >>> Probably the behaviour depends on the window manager. >> >> Yes, I am quite sure it isn't "probably" but "surely" :), as I wrote earlier by >> the way. >> >>> With my window manager with-selected-frame displays >>> the prompt in another frame, but input is inserted >>> into the original buffer. Maybe we should have >>> a new option whether to use select-frame-set-input-focus? >> >> I am not sure I understand what you mean with input being inserted in the >> original buffer. > > For example, the original buffer is *scratch*. The minibuffer > pops up in another frame. I start typing. But letters get > inserted to *scratch*, not to the minibuffer. This is because > focus is not switched to another frame. Aha, ok, I understand now. I haven't checked that one, tbh. When I realized the minibufer pops on other frame, I realized I didn't liked it, so I reworked everything so focus stays on the frame where I type. As I remember now, I also have set Emacs option to auto select window based on the cursor, mouse-autoselect-window, so it might be that one that interfers too. I totally forgott that one earlier. >>>> Have you tested *everything*? Interactively and from lisp? >>> >>> I see no problems with this both interactively and from lisp: >> >> If you don't care to ask the user which window to choose when ambigous, then you >> don't have to care about this at all. If you don't want to take care of >> multiple windows with possibly ambigous names, user misstyping a name and >> possibly irregular name, then you don't need to prompt the user at all, just >> take the first info buffer you find or force user to *always* select manually a >> window and you are all good. But in my opinion it is not hard to have it slightly >> more polished and automated as I did. > > I don't know what do you mean by the words "don't care" > since this implementation still uses your function > 'info-window' that asks the user which window to choose. > Please look carefully at Info-index-other-window: > >>> #+begin_src emacs-lisp >>> (defmacro with-selected-window-frame (window &rest body) >>> `(let ((old-frame (selected-frame)) >>> (frame (window-frame ,window))) >>> (unless (eq frame old-frame) >>> (select-frame frame 'norecord) >>> (select-frame-set-input-focus frame 'norecord)) >>> (prog1 (with-selected-window ,window >>> ,@body) >>> (select-frame old-frame 'norecord) >>> (select-frame-set-input-focus old-frame 'norecord)))) >>> >>> (defun Info-index-other-window (topic &optional window) >>> (interactive >>> (with-selected-window-frame (info-window) >>> (append (eval (cadr (interactive-form 'Info-index))) >>> (list (selected-window))))) >>> (with-selected-window (or window (info-window)) >>> (Info-index topic))) >>> #+end_src I thought it was just left over while testing. Actually you want to keep the part I disslike most, and want to redo the part(s) I think are good :). When I wrote about the patch, I had three sections of text: the good, the bad and the ugly. Part about info-window and find-window-for-help was "the ugly" part. The bad one was about alterered signatures and the good one was about everythign been possible to implement user-friendly. >>> You can't avoid adding the window argument. Otherwise, you need >>> to invent such hacks as sending the window selected by the user >>> to the command body via a symbol property. >>> >>> But in the wrapper command like above there is no problem >>> of adding the window argument to the new command. >> >> I wasn't familiar with interactive form enough and didn't know how to connect >> the optional argument from within interactive form. Symbol-plist was just >> workaround for lack of a better knowledge :). I stated that explicitly in the >> mail with the patch, but you perhaps didn't have time to read the mail? > > I read all your mails carefully, and noted that there is no better alternative > than adding the window argument. Ok, I am sorry if I missunderstand you, I thought you mean I want add the window argument to *all commands*, which I don't want. Only those that are called possibly more then once, either because they are called from other command, or recursively. The argument is there just to avoid the prompting more then once. Now if you want to wrap everything, then I think you can avoid the argument completely *iff* you can switch to the info window before you call the wrapped command. >> Anyway, I understand now how is it done: >> >> (append (eval (cadr (interactive-form 'Info-index))) >> (list (selected-window))) >> >> I have to return a list of all arguments from interactive form; it was that >> simple. Thanks for the example. > > Indeed, this is only an example. More handling may be needed for > the return value of interactive-form. I never wrote complex interactive forms before, so I was a unfamiliar with the detials and a bit lazy to look around andd see how it is done, so I did a hack, but from your example I understand now how it is used. It was a couple of minutes to rework the patch without symbol-plist hack :). Thanks for showing it. >>> Maybe it's possible even to write a macro that will generate >>> such wrapper commands automatically from existing commands. >>> >>> It seems you assume that all commands should take a window. >> >> Why would I assume that? I wrote explicitly which commands were extended with >> an optional window argument and why. I don't feel for repeating entire text here, >> if you have interest, please take a look at the original email, you don't seem >> to have read it all. That mail answered your opening question, but I didn't want >> to point it out earlier to not sound impolite, instead I tried to clarify the >> problems and choices further. > > I was referring to your words from another mail that I read carefully: > >> What I am trying to say is that command has to be written with the assumptions >> that user can call it from any window and Emacs frame, and that all prompting >> should be done in the frame at which user types, so I have reworked info commands >> to work with those assumptions. > > I don't agree that all commands should be written with the assumption > that they can be called from any window. Only commands with global > keybindings usually prepare the buffer with the predefined name and mode, > then mode-local keybindings and mode-local commands assume that they are > operating in the right buffer. With "all commands" I ment all commands that wish to be callable from other buffers then those they act on. I didn't mean literally each and every in existence, since as I mentioned, command in Emacs can be anything, I have one that shutts down the computer. I am not sure if I would draw such absolut divider on what should and what would should not be written as any-window-command or how to call them. As I wrote I think it is really up to a command, but yes I do agree that not *all* commands should be callable from everywhere. I don't think I have enough understanding to yet draw some hard divider what should be and what should not be callable from any buffer. What I have got from this experiment is the understanding what is needed to write such command and what are possible problems. At least I think so, but there is probably more to it, that I haven't yet encountered. What I also think is that you could encapsulate that into a macro, similar to define-minor-mode, which encapsulate boring details and window switching, so people don't have to thinkg about it. That would eliminate need for a wrapper and perhaps made usage of prefix argument more uniform and predicatble. But that is just an idea, I haven't experimented. >t >> But there are no such assumption for most commands that work >>> only in the selected window. >> >> I am not sure what you are trying to say here. If we have several live buffers >> and wish to act on one, then the user has to choose one somehow, no? We can >> either try to automate stuff as I have tried in this patch, by prompting the >> user in ambigous case when system can't figure it on its own, or we can have a >> dumb system that forces user to *always* select a window prior to acting on a >> buffer. If you take a look at the original mail you will understand which commands >> has got the optional window argument and why, but if you don't prompt the user, >> than you don't need that argument at all. > > Only commands with global keybindings need a way to select the right buffer. > The best example of such command is the entry point to the Info browser > the command 'info' bound globally to 'C-h i'. This command allows the user > to select the right buffer "*info*<N>" by using the prefix arg. > > What you are trying to do is to copy this logic to all Info-local commands. I am not sure how you mean to use this, and why do you think C-h i is enough? I don't know what your assumption and expectation is. From very beginning I said I wish to minimize switching between windows. So I do wish more or less all info and help mode commands to be callable from other windows. Also I definitely want to have Info-mode-map on a prefix key, so which-key can show me the popup, since I have a memory like a gold fish and forgott which key is bound to what. That was one of reasons why I went away from pre/post hook thing. To be honest I am not sure I understand how you mean to use the thing. > And here comes the disagreement about the ways of doing this. I am not sure what I can say here. I understand it is a lot, 2k sloc patch is big; and I do understand there are potentiallys (or sure) bugs, but the work is done, I am using it myself, and it seem to be fine. If more people tested it, I don't think it would be too long before bugs are fixed. In my opinion, if we do things, lets do them properly from the beginning :). Piling hack on hack will just lead to more hacks. I don't that is how I see it, but I also don't think it is very complicated patch conceptually. I have attached a patch I reworked today so I can use it with the help-mode and with removed plist hack. If you look at the help-mode functions, you will see the change to them is quite trivial. I do disslike the help-mode/info-mode though. > I know that you already said about this, so hereby I confirm that I already > have seen your opinion:t > >> Making a wrapper just to put a call to a command into with-selected-window, >> instead of wrapping the body of that command with the same macro, is not so much >> better; just different. Sure "less intrusive" on a command itself, but there is >> a new command and then we have two where one can do the jobb. > > Replying to your opinion, I expressed a preference to avoid massive changes > in the existing functions. I totally understand your sentiment, and I myself would be all for a "magic pedal" that just switches the right thing on, but there is a limit to hackiness too. >> There is also a problem of prompting and input focus. As I wrote in response to >> Eli, each command is its own little program, and can prompt user for whatever >> reason whenever. Thus each command should be written with the assumption that >> input should be presented to user on the frame where user types and with >> assumption that user is not executing it from the buffer/window on which it >> should act. You can achieve all this with tools already in Emacs, no need to >> introduce any new concepts or macros, and it will also work regardless of the >> window manager (I think). > > I have no idea how this would work for window managers that don't switch focus > to the frame with the active minibuffer. If you pre-select window (and frame) before you call an Info-mode command, than the focus will be on that frame. You don't need to do anything special, since with-selected-frame should select frame, window and the visible buffer, so original command will execute in the "right" context. Problem as stated was, that when mouse cursor is on the "old frame", the old frame gets focus, which messes things up. Try to set mouse-autoselect-window in your Emacs, and foucs follow mouse in window manager, if you use some floating wm. I guess tiling WMs don't have this problem, but I don't know really, I haven't used anything other then dwm, and it was long time ago. >> It is just that the old commands are not written with those assumptions, so I >> rewrote Info commands in this patch. I am not sure you can achive that >> with wrapping, but perhaps there is a way, you can try. > > Do you see some needs that can't be achieved with wrapping commands? Depends on your ambitions :). Focus and input are potential problems. How do you plan to use wrappers? If you create double commands, then you will have a bunch of "-other-window" commands, would you have a separate mode maps for them, or how do you plan to present them to a user? Which one do you leave out and why? I personally don't think it is pretty to switch focus to other frame, either in vertical, left/right setup, or in horizontal tob/bottom setup. I prefer minibufer popping up on the frame where I already type. >> In my opinion wrapping is OK if we for some reason can't alter the code, but in >> the case of Info and help mode, I don't see such reason, especially since it is >> possible to do everything backwards compatible. On the negative side, >> wrapping introduces double set of commands, so what are you saving? You are >> wrapping code from "outside", while I have done it from "inside", but overall, >> the principle is the same. On a plus side for wrappers is that you can actually >> write a codegen for wrappers, hopefully in form of a macro and it will work for >> other modes. So it is not only negatives, but you could also have both >> approaches at the same time too :). > > Indeed, duplication of the command set is a drawback of wrapping commands. > OTOH, wrapping commands could be created mechanically, and they are very small. I think wrapping the function body in with-selected-window is even smaller, look at help-mode in attached patch. Info commands are many, relatively big, and have relatively complex interactive form, so the patch is massive, but conceptually it is simple. On plus side is that it is backwards compatible and does not introduce any new concepts at all, changes are mostly under the hood and user can just continue to use info as before or potentially from any buffer. > So we have to decide what is worse: duplication of the command set, Eli has already decided :). I see this as a done story. It is ok for me. I build my Emacs with custom patches anywway. > or altering all existing commands by adding a new window argument to all of them Nah; you don't need to alter all existing commands with optional window argument; I haven't altered them all in this patch either. Just those that would potentially prompt user multiple times. > and making them more complicated. Nah, they are not more complicated, look at the patched help-mode in the attached patch. Alternatively, we can also say that current practice of writing commands is inadequate in some cases. Just hook pre/post hook on a key, and live with that, it will work in "most cases" :). [-- Attachment #2: 0001-Use-help-and-Info-mode-commands-from-any-buffer.patch --] [-- Type: text/x-patch, Size: 89814 bytes --] From 1650339b51ebea70a8454835145afd01c7400c80 Mon Sep 17 00:00:00 2001 From: Arthur Miller <arthur.miller@live.com> Date: Wed, 28 Jun 2023 21:43:30 +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. * lisp/help-mode.el (help-link): New symbol used to establish a connection between a buffer and a help buffer. * lisp/help-mode.el (help-window): New function used to find a help window to act on. * 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. * lisp/info.el (Info-virtual-file-p): * lisp/info.el (Info-menu): * lisp/info.el (Info-next): * lisp/info.el (Info-prev): * lisp/info.el (Info-up): * lisp/info.el (Info-history): * lisp/info.el (Info-history-back): * lisp/info.el (Info-history-forward): * lisp/info.el (Info-directory): * lisp/info.el (Info-toc-insert): * lisp/info.el (Info-toc): * lisp/info.el (Info-extract-menu-item): * lisp/info.el (Info-nth-menu-item): * lisp/info.el (Info-top-node): * lisp/info.el (Info-final-node): * lisp/info.el (Info-forward-node): * lisp/info.el (Info-backward-node): * lisp/info.el (Info-next-menu-item): * lisp/info.el (Info-last-menu-item): * lisp/info.el (Info-next-preorder): * lisp/info.el (Info-last-preorder): * lisp/info.el (Info-scroll-up): * lisp/info.el (Info-scroll-down): * lisp/info.el (Info-mouse-scroll-up): * lisp/info.el (Info-next-reference): * lisp/info.el (Info-prev-reference): * lisp/info.el (Info-index): * lisp/info.el (Info-index-next): * lisp/info.el (Info-find-index-name): * lisp/info.el (Info-virtual-index): * lisp/info.el (Info-apropos): * lisp/info.el (Info-finder): * lisp/info.el (Info-undefined): * lisp/info.el (Info-follow-nearest-node): * lisp/info.el (Info-summary): * lisp/info.el (Info-copy-current-node-name): Info-mode commands adapted to be called from any buffer. * lisp/info.el (Info-describe-mode): * lisp/info.el (Info-quit-window): * lisp/info.el (Info-beginning-of-buffer): * lisp/info.el (Info-end-of-buffer): New commands. Do what they wrapped counterparts without 'Info-' prefix do, but specifically in Info buffer. * lisp/info.el (Info-link): New symbol used to establish a connection between a buffer and an Info buffer. * lisp/info.el (Info-mode-map): * lisp/info.el (Info-mode-menu): Update bindings to reflect the new commands for previously generic commands. * lisp/window.el (window-list-by-mode): * lisp/window.el (get-window-from-user): * lisp/window.el (get-numbered-window): New functions. Used internally as help functions for find-window-for-help. * lisp/window.el (find-window-for-help): New function. Used to obtain a window for Info- or help-mode. --- lisp/help-mode.el | 209 +++++-- lisp/info.el | 1317 +++++++++++++++++++++++++-------------------- lisp/window.el | 67 +++ 3 files changed, 961 insertions(+), 632 deletions(-) diff --git a/lisp/help-mode.el b/lisp/help-mode.el index bf64d032b65..2a637c9805c 100644 --- a/lisp/help-mode.el +++ b/lisp/help-mode.el @@ -47,7 +47,21 @@ help-mode-map "s" #'help-view-source "I" #'help-goto-lispref-info "i" #'help-goto-info - "c" #'help-customize) + "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 +74,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"] @@ -134,6 +148,9 @@ help-xref-stack-forward-item The format is (FUNCTION ARGS...).") (put 'help-xref-stack-forward-item 'permanent-local t) +(defvar-local help-link nil + "A connection to a help-mode buffer from another buffer.") + (setq-default help-xref-stack nil help-xref-stack-item nil) (setq-default help-xref-forward-stack nil help-xref-forward-stack-item nil) @@ -151,6 +168,48 @@ help-mode-hook :type 'hook :group 'help) \f +;; help function to help choose a help window to act on: +(defun help-window (&optional only-selected-frame) + "Return a help window from the list of all windows. + +If the current buffer is already a help buffer return selected +window, else find a help window from the list of visible help buffers. +When ONLY-SELECTED-FRAME is true, consider window list only on +selected frame. + +When invoked with a numeric prefix N return a help window named *help*<N>. +When called with a non-numeric prefix, C-u C-u, create a connection to a help +window that will be used as the default for further choice in case of multiple +help windows. If the connection is set, C-u prefix can bes used to act on +another help window but the connected one." + (let* ((all-frames (not only-selected-frame)) + window) + (cond ((equal current-prefix-arg '(16)) ; always ask + (setq help-link nil + current-prefix-arg nil + window (find-window-for-help 'help-mode all-frames)) + (when window (setq help-link window))) + ((equal current-prefix-arg '(4)) ; do in other help window + (setq current-prefix-arg nil + window (find-window-for-help + 'help-mode all-frames + (unless (eq major-mode 'help-mode) help-link)))) + ((and (not current-prefix-arg) (eq major-mode 'help-mode)) + (setq window (selected-window))) + (t + (setq window (if (and help-link + (window-live-p help-link) + (not (numberp current-prefix-arg))) + help-link + (find-window-for-help 'help-mode all-frames))))) + (unless window + (user-error + (if (numberp current-prefix-arg) + (format "No help buffer named *help*<%s> found." + current-prefix-arg) + "There are no visible help buffers."))) + window)) +\f ;; Button types used by help (define-button-type 'help-xref @@ -763,7 +822,73 @@ 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) + (with-selected-window (help-window) + (quit-window nil (selected-window)))) + +(defun help-describe-mode () + "As `describe-mode' but for *Help* buffer only." + (interactive) + (with-selected-window (help-window) + (with-current-buffer (window-buffer) + (describe-mode)))) + +(defun help-beginning-of-buffer () + "As `help-beginning-of-buffer' but for *Help* buffer only." + (interactive) + (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) + (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) + (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) + (with-selected-window (help-window) + (forward-button 1))) + +(defun help-backward-button () + (interactive) + (with-selected-window (help-window) + (backward-button 1))) + +(defun help-button-describe () + (interactive) + (with-selected-window (help-window) + (button-describe))) +(defun help-push-button () + (interactive) + (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") + (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") + (with-selected-window (help-window) + (scroll-down-command arg))) \f ;; Navigation/hyperlinking with xrefs @@ -810,25 +935,28 @@ 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"))) + (with-selected-window (help-window) + (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"))) + (with-selected-window (help-window) + (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))) + (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 +964,52 @@ 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))) + (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))) + (with-selected-window (help-window) + (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)) + (with-selected-window (help-window) + (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))) (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)) + (with-selected-window (help-window) + (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))))) + (with-selected-window (help-window) + (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..d32ffb64172 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -400,6 +400,54 @@ Info-virtual-nodes (defvar-local Info-current-node-virtual nil "Non-nil if the current Info node is virtual.") +(defvar-local Info-link nil + "A connection to an Info-mode buffer.") + +(defun info-window (&optional only-selected-frame) + "Return an info window from the list of all windows. + +If the current buffer is already an info buffer return selected +window, else find an info window from the list of visible info buffers. +When ONLY-SELECTED-FRAME is true, consider window list only on +selected frame. + +When invoked with a numeric prefix N return info window named *info*<N>. +When called with a non-numeric prefix, C-u C-u, create a connection to an Info +window that will be used as the default for further choice in case of multiple +info windows. If the connection is set, C-u prefix can bes used to acto on other +info window but the connected one. + +This function memoizes its result in symbols value slot. The last +result is not preserved between function calls." + (let* ((all-frames (not only-selected-frame)) + window) + (cond ((equal current-prefix-arg '(16)) ; always ask + (setq Info-link nil + current-prefix-arg nil + window (find-window-for-help 'Info-mode all-frames)) + (when window (setq Info-link window))) + ((equal current-prefix-arg '(4)) ; do in other info window + (setq current-prefix-arg nil + window (find-window-for-help + 'Info-mode all-frames + (unless (eq major-mode 'Info-mode) + Info-link)))) + ((and (not current-prefix-arg) (eq major-mode 'Info-mode)) + (setq window (selected-window))) + (t + (setq window (if (and Info-link + (window-live-p Info-link) + (not (numberp current-prefix-arg))) + Info-link + (find-window-for-help 'Info-mode all-frames))))) + (unless window + (user-error + (if (numberp current-prefix-arg) + (format "No info buffer named *info*<%s> found." + current-prefix-arg) + "There are no visible info buffers."))) + window)) + (defun Info-virtual-file-p (filename) "Check if Info file FILENAME is virtual." (Info-virtual-fun 'find-file filename nil)) @@ -787,7 +835,7 @@ info-setup (if (and (zerop (buffer-size)) (null Info-history)) ;; If we just created the Info buffer, go to the directory. - (Info-directory)))) + (Info-directory (selected-window))))) ;;;###autoload (defun info-emacs-manual () @@ -831,12 +879,12 @@ info-standalone ;; The return value is the value of point at the beginning of matching ;; REGEXP, if the function succeeds, nil otherwise. (defun Info-node-at-bob-matching (regexp) - (and (bobp) ; are we at beginning of buffer? - (looking-at "\^_") ; does it begin with node delimiter? + (and (bobp) ; are we at beginning of buffer? + (looking-at "\^_") ; does it begin with node delimiter? (let (beg) (forward-line 1) (setq beg (point)) - (forward-line 1) ; does the line after delimiter match REGEXP? + (forward-line 1) ; does the line after delimiter match REGEXP? (re-search-backward regexp beg t)))) (defun Info-find-file (filename &optional noerror no-pop-to-dir) @@ -2269,89 +2317,84 @@ Info-following-node-name (and (looking-at (Info-following-node-name-re)) (match-string-no-properties 1))) -(defun Info-next () +(defun Info-next (&optional window) "Go to the \"next\" node, staying on the same hierarchical level. This command doesn't descend into sub-nodes, like \\<Info-mode-map>\\[Info-forward-node] does." (interactive nil Info-mode) - ;; In case another window is currently selected - (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (with-selected-window (or window (info-window)) (Info-goto-node (Info-extract-pointer "next")))) -(defun Info-prev () +(defun Info-prev (&optional window) "Go to the \"previous\" node, staying on the same hierarchical level. This command doesn't go up to the parent node, like \\<Info-mode-map>\\[Info-backward-node] does." (interactive nil Info-mode) - ;; In case another window is currently selected - (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (with-selected-window (or window (info-window)) (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")))) -(defun Info-up (&optional same-file) +(defun Info-up (&optional same-file window) "Go to the superior node of this node. If SAME-FILE is non-nil, do not move to a different Info file." (interactive nil Info-mode) - ;; In case another window is currently selected - (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (with-selected-window (or window (info-window)) (let ((old-node Info-current-node) - (old-file Info-current-file) - (node (Info-extract-pointer "up")) p) + (old-file Info-current-file) + (node (Info-extract-pointer "up")) p) (and same-file - (string-match "^(" node) - (error "Up node is in another Info file")) + (string-match "^(" node) + (error "Up node is in another Info file")) (Info-goto-node node) (setq p (point)) (goto-char (point-min)) (if (and (stringp old-file) - (search-forward "\n* Menu:" nil t) - (re-search-forward - (if (string-equal old-node "Top") - (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")") - (concat "\n\\* +\\(" (regexp-quote old-node) - ":\\|[^:]+: +" (regexp-quote old-node) "\\)")) - nil t)) - (progn (beginning-of-line) (if (looking-at "^\\* ") (forward-char 2))) - (goto-char p) - (Info-restore-point Info-history)))) - ;; If scroll-conservatively is non-zero and less than 101, display - ;; as much of the superior node above the target line as possible. - (when (< 0 scroll-conservatively 101) - (recenter))) + (search-forward "\n* Menu:" nil t) + (re-search-forward + (if (string-equal old-node "Top") + (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")") + (concat "\n\\* +\\(" (regexp-quote old-node) + ":\\|[^:]+: +" (regexp-quote old-node) "\\)")) + nil t)) + (progn (beginning-of-line) (if (looking-at "^\\* ") (forward-char 2))) + (goto-char p) + (Info-restore-point Info-history))) + ;; If scroll-conservatively is non-zero and less than 101, display + ;; as much of the superior node above the target line as possible. + (when (< 0 scroll-conservatively 101) (recenter)))) (defun Info-history-back () "Go back in the history to the last node visited." (interactive nil Info-mode) - (or Info-history - (user-error "This is the first Info node you looked at")) - (let ((history-forward - (cons (list Info-current-file Info-current-node (point)) - Info-history-forward)) - filename nodename opoint) - (setq filename (car (car Info-history))) - (setq nodename (car (cdr (car Info-history)))) - (setq opoint (car (cdr (cdr (car Info-history))))) - (setq Info-history (cdr Info-history)) - (Info-find-node filename nodename) - (setq Info-history (cdr Info-history)) - (setq Info-history-forward history-forward) - (goto-char opoint))) + (with-selected-window (info-window) + (or Info-history + (user-error "This is the first Info node you looked at")) + (let ((history-forward + (cons (list Info-current-file Info-current-node (point)) + Info-history-forward)) + filename nodename opoint) + (setq filename (car (car Info-history))) + (setq nodename (car (cdr (car Info-history)))) + (setq opoint (car (cdr (cdr (car Info-history))))) + (setq Info-history (cdr Info-history)) + (Info-find-node filename nodename) + (setq Info-history (cdr Info-history)) + (setq Info-history-forward history-forward) + (goto-char opoint)))) (defalias 'Info-last 'Info-history-back) (defun Info-history-forward () "Go forward in the history of visited nodes." (interactive nil Info-mode) - (or Info-history-forward - (user-error "This is the last Info node you looked at")) - (let ((history-forward (cdr Info-history-forward)) - filename nodename opoint) - (setq filename (car (car Info-history-forward))) - (setq nodename (car (cdr (car Info-history-forward)))) - (setq opoint (car (cdr (cdr (car Info-history-forward))))) - (Info-find-node filename nodename) - (setq Info-history-forward history-forward) - (goto-char opoint))) + (with-selected-window (info-window) + (or Info-history-forward + (user-error "This is the last Info node you looked at")) + (let ((history-forward (cdr Info-history-forward)) + filename nodename opoint) + (setq filename (car (car Info-history-forward))) + (setq nodename (car (cdr (car Info-history-forward)))) + (setq opoint (car (cdr (cdr (car Info-history-forward))))) + (Info-find-node filename nodename) + (setq Info-history-forward history-forward) + (goto-char opoint)))) \f (add-to-list 'Info-virtual-files '("\\`dir\\'" @@ -2374,10 +2417,11 @@ Info-directory-find-node (Info-insert-dir)) ;;;###autoload -(defun Info-directory () +(defun Info-directory (&optional window) "Go to the Info directory node." (interactive) - (Info-find-node "dir" "top")) + (with-selected-window (or window (info-window)) + (Info-find-node "dir" "top"))) \f (add-to-list 'Info-virtual-files '("\\`\\*History\\*\\'" @@ -2416,9 +2460,10 @@ Info-history-find-node (defun Info-history () "Go to a node with a menu of visited nodes." (interactive nil Info-mode) - (Info-find-node "*History*" "Top") - (Info-next-reference) - (Info-next-reference)) + (with-selected-window (info-window) + (Info-find-node "*History*" "Top") + (Info-next-reference) + (Info-next-reference))) \f (add-to-list 'Info-virtual-nodes '("\\`\\*TOC\\*\\'" @@ -2453,12 +2498,13 @@ Info-toc "Go to a node with table of contents of the current Info file. Table of contents is created from the tree structure of menus." (interactive nil Info-mode) - (Info-find-node Info-current-file "*TOC*") - (let ((prev-node (nth 1 (car Info-history))) p) - (goto-char (point-min)) - (if (setq p (search-forward (concat "*Note " prev-node ":") nil t)) - (setq p (- p (length prev-node) 2))) - (goto-char (or p (point-min))))) + (with-selected-window (info-window) + (Info-find-node Info-current-file "*TOC*") + (let ((prev-node (nth 1 (car Info-history))) p) + (goto-char (point-min)) + (if (setq p (search-forward (concat "*Note " prev-node ":") nil t)) + (setq p (- p (length prev-node) 2))) + (goto-char (or p (point-min)))))) (defun Info-toc-insert (nodes node-list level curr-file) "Insert table of contents with references to nodes." @@ -2568,7 +2614,7 @@ Info-toc-nodes (cdr (assoc filename Info-toc-nodes))))) \f -(defun Info-follow-reference (footnotename &optional fork) +(defun Info-follow-reference (footnotename &optional fork window) "Follow cross reference named FOOTNOTENAME to the node it refers to. FOOTNOTENAME may be an abbreviation of the reference name. If FORK is non-nil (interactively with a prefix arg), show the node in @@ -2577,91 +2623,96 @@ Info-follow-reference (interactive (let ((completion-ignore-case t) (case-fold-search t) + (window (info-window)) completions default alt-default (start-point (point)) str i bol eol) - (save-excursion - ;; Store end and beginning of line. - (setq eol (line-end-position) - bol (line-beginning-position)) - (goto-char (point-min)) - (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t) - (setq str (match-string-no-properties 1)) - ;; See if this one should be the default. - (and (null default) - (<= (match-beginning 0) start-point) - (<= start-point (point)) - (setq default t)) - ;; See if this one should be the alternate default. - (and (null alt-default) - (and (<= bol (match-beginning 0)) - (<= (point) eol)) - (setq alt-default t)) - (setq i 0) - (while (setq i (string-match "[ \n\t]+" str i)) - (setq str (concat (substring str 0 i) " " - (substring str (match-end 0)))) - (setq i (1+ i))) - ;; Record as a completion and perhaps as default. - (if (eq default t) (setq default str)) - (if (eq alt-default t) (setq alt-default str)) - ;; Don't add this string if it's a duplicate. - (or (assoc-string str completions t) - (push str completions))) - (setq completions (nreverse completions))) - ;; If no good default was found, try an alternate. - (or default - (setq default alt-default)) - ;; If only one cross-reference found, then make it default. - (if (eq (length completions) 1) - (setq default (car completions))) - (if completions - (let ((input (completing-read (format-prompt "Follow reference named" - default) - completions nil t))) - (list (if (equal input "") - default input) - current-prefix-arg)) - (user-error "No cross-references in this node"))) - Info-mode) - - (unless footnotename - (error "No reference was specified")) - - (let (target i (str (concat "\\*note " (regexp-quote footnotename))) - (case-fold-search t)) - (while (setq i (string-search " " str i)) - (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i)))) - (setq i (+ i 6))) - (save-excursion - ;; Move point to the beginning of reference if point is on reference - (or (looking-at "\\*note[ \n\t]+") - (and (looking-back "\\*note[ \n\t]+" - (save-excursion (skip-chars-backward " \n\t") - (line-beginning-position))) - (goto-char (match-beginning 0))) - (if (and (save-excursion - (goto-char (+ (point) 5)) ; skip a possible *note - (re-search-backward "\\*note[ \n\t]+" nil t) - (looking-at str)) - (<= (point) (match-end 0))) - (goto-char (match-beginning 0)))) - ;; Go to the reference closest to point - (let ((next-ref (save-excursion (and (re-search-forward str nil t) - (+ (match-beginning 0) 5)))) - (prev-ref (save-excursion (and (re-search-backward str nil t) - (+ (match-beginning 0) 5))))) - (goto-char (cond ((and next-ref prev-ref) - (if (< (abs (- next-ref (point))) - (abs (- prev-ref (point)))) - next-ref prev-ref)) - ((or next-ref prev-ref)) - ((user-error "No cross-reference named %s" - footnotename)))) - (setq target (Info-extract-menu-node-name t)))) - (while (setq i (string-match "[ \t\n]+" target i)) - (setq target (concat (substring target 0 i) " " - (substring target (match-end 0)))) - (setq i (+ i 1))) - (Info-goto-node target fork))) + (with-current-buffer (window-buffer window) + (save-excursion + ;; Store end and beginning of line. + (setq eol (line-end-position) + bol (line-beginning-position)) + (goto-char (point-min)) + (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t) + (setq str (match-string-no-properties 1)) + ;; See if this one should be the default. + (and (null default) + (<= (match-beginning 0) start-point) + (<= start-point (point)) + (setq default t)) + ;; See if this one should be the alternate default. + (and (null alt-default) + (and (<= bol (match-beginning 0)) + (<= (point) eol)) + (setq alt-default t)) + (setq i 0) + (while (setq i (string-match "[ \n\t]+" str i)) + (setq str (concat (substring str 0 i) " " + (substring str (match-end 0)))) + (setq i (1+ i))) + ;; Record as a completion and perhaps as default. + (if (eq default t) (setq default str)) + (if (eq alt-default t) (setq alt-default str)) + ;; Don't add this string if it's a duplicate. + (or (assoc-string str completions t) + (push str completions))) + (setq completions (nreverse completions))) + ;; If no good default was found, try an alternate. + (or default + (setq default alt-default)) + ;; If only one cross-reference found, then make it default. + (if (eq (length completions) 1) + (setq default (car completions))) + (if completions + (let ((input (completing-read (format-prompt "Follow reference named" + default) + completions nil t))) + (list (if (equal input "") + default input) + current-prefix-arg)) + (user-error "No cross-references in this node"))) + (list Info-mode window))) + (let ((window (if (called-interactively-p 'any) + (get 'Info-follow-reference :selected-window) + (info-window)))) + (with-selected-window window + (unless footnotename + (error "No reference was specified")) + (let (target i (str (concat "\\*note " (regexp-quote footnotename))) + (case-fold-search t)) + (while (setq i (string-search " " str i)) + (setq str (concat (substring str 0 i) + "[ \t\n]+" (substring str (1+ i)))) + (setq i (+ i 6))) + (save-excursion + ;; Move point to the beginning of reference if point is on reference + (or (looking-at "\\*note[ \n\t]+") + (and (looking-back "\\*note[ \n\t]+" + (save-excursion (skip-chars-backward " \n\t") + (line-beginning-position))) + (goto-char (match-beginning 0))) + (if (and (save-excursion + (goto-char (+ (point) 5)) ; skip a possible *note + (re-search-backward "\\*note[ \n\t]+" nil t) + (looking-at str)) + (<= (point) (match-end 0))) + (goto-char (match-beginning 0)))) + ;; Go to the reference closest to point + (let ((next-ref (save-excursion (and (re-search-forward str nil t) + (+ (match-beginning 0) 5)))) + (prev-ref (save-excursion (and (re-search-backward str nil t) + (+ (match-beginning 0) 5))))) + (goto-char (cond ((and next-ref prev-ref) + (if (< (abs (- next-ref (point))) + (abs (- prev-ref (point)))) + next-ref prev-ref)) + ((or next-ref prev-ref)) + ((user-error "No cross-reference named %s" + footnotename)))) + (setq target (Info-extract-menu-node-name t)))) + (while (setq i (string-match "[ \t\n]+" target i)) + (setq target (concat (substring target 0 i) " " + (substring target (match-end 0)))) + (setq i (+ i 1))) + (Info-goto-node target fork))))) (defconst Info-menu-entry-name-re "\\(?:[^:]\\|:[^:,.;() \t\n]\\)*" ;; We allow newline because this is also used in Info-follow-reference, @@ -2788,8 +2839,7 @@ Info-complete-menu-item Info-complete-nodes))) (complete-with-action action completions string predicate)))))))) - -(defun Info-menu (menu-item &optional fork) +(defun Info-menu (menu-item &optional fork window) "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. @@ -2798,38 +2848,39 @@ Info-menu new buffer." (interactive (let (;; If point is within a menu item, use that item as the default - (default nil) - (p (point)) - beg - (case-fold-search t)) - (save-excursion - (goto-char (point-min)) - (if (not (search-forward "\n* menu:" nil t)) - (user-error "No menu in this node")) - (setq beg (point)) - (and (< (point) p) - (save-excursion - (goto-char p) - (end-of-line) - (if (re-search-backward (concat "\n\\* +\\(" - Info-menu-entry-name-re - "\\):") - beg t) - (setq default (match-string-no-properties 1)))))) - (let ((item nil)) - (while (null item) - (setq item (let ((completion-ignore-case t) - (Info-complete-menu-buffer (current-buffer))) - (completing-read (format-prompt "Menu item" default) - #'Info-complete-menu-item nil t nil nil - default)))) - (list item current-prefix-arg))) + (default nil) + (p (point)) + beg + (case-fold-search t) + (window (info-window))) + (with-current-buffer (window-buffer window) + (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 window)))) 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)))) + (with-selected-window (or window (info-window)) + (Info-goto-node (Info-extract-menu-item menu-item) + (and fork + (if (stringp fork) fork menu-item))))) (defun Info-extract-menu-item (menu-item) (setq menu-item (regexp-quote menu-item)) @@ -2869,32 +2920,35 @@ Info-nth-menu-item "Go to the node of the Nth menu item. N is the digit argument used to invoke this command." (interactive nil Info-mode) - (Info-goto-node - (Info-extract-menu-counting - (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0)))) + (with-selected-window (info-window) + (Info-goto-node + (Info-extract-menu-counting + (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0))))) (defun Info-top-node () "Go to the Top node of this file." (interactive nil Info-mode) - (Info-goto-node "Top")) + (with-selected-window (info-window) + (Info-goto-node "Top"))) (defun Info-final-node () "Go to the final node in this file." (interactive nil Info-mode) - (Info-goto-node "Top") - (let ((Info-history nil) - (case-fold-search t)) - ;; Go to the last node in the menu of Top. But don't delve into - ;; detailed node listings. - (Info-goto-node (Info-extract-menu-counting nil t)) - ;; If the last node in the menu is not last in pointer structure, - ;; move forward (but not down- or upward - see bug#1116) until we - ;; can't go any farther. - (while (Info-forward-node t t t) nil) - ;; Then keep moving down to last subnode, unless we reach an index. - (while (and (not (Info-index-node)) - (save-excursion (search-forward "\n* Menu:" nil t))) - (Info-goto-node (Info-extract-menu-counting nil))))) + (with-selected-window (info-window) + (Info-goto-node "Top") + (let ((Info-history nil) + (case-fold-search t)) + ;; Go to the last node in the menu of Top. But don't delve into + ;; detailed node listings. + (Info-goto-node (Info-extract-menu-counting nil t)) + ;; If the last node in the menu is not last in pointer structure, + ;; move forward (but not down- or upward - see bug#1116) until we + ;; can't go any farther. + (while (Info-forward-node t t t) nil) + ;; Then keep moving down to last subnode, unless we reach an index. + (while (and (not (Info-index-node)) + (save-excursion (search-forward "\n* Menu:" nil t))) + (Info-goto-node (Info-extract-menu-counting nil)))))) (defun Info-forward-node (&optional not-down not-up no-error) "Go forward one node, considering all nodes as forming one sequence. @@ -2905,160 +2959,166 @@ Info-forward-node NOT-UP non-nil means don't go to parent nodes, and NO-ERROR non-nil means don't signal a user-error if there's no node to go to." (interactive nil Info-mode) - (goto-char (point-min)) - (forward-line 1) - (let ((case-fold-search t)) - ;; three possibilities, in order of priority: - ;; 1. next node is in a menu in this node (but not in an index) - ;; 2. next node is next at same level - ;; 3. next node is up and next - (cond ((and (not not-down) - (save-excursion (search-forward "\n* menu:" nil t)) - (not (Info-index-node))) - (Info-goto-node (Info-extract-menu-counting 1)) - t) - ((save-excursion (search-backward "next:" nil t)) - (Info-next) - t) - ((and (not not-up) - (save-excursion (search-backward "up:" nil t)) - ;; Use string-equal, not equal, to ignore text props. - (not (string-equal (downcase (Info-extract-pointer "up")) - "top"))) - (let ((old-node Info-current-node)) - (Info-up) - (let ((old-history Info-history) - success) - (unwind-protect - (setq success (Info-forward-node t nil no-error)) - (or success (Info-goto-node old-node))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history))))) - (no-error nil) - (t (user-error "No pointer forward from this node"))))) + (with-selected-window (info-window) + (goto-char (point-min)) + (forward-line 1) + (let ((case-fold-search t)) + ;; three possibilities, in order of priority: + ;; 1. next node is in a menu in this node (but not in an index) + ;; 2. next node is next at same level + ;; 3. next node is up and next + (cond ((and (not not-down) + (save-excursion (search-forward "\n* menu:" nil t)) + (not (Info-index-node))) + (Info-goto-node (Info-extract-menu-counting 1)) + t) + ((save-excursion (search-backward "next:" nil t)) + (Info-next (selected-window)) + t) + ((and (not not-up) + (save-excursion (search-backward "up:" nil t)) + ;; Use string-equal, not equal, to ignore text props. + (not (string-equal (downcase (Info-extract-pointer "up")) + "top"))) + (let ((old-node Info-current-node)) + (Info-up nil (selected-window)) + (let ((old-history Info-history) + success) + (unwind-protect + (setq success (Info-forward-node t nil no-error)) + (or success (Info-goto-node old-node))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history))))) + (no-error nil) + (t (user-error "No pointer forward from this node")))))) (defun Info-backward-node () "Go backward one node, considering all nodes as forming one sequence. If the current node has a \"previous\" node, go to it, descending into its last sub-node, if any; otherwise go \"up\" to the parent node." (interactive nil Info-mode) - (let ((prevnode (Info-extract-pointer "prev[ious]*" t)) - (upnode (Info-extract-pointer "up" t)) - (case-fold-search t)) - (cond ((and upnode (string-search "(" upnode)) - (user-error "First node in file")) - ((and upnode (or (null prevnode) - ;; Use string-equal, not equal, - ;; to ignore text properties. - (string-equal (downcase prevnode) - (downcase upnode)))) - (Info-up)) - (prevnode - ;; If we move back at the same level, - ;; go down to find the last subnode*. - (Info-prev) - (let ((old-history Info-history)) - (while (and (not (Info-index-node)) - (save-excursion (search-forward "\n* Menu:" nil t))) - (Info-goto-node (Info-extract-menu-counting nil))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history)))) - (t - (user-error "No pointer backward from this node"))))) + (with-selected-window (info-window) + (let ((prevnode (Info-extract-pointer "prev[ious]*" t)) + (upnode (Info-extract-pointer "up" t)) + (case-fold-search t)) + (cond ((and upnode (string-search "(" upnode)) + (user-error "First node in file")) + ((and upnode (or (null prevnode) + ;; Use string-equal, not equal, + ;; to ignore text properties. + (string-equal (downcase prevnode) + (downcase upnode)))) + (Info-up nil (selected-window))) + (prevnode + ;; If we move back at the same level, + ;; go down to find the last subnode*. + (Info-prev (selected-window)) + (let ((old-history Info-history)) + (while (and (not (Info-index-node)) + (save-excursion (search-forward "\n* Menu:" nil t))) + (Info-goto-node (Info-extract-menu-counting nil))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history)))) + (t + (user-error "No pointer backward from this node")))))) (define-obsolete-function-alias 'Info-exit #'quit-window "27.1") -(defun Info-next-menu-item () +(defun Info-next-menu-item (&optional window) "Go to the node of the next menu item." (interactive nil Info-mode) - ;; Bind this in case the user sets it to nil. - (let* ((case-fold-search t) - (node - (save-excursion - (forward-line -1) - (search-forward "\n* menu:" nil t) - (and (search-forward "\n* " nil t) - (Info-extract-menu-node-name))))) - (if node (Info-goto-node node) - (user-error "No more items in menu")))) + (with-selected-window (or window (info-window)) + ;; Bind this in case the user sets it to nil. + (let* ((case-fold-search t) + (node + (save-excursion + (forward-line -1) + (search-forward "\n* menu:" nil t) + (and (search-forward "\n* " nil t) + (Info-extract-menu-node-name))))) + (if node (Info-goto-node node) + (user-error "No more items in menu"))))) (defun Info-last-menu-item () "Go to the node of the previous menu item." (interactive nil Info-mode) - (save-excursion - (forward-line 1) - ;; Bind this in case the user sets it to nil. - (let* ((case-fold-search t) - (beg (save-excursion - (and (search-backward "\n* menu:" nil t) - (point))))) - (or (and beg (search-backward "\n* " beg t)) - (user-error "No previous items in menu"))) - (Info-goto-node (save-excursion - (goto-char (match-end 0)) - (Info-extract-menu-node-name))))) + (with-selected-window (info-window) + (save-excursion + (forward-line 1) + ;; Bind this in case the user sets it to nil. + (let* ((case-fold-search t) + (beg (save-excursion + (and (search-backward "\n* menu:" nil t) + (point))))) + (or (and beg (search-backward "\n* " beg t)) + (user-error "No previous items in menu"))) + (Info-goto-node (save-excursion + (goto-char (match-end 0)) + (Info-extract-menu-node-name)))))) (defmacro Info-no-error (&rest body) `(condition-case nil (progn ,@body t) (error nil))) -(defun Info-next-preorder () +(defun Info-next-preorder (&optional window) "Go to the next subnode or the next node, or go up a level." (interactive nil Info-mode) - (cond ((Info-no-error (Info-next-menu-item))) - ((Info-no-error (Info-next))) - ((Info-no-error (Info-up t)) - ;; Since we have already gone thru all the items in this menu, - ;; go up to the end of this node. - (goto-char (point-max)) - ;; Since logically we are done with the node with that menu, - ;; move on from it. But don't add intermediate nodes - ;; to the history on recursive calls. - (let ((old-history Info-history)) - (Info-next-preorder) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history)))) - (t - (user-error "No more nodes")))) - -(defun Info-last-preorder () + (with-selected-window (or window (info-window)) + (cond ((Info-no-error (Info-next-menu-item (selected-window)))) + ((Info-no-error (Info-next (selected-window)))) + ((Info-no-error (Info-up t)) + ;; Since we have already gone thru all the items in this menu, + ;; go up to the end of this node. + (goto-char (point-max)) + ;; Since logically we are done with the node with that menu, + ;; move on from it. But don't add intermediate nodes + ;; to the history on recursive calls. + (let ((old-history Info-history)) + (Info-next-preorder (selected-window)) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history)))) + (t + (user-error "No more nodes"))))) + +(defun Info-last-preorder (&optional window) "Go to the last node, popping up a level if there is none." (interactive nil Info-mode) - (cond ((and Info-scroll-prefer-subnodes - (Info-no-error - (Info-last-menu-item) - ;; If we go down a menu item, go to the end of the node - ;; so we can scroll back through it. - (goto-char (point-max)))) - ;; Keep going down, as long as there are nested menu nodes. - (let ((old-history Info-history)) - (while (Info-no-error - (Info-last-menu-item) - ;; If we go down a menu item, go to the end of the node - ;; so we can scroll back through it. - (goto-char (point-max)))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history))) - (recenter -1)) - ((and (Info-no-error (Info-extract-pointer "prev")) - (not (equal (Info-extract-pointer "up") - (Info-extract-pointer "prev")))) - (Info-no-error (Info-prev)) - (goto-char (point-max)) - (let ((old-history Info-history)) - (while (Info-no-error - (Info-last-menu-item) - ;; If we go down a menu item, go to the end of the node - ;; so we can scroll back through it. - (goto-char (point-max)))) - (if Info-history-skip-intermediate-nodes - (setq Info-history old-history))) - (recenter -1)) - ((Info-no-error (Info-up t)) - (goto-char (point-min)) - (let ((case-fold-search t)) - (or (search-forward "\n* Menu:" nil t) - (goto-char (point-max))))) - (t (user-error "No previous nodes")))) + (with-selected-window (or window (info-window)) + (cond ((and Info-scroll-prefer-subnodes + (Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the node + ;; so we can scroll back through it. + (goto-char (point-max)))) + ;; Keep going down, as long as there are nested menu nodes. + (let ((old-history Info-history)) + (while (Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the node + ;; so we can scroll back through it. + (goto-char (point-max)))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history))) + (recenter -1)) + ((and (Info-no-error (Info-extract-pointer "prev")) + (not (equal (Info-extract-pointer "up") + (Info-extract-pointer "prev")))) + (Info-no-error (Info-prev (selected-window))) + (goto-char (point-max)) + (let ((old-history Info-history)) + (while (Info-no-error + (Info-last-menu-item) + ;; If we go down a menu item, go to the end of the node + ;; so we can scroll back through it. + (goto-char (point-max)))) + (if Info-history-skip-intermediate-nodes + (setq Info-history old-history))) + (recenter -1)) + ((Info-no-error (Info-up t)) + (goto-char (point-min)) + (let ((case-fold-search t)) + (or (search-forward "\n* Menu:" nil t) + (goto-char (point-max))))) + (t (user-error "No previous nodes"))))) (defun Info-scroll-up () "Scroll one screenful forward in Info, considering all nodes as one sequence. @@ -3073,25 +3133,25 @@ Info-scroll-up the menu of a node, it moves to subnode indicated by the following menu item. (That case won't normally result from this command, but can happen in other ways.)" - (interactive nil Info-mode) - (if (or (< (window-start) (point-min)) - (> (window-start) (point-max))) - (set-window-start (selected-window) (point))) - (let* ((case-fold-search t) - (virtual-end (save-excursion - (goto-char (point-min)) - (if (and Info-scroll-prefer-subnodes - (search-forward "\n* Menu:" nil t)) - (point) - (point-max))))) - (if (or (< virtual-end (window-start)) - (pos-visible-in-window-p virtual-end)) - (cond - (Info-scroll-prefer-subnodes (Info-next-preorder)) - ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1)))) - (t (Info-next-preorder))) - (scroll-up)))) + (with-selected-window (info-window) + (if (or (< (window-start) (point-min)) + (> (window-start) (point-max))) + (set-window-start (selected-window) (point))) + (let* ((case-fold-search t) + (virtual-end (save-excursion + (goto-char (point-min)) + (if (and Info-scroll-prefer-subnodes + (search-forward "\n* Menu:" nil t)) + (point) + (point-max))))) + (if (or (< virtual-end (window-start)) + (pos-visible-in-window-p virtual-end)) + (cond + (Info-scroll-prefer-subnodes (Info-next-preorder (selected-window))) + ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1)))) + (t (Info-next-preorder (selected-window)))) + (scroll-up))))) (defun Info-mouse-scroll-up (e) "Scroll one screenful forward in Info, using the mouse. @@ -3109,21 +3169,22 @@ Info-scroll-down beginning of a node, that goes to the previous node or back up to the parent node." (interactive nil Info-mode) - (if (or (< (window-start) (point-min)) - (> (window-start) (point-max))) - (set-window-start (selected-window) (point))) - (let* ((case-fold-search t) - (current-point (point)) - (virtual-end - (and Info-scroll-prefer-subnodes - (save-excursion - (setq current-point (line-beginning-position)) - (goto-char (point-min)) - (search-forward "\n* Menu:" current-point t))))) - (if (or virtual-end - (pos-visible-in-window-p (point-min) nil t)) - (Info-last-preorder) - (scroll-down)))) + (with-selected-window (info-window) + (if (or (< (window-start) (point-min)) + (> (window-start) (point-max))) + (set-window-start (selected-window) (point))) + (let* ((case-fold-search t) + (current-point (point)) + (virtual-end + (and Info-scroll-prefer-subnodes + (save-excursion + (setq current-point (line-beginning-position)) + (goto-char (point-min)) + (search-forward "\n* Menu:" current-point t))))) + (if (or virtual-end + (pos-visible-in-window-p (point-min) nil t)) + (Info-last-preorder (selected-window)) + (scroll-down))))) (defun Info-mouse-scroll-down (e) "Scroll one screenful backward in Info, using the mouse. @@ -3170,60 +3231,62 @@ Info-prev-reference-or-link (goto-char plink)) (if pxref (goto-char (or (match-beginning 1) (match-beginning 0))))))) -(defun Info-next-reference (&optional recur count) +(defun Info-next-reference (&optional recur count window) "Move cursor to the next cross-reference or menu item in the node. If COUNT is non-nil (interactively with a prefix arg), jump over COUNT cross-references." (interactive "i\np" Info-mode) - (unless count - (setq count 1)) - (if (< count 0) - (Info-prev-reference recur (- count)) - (while (unless (zerop count) (setq count (1- count))) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") - (old-pt (point)) - (case-fold-search t)) - (or (eobp) (forward-char 1)) - (or (Info-next-reference-or-link pat 'link) - (progn - (goto-char (point-min)) - (or (Info-next-reference-or-link pat 'link) - (progn - (goto-char old-pt) - (user-error "No cross references in this node"))))) - (if (looking-at "\\* Menu:") - (if recur - (user-error "No cross references in this node") - (Info-next-reference t)) - (if (looking-at "^\\* ") - (forward-char 2))))))) - -(defun Info-prev-reference (&optional recur count) + (with-selected-window (or window (info-window)) + (unless count + (setq count 1)) + (if (< count 0) + (Info-prev-reference recur (- count) (selected-window)) + (while (unless (zerop count) (setq count (1- count))) + (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") + (old-pt (point)) + (case-fold-search t)) + (or (eobp) (forward-char 1)) + (or (Info-next-reference-or-link pat 'link) + (progn + (goto-char (point-min)) + (or (Info-next-reference-or-link pat 'link) + (progn + (goto-char old-pt) + (user-error "No cross references in this node"))))) + (if (looking-at "\\* Menu:") + (if recur + (user-error "No cross references in this node") + (Info-next-reference t nil (selected-window))) + (if (looking-at "^\\* ") + (forward-char 2)))))))) + +(defun Info-prev-reference (&optional recur count window) "Move cursor to the previous cross-reference or menu item in the node. If COUNT is non-nil (interactively with a prefix arg), jump over COUNT cross-references." (interactive "i\np" Info-mode) - (unless count - (setq count 1)) - (if (< count 0) - (Info-next-reference recur (- count)) - (while (unless (zerop count) (setq count (1- count))) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") - (old-pt (point)) - (case-fold-search t)) - (or (Info-prev-reference-or-link pat 'link) - (progn - (goto-char (point-max)) - (or (Info-prev-reference-or-link pat 'link) - (progn - (goto-char old-pt) - (user-error "No cross references in this node"))))) - (if (looking-at "\\* Menu:") - (if recur - (user-error "No cross references in this node") - (Info-prev-reference t)) - (if (looking-at "^\\* ") - (forward-char 2))))))) + (with-selected-window (or window (info-window)) + (unless count + (setq count 1)) + (if (< count 0) + (Info-next-reference recur (- count) (selected-window)) + (while (unless (zerop count) (setq count (1- count))) + (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") + (old-pt (point)) + (case-fold-search t)) + (or (Info-prev-reference-or-link pat 'link) + (progn + (goto-char (point-max)) + (or (Info-prev-reference-or-link pat 'link) + (progn + (goto-char old-pt) + (user-error "No cross references in this node"))))) + (if (looking-at "\\* Menu:") + (if recur + (user-error "No cross references in this node") + (Info-prev-reference t nil (selected-window))) + (if (looking-at "^\\* ") + (forward-char 2)))))))) \f (defun Info-index-nodes (&optional file) "Return a list of names of all index nodes in Info FILE. @@ -3337,72 +3400,80 @@ info--ensure-not-in-directory-node "type \\[Info-menu] to select a manual"))))) ;;;###autoload -(defun Info-index (topic) +(defun Info-index (topic &optional window) "Look up a string TOPIC in the index for this manual and go to that entry. If there are no exact matches to the specified topic, this chooses the first match which is a case-insensitive substring of a topic. Use the \\<Info-mode-map>\\[Info-index-next] command to see the other matches. Give an empty topic name to go to the Index node itself." (interactive - (list - (let ((completion-ignore-case t) - (Info-complete-menu-buffer (clone-buffer)) - (Info-complete-nodes (Info-index-nodes)) - (Info-history-list nil)) - (info--ensure-not-in-directory-node) - (unwind-protect - (with-current-buffer Info-complete-menu-buffer - (Info-goto-index) - (completing-read "Index topic: " #'Info-complete-menu-item)) - (kill-buffer Info-complete-menu-buffer))))) - (info--ensure-not-in-directory-node) - ;; Strip leading colon in topic; index format does not allow them. - (if (and (stringp topic) - (> (length topic) 0) - (= (aref topic 0) ?:)) - (setq topic (substring topic 1))) - (let ((orignode Info-current-node) - (pattern (format "\n\\* +\\([^\n]*\\(%s\\)[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" - (regexp-quote topic))) - node (nodes (Info-index-nodes)) - (ohist-list Info-history-list) - (case-fold-search t)) - (Info-goto-index) - (or (equal topic "") - (let ((matches nil) - (exact nil) - ;; We bind Info-history to nil for internal node-switches so - ;; that we don't put junk in the history. In the first - ;; Info-goto-index call, above, we do update the history - ;; because that is what the user's previous node choice into it. - (Info-history nil) - found) - (while - (progn - (goto-char (point-min)) - (while (re-search-forward pattern nil t) - (let ((entry (match-string-no-properties 1)) - (nodename (match-string-no-properties 3)) - (line (string-to-number (concat "0" (match-string 4))))) - (add-text-properties - (- (match-beginning 2) (match-beginning 1)) - (- (match-end 2) (match-beginning 1)) - '(face info-index-match) entry) - (push (list entry nodename Info-current-node line) matches))) - (setq nodes (cdr nodes) node (car nodes))) - (Info-goto-node node)) - (or matches - (progn - (Info-goto-node orignode) - (user-error "No `%s' in index" topic))) - ;; Here it is a feature that assoc is case-sensitive. - (while (setq found (assoc topic matches)) - (setq exact (cons found exact) - matches (delq found matches))) - (setq Info-history-list ohist-list) - (setq Info-index-alternatives (nconc exact (nreverse matches)) - Info--current-index-alternative 0) - (Info-index-next 0))))) + (let ((frame-to-prompt-in (selected-frame)) + (window (info-window))) + (with-selected-window window + (list + (let ((completion-ignore-case t) + (Info-complete-menu-buffer (clone-buffer)) + (Info-complete-nodes (Info-index-nodes)) + (Info-history-list nil)) + (info--ensure-not-in-directory-node) + (unwind-protect + (with-current-buffer Info-complete-menu-buffer + (Info-goto-index) + (with-selected-frame frame-to-prompt-in + (completing-read "Index topic: " + #'Info-complete-menu-item))) + (kill-buffer Info-complete-menu-buffer))) + window))) + Info-mode) + (with-selected-window (or window (info-window)) + (info--ensure-not-in-directory-node) + ;; Strip leading colon in topic; index format does not allow them. + (if (and (stringp topic) + (> (length topic) 0) + (= (aref topic 0) ?:)) + (setq topic (substring topic 1))) + (let ((orignode Info-current-node) + (pattern (format "\n\\* +\\([^\n]*\\(%s\\)[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" + (regexp-quote topic))) + node (nodes (Info-index-nodes)) + (ohist-list Info-history-list) + (case-fold-search t)) + (Info-goto-index) + (or (equal topic "") + (let ((matches nil) + (exact nil) + ;; We bind Info-history to nil for internal node-switches so + ;; that we don't put junk in the history. In the first + ;; Info-goto-index call, above, we do update the history + ;; because that is what the user's previous node choice into it. + (Info-history nil) + found) + (while + (progn + (goto-char (point-min)) + (while (re-search-forward pattern nil t) + (let ((entry (match-string-no-properties 1)) + (nodename (match-string-no-properties 3)) + (line (string-to-number (concat "0" (match-string 4))))) + (add-text-properties + (- (match-beginning 2) (match-beginning 1)) + (- (match-end 2) (match-beginning 1)) + '(face info-index-match) entry) + (push (list entry nodename Info-current-node line) matches))) + (setq nodes (cdr nodes) node (car nodes))) + (Info-goto-node node)) + (or matches + (progn + (Info-goto-node orignode) + (user-error "No `%s' in index" topic))) + ;; Here it is a feature that assoc is case-sensitive. + (while (setq found (assoc topic matches)) + (setq exact (cons found exact) + matches (delq found matches))) + (setq Info-history-list ohist-list) + (setq Info-index-alternatives (nconc exact (nreverse matches)) + Info--current-index-alternative 0) + (Info-index-next 0)))))) (defun Info-index-next (num) "Go to the next matching index item from the last \\<Info-mode-map>\\[Info-index] command. @@ -3411,45 +3482,46 @@ Info-index-next Also see the `Info-warn-on-index-alternatives-wrap' user option." (interactive "p" Info-mode) - (unless Info-index-alternatives - (user-error "No previous `i' command")) - (let ((index (+ Info--current-index-alternative num)) - (total (length Info-index-alternatives)) - (next-key (key-description (where-is-internal - 'Info-index-next overriding-local-map t)))) - (if (and Info-warn-on-index-alternatives-wrap - (> total 1) - (cond - ((< index 0) - (setq Info--current-index-alternative (- total 2)) - (message - "No previous matches, use `%s' to continue from end of list" - next-key) - t) - ((>= index total) - (setq Info--current-index-alternative -1) - (message - "No previous matches, use `%s' to continue from start of list" - next-key) - t))) - () ; Do nothing - (setq index (mod index total) - Info--current-index-alternative index) - (let ((entry (nth index Info-index-alternatives))) - (Info-goto-node (nth 1 entry)) - (if (> (nth 3 entry) 0) - ;; Forward 2 lines less because `Info-find-node-2' initially - ;; puts point to the 2nd line. - (forward-line (- (nth 3 entry) 2)) - (forward-line 3) ; don't search in headers - (Info-find-index-name (car entry))) - (message "Found `%s' in %s. %s" - (car entry) - (nth 2 entry) - (if (> total 1) - (format-message - "(%s total; use `%s' for next)" total next-key) - "(Only match)")))))) + (with-selected-window (info-window) + (unless Info-index-alternatives + (user-error "No previous `i' command")) + (let ((index (+ Info--current-index-alternative num)) + (total (length Info-index-alternatives)) + (next-key (key-description (where-is-internal + 'Info-index-next overriding-local-map t)))) + (if (and Info-warn-on-index-alternatives-wrap + (> total 1) + (cond + ((< index 0) + (setq Info--current-index-alternative (- total 2)) + (message + "No previous matches, use `%s' to continue from end of list" + next-key) + t) + ((>= index total) + (setq Info--current-index-alternative -1) + (message + "No previous matches, use `%s' to continue from start of list" + next-key) + t))) + () ; Do nothing + (setq index (mod index total) + Info--current-index-alternative index) + (let ((entry (nth index Info-index-alternatives))) + (Info-goto-node (nth 1 entry)) + (if (> (nth 3 entry) 0) + ;; Forward 2 lines less because `Info-find-node-2' initially + ;; puts point to the 2nd line. + (forward-line (- (nth 3 entry) 2)) + (forward-line 3) ; don't search in headers + (Info-find-index-name (car entry))) + (message "Found `%s' in %s. %s" + (car entry) + (nth 2 entry) + (if (> total 1) + (format-message + "(%s total; use `%s' for next)" total next-key) + "(Only match)"))))))) (defun Info-find-index-name (name) "Move point to the place within the current node where NAME is defined." @@ -3526,40 +3598,47 @@ Info-virtual-index-find-node (format "*Index for ‘%s’*::" (cdr (nth 0 nodeinfo))) (cdr (nth 0 nodeinfo))))))))) -(defun Info-virtual-index (topic) +(defun Info-virtual-index (topic &optional window) "Show a node with all lines in the index containing a string TOPIC. Like `Info-index' but displays a node with index search results. Give an empty topic name to go to the node with links to previous search results." ;; `interactive' is a copy from `Info-index' (interactive - (list - (let ((completion-ignore-case t) - (Info-complete-menu-buffer (clone-buffer)) - (Info-complete-nodes (Info-index-nodes)) - (Info-history-list nil)) - (info--ensure-not-in-directory-node) - (unwind-protect - (with-current-buffer Info-complete-menu-buffer - (Info-goto-index) - (completing-read "Index topic: " #'Info-complete-menu-item)) - (kill-buffer Info-complete-menu-buffer)))) + (let ((frame-to-prompt-in (selected-frame)) + (window (info-window))) + (with-selected-window window + (list + (let ((completion-ignore-case t) + (Info-complete-menu-buffer (clone-buffer)) + (Info-complete-nodes (Info-index-nodes)) + (Info-history-list nil)) + (info--ensure-not-in-directory-node) + (unwind-protect + (with-current-buffer Info-complete-menu-buffer + (Info-goto-index) + (with-selected-frame frame-to-prompt-in + (completing-read "Index topic: " + #'Info-complete-menu-item))) + (kill-buffer Info-complete-menu-buffer))) + window))) Info-mode) (if (equal topic "") (Info-find-node Info-current-file "*Index*") - (unless (assoc (cons Info-current-file topic) Info-virtual-index-nodes) - (let ((orignode Info-current-node) - (ohist-list Info-history-list)) - ;; Reuse `Info-index' to set `Info-index-alternatives'. - (Info-index topic) - (push (cons (cons Info-current-file topic) Info-index-alternatives) - Info-virtual-index-nodes) - ;; Clean up unnecessary side-effects of `Info-index'. - (setq Info-history-list ohist-list) - (Info-goto-node orignode) - (message ""))) - (Info-find-node Info-current-file - (format "*Index for ‘%s’*" topic)))) + (with-selected-window (or window (info-window)) + (unless (assoc (cons Info-current-file topic) Info-virtual-index-nodes) + (let ((orignode Info-current-node) + (ohist-list Info-history-list)) + ;; Reuse `Info-index' to set `Info-index-alternatives'. + (Info-index topic) + (push (cons (cons Info-current-file topic) Info-index-alternatives) + Info-virtual-index-nodes) + ;; Clean up unnecessary side-effects of `Info-index'. + (setq Info-history-list ohist-list) + (Info-goto-node orignode) + (message ""))) + (Info-find-node Info-current-file + (format "*Index for ‘%s’*" topic))))) \f (add-to-list 'Info-virtual-files '("\\`\\*Apropos\\*\\'" @@ -3694,20 +3773,25 @@ info-apropos "Search indices of all known Info files on your system for STRING. If REGEXP (interactively, the prefix), use a regexp match. -Display a menu of the possible matches." +Display a menu of the possible matches in selected window. If given +numeric prefix argument display results in window *info*<N>. With +non-numeric argument prompt user for info buffer to display results in." (interactive "sIndex apropos: \nP") - (if (equal string "") - (Info-find-node Info-apropos-file "Top") - (let ((nodes Info-apropos-nodes) - nodename) - (while (and nodes (not (equal string (nth 1 (car nodes))))) - (setq nodes (cdr nodes))) - (if nodes - (Info-find-node Info-apropos-file (car (car nodes)) nil nil t) - (setq nodename (format "Index for ‘%s’" string)) - (push (list nodename string (Info-apropos-matches string regexp)) - Info-apropos-nodes) - (Info-find-node Info-apropos-file nodename))))) + (with-selected-window (if current-prefix-arg + (info-window) + (selected-window)) + (if (equal string "") + (Info-find-node Info-apropos-file "Top") + (let ((nodes Info-apropos-nodes) + nodename) + (while (and nodes (not (equal string (nth 1 (car nodes))))) + (setq nodes (cdr nodes))) + (if nodes + (Info-find-node Info-apropos-file (car (car nodes)) nil nil t) + (setq nodename (format "Index for ‘%s’" string)) + (push (list nodename string (Info-apropos-matches string regexp)) + Info-apropos-nodes) + (Info-find-node Info-apropos-file nodename)))))) \f (add-to-list 'Info-virtual-files '("\\`\\*Finder.*\\*\\'" @@ -3834,25 +3918,33 @@ Info-finder-find-node (insert "Can’t find package description.\n\n")))))) ;;;###autoload -(defun info-finder (&optional keywords) +(defun info-finder (&optional keywords window) "Display descriptions of the keywords in the Finder virtual manual. -In interactive use, a prefix argument directs this command to read +In interactive use, a non-numeric prefix argument directs this command to read a list of keywords separated by comma. After that, it displays a node -with a list of packages that contain all specified keywords." +with a list of packages that contain all specified keywords. Numeric +prefix argument will choose window with name *info*<N> if such window +exists otherwise it will prompt the user to choose a window." (interactive - (when current-prefix-arg - (require 'finder) - (list - (completing-read-multiple - "Keywords (separated by comma): " - (mapcar #'symbol-name (mapcar #'car (append finder-known-keywords - (finder-unknown-keywords)))) - nil t)))) + (let ((window (info-window))) + (when (and current-prefix-arg + (not (numberp current-prefix-arg))) + (require 'finder) + (let ((current-prefix-arg nil)) + (with-current-buffer (window-buffer window) + (list + (completing-read-multiple + "Keywords (separated by comma): " + (mapcar #'symbol-name (mapcar #'car (append finder-known-keywords + (finder-unknown-keywords)))) + nil t) + window)))))) + (setq current-prefix-arg nil) (require 'finder) - (if keywords - (Info-find-node Info-finder-file (mapconcat 'identity keywords ", ")) - (Info-find-node Info-finder-file "Top"))) - + (with-selected-window (or window (info-window)) + (if keywords + (Info-find-node Info-finder-file (mapconcat 'identity keywords ", ")) + (Info-find-node Info-finder-file "Top")))) \f (defun Info-undefined () "Make command be undefined in Info." @@ -3937,7 +4029,7 @@ Info-mouse-follow-nearest-node (mouse-set-point click) (and (not (Info-follow-nearest-node)) (save-excursion (forward-line 1) (eobp)) - (Info-next-preorder))) + (Info-next-preorder (selected-window)))) (defun Info-follow-nearest-node (&optional fork) "Follow a node reference near point. @@ -4030,15 +4122,15 @@ Info-mouse-follow-link (Info-goto-node link-args)) ;; These special values of the `link-args' property are used ;; for navigation; see `Info-fontify-node'. - ((eq link-args 'prev) (Info-prev)) - ((eq link-args 'next) (Info-next)) - ((eq link-args 'up) (Info-up))))) + ((eq link-args 'prev) (Info-prev (selected-window))) + ((eq link-args 'next) (Info-next (selected-window))) + ((eq link-args 'up) (Info-up (selected-window)))))) \f (defvar Info-mode-map (let ((map (make-keymap))) (suppress-keymap map) - (define-key map "." 'beginning-of-buffer) + (define-key map "." 'Info-beginning-of-buffer) (define-key map " " 'Info-scroll-up) (define-key map [?\S-\ ] 'Info-scroll-down) (define-key map "\C-m" 'Info-follow-nearest-node) @@ -4060,10 +4152,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 +4163,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 +4171,7 @@ Info-mode-map (define-key map "m" 'Info-menu) (define-key map "n" 'Info-next) (define-key map "p" 'Info-prev) - (define-key map "q" 'quit-window) + (define-key map "q" 'Info-quit-window) (define-key map "r" 'Info-history-forward) (define-key map "s" 'Info-search) (define-key map "S" 'Info-search-case-sensitively) @@ -4104,7 +4196,6 @@ Info-mode-map map) "Keymap containing Info commands.") - (defun Info-check-pointer (item) "Non-nil if ITEM is present in this node." (condition-case nil @@ -4125,7 +4216,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 +4414,21 @@ Info-copy-current-node-name The name of the Info file is prepended to the node name in parentheses. With a zero prefix arg, put the name inside a function call to `info'." (interactive "P" Info-mode) - (unless Info-current-node - (user-error "No current Info node")) - (let ((node (if (stringp Info-current-file) - (concat "(" (file-name-sans-extension - (file-name-nondirectory Info-current-file)) - ") " - Info-current-node)))) - (if (zerop (prefix-numeric-value arg)) - (setq node (concat "(info \"" node "\")"))) - (unless (stringp Info-current-file) - (setq node (format "(Info-find-node '%S '%S)" - Info-current-file Info-current-node))) - (kill-new node) - (message "%s" node))) + (with-selected-window (info-window) + (unless Info-current-node + (user-error "No current Info node")) + (let ((node (if (stringp Info-current-file) + (concat "(" (file-name-sans-extension + (file-name-nondirectory Info-current-file)) + ") " + Info-current-node)))) + (if (zerop (prefix-numeric-value arg)) + (setq node (concat "(info \"" node "\")"))) + (unless (stringp Info-current-file) + (setq node (format "(Info-find-node '%S '%S)" + Info-current-file Info-current-node))) + (kill-new node) + (message "%s" node)))) \f ;; Info mode is suitable only for specially formatted data. @@ -5230,8 +5322,7 @@ Info-speedbar-browser ;; Make sure that speedbar is active (speedbar-frame-mode 1) ;; Now, throw us into Info mode on speedbar. - (speedbar-change-initial-expansion-list "Info") - ) + (speedbar-change-initial-expansion-list "Info")) ;; speedbar loads dframe at runtime. (declare-function dframe-select-attached-frame "dframe" (&optional frame)) @@ -5509,6 +5600,44 @@ info--manual-names (apply-partially #'Info-read-node-name-2 Info-directory-list (mapcar #'car Info-suffix-list)))))))) +\f +;;; General buffer manipulation support +;; commands from special-mode wrapped to work on Info-mode only + +(defun Info-beginning-of-buffer () + "Move point to the beginning of *info* buffer." + (interactive) + (with-selected-window (info-window) + (goto-char (point-min)))) + +(defun Info-end-of-buffer () + "Move point to the beginning of *info* buffer." + (interactive) + (with-selected-window (info-window) + (goto-char (point-max)))) + +(defun Info-describe-mode () + "As `describe-mode' but for Info-mode only." + (interactive) + ;; I guess we are good if there is any buffer in Info-mode so + ;; we do a special here to prevent prompting + ;; If there are no info buffer, use (info-window) to signal + ;; the error; this to not duplicate the error message in two places + (catch 'found + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (eq major-mode 'Info-mode) + (describe-mode) + (throw 'found buffer)))) + (throw 'found + (with-temp-buffer + (Info-mode) + (describe-mode))))) + +(defun Info-quit-window () + (interactive) + (with-selected-window (info-window) + (quit-window nil (selected-window)))) (provide 'info) diff --git a/lisp/window.el b/lisp/window.el index 5964fe37ee6..2cc5cc6a274 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -10741,6 +10741,73 @@ window-prefix-map "0" #'delete-windows-on) (define-key ctl-x-map "w" window-prefix-map) +\f +;; help function to find an Info- or help-mode window +(defun window-list-by-mode (mode &optional exclude all-frames) + "Get list of windows to act on, displaying live buffer with major mode MODE. + +If EXCLUDE is not nil it should be a window to exclude from the list. +All-FRAMES has same meaning as in `walk-windows' function." + (let (windows) + (walk-windows + (lambda (w) + (with-current-buffer (window-buffer w) + (and (eq major-mode mode) + (not (eq exclude w)) + (not (eq (selected-window) w)) + (push (cons (prin1-to-string w) w) windows)))) + nil all-frames) + windows)) + +(defun get-window-from-user (window-list) + "Ask user to choose a window to act on via completing read. + +This is internal helper function used by `find-window-for-help'" + (cdr + (assoc + (completing-read "Act on window: " window-list) window-list))) + +(defun get-numbered-window (window-list N) + "Get a window from a WINDOW-LIST matching string \"<%N>\" in its name." + (let ((numbered-window-list + (let ((new-list nil)) + (dolist (elt window-list) + (when (string-match-p (format "<%s>" N) (car elt)) + (push elt new-list))) + new-list))) + (cond + ((= (length numbered-window-list) 0) nil) + ((= (length numbered-window-list) 1) + (cdar numbered-window-list)) + ((> (length numbered-window-list) 1) + (get-window-from-user numbered-window-list))))) + +(defun find-window-for-help (mode &optional all-frames exclude) + "Find window displaing buffer with major-mode MODE. + +With numeric argument N, return window containing <N> in the name otherwise nil. +With non-numeric prefix, prompt user to select one of the buffers matching +MODE. + +Meaning of ALL-FRAMES is same as in `window-list-1'. +EXCLIDE is an Info window not to be considered as a candidate." + (let ((window-list (remq (selected-window) + (window-list-by-mode mode exclude all-frames)))) + (when (> (length window-list) 0) + (let ((window + (cond + ((numberp current-prefix-arg) + (let ((window + (get-numbered-window window-list current-prefix-arg))) + (if window + window + (when (> (length window-list) 0) ;; misstyped number arg? + (get-window-from-user window-list))))) + ((> (length window-list) 1) + (get-window-from-user window-list)) + (t (cdar window-list))))) + window)))) + (provide 'window) ;;; window.el ends here -- 2.40.0 ^ permalink raw reply related [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-28 21:52 ` Arthur Miller @ 2023-06-29 6:44 ` Juri Linkov 2023-06-29 12:42 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-06-29 6:44 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >>>>>> But it seems this is not enough because with-selected-frame >>>>>> still fails to switch focus to another frame. You need also >>>>>> to use select-frame-set-input-focus. >>>>> Where it fails? For me it prompts me on correct frame. I didn't want to switch >>>>> focus on the info frame. I am aware of select-frame-set-input-focus, have used >>>>> it in some test actually. >>>> >>>> Probably the behaviour depends on the window manager. >>> >>> Yes, I am quite sure it isn't "probably" but "surely" :), as I wrote earlier by >>> the way. >>> >>>> With my window manager with-selected-frame displays >>>> the prompt in another frame, but input is inserted >>>> into the original buffer. Maybe we should have >>>> a new option whether to use select-frame-set-input-focus? >>> >>> I am not sure I understand what you mean with input being inserted in the >>> original buffer. >> >> For example, the original buffer is *scratch*. The minibuffer >> pops up in another frame. I start typing. But letters get >> inserted to *scratch*, not to the minibuffer. This is because >> focus is not switched to another frame. > > Aha, ok, I understand now. I haven't checked that one, tbh. When I realized the > minibufer pops on other frame, I realized I didn't liked it, so I reworked > everything so focus stays on the frame where I type. As I remember now, I > also have set Emacs option to auto select window based on the cursor, > mouse-autoselect-window, so it might be that one that interfers too. I totally > forgott that one earlier. I tried to set mouse-autoselect-window, but still input focus is not switched to another frame with e.g. (with-selected-frame (next-frame) (read-string "Prompt: ")) >>>>> Have you tested *everything*? Interactively and from lisp? >>>> >>>> I see no problems with this both interactively and from lisp: >>> >>> If you don't care to ask the user which window to choose when ambigous, then you >>> don't have to care about this at all. If you don't want to take care of >>> multiple windows with possibly ambigous names, user misstyping a name and >>> possibly irregular name, then you don't need to prompt the user at all, just >>> take the first info buffer you find or force user to *always* select manually a >>> window and you are all good. But in my opinion it is not hard to have it slightly >>> more polished and automated as I did. >> >> I don't know what do you mean by the words "don't care" >> since this implementation still uses your function >> 'info-window' that asks the user which window to choose. >> Please look carefully at Info-index-other-window: >> >>>> #+begin_src emacs-lisp >>>> (defmacro with-selected-window-frame (window &rest body) >>>> `(let ((old-frame (selected-frame)) >>>> (frame (window-frame ,window))) >>>> (unless (eq frame old-frame) >>>> (select-frame frame 'norecord) >>>> (select-frame-set-input-focus frame 'norecord)) >>>> (prog1 (with-selected-window ,window >>>> ,@body) >>>> (select-frame old-frame 'norecord) >>>> (select-frame-set-input-focus old-frame 'norecord)))) >>>> >>>> (defun Info-index-other-window (topic &optional window) >>>> (interactive >>>> (with-selected-window-frame (info-window) >>>> (append (eval (cadr (interactive-form 'Info-index))) >>>> (list (selected-window))))) >>>> (with-selected-window (or window (info-window)) >>>> (Info-index topic))) >>>> #+end_src > > I thought it was just left over while testing. Actually you want to keep the > part I disslike most, and want to redo the part(s) I think are good :). When I > wrote about the patch, I had three sections of text: the good, the bad and the > ugly. Part about info-window and find-window-for-help was "the ugly" part. The > bad one was about alterered signatures and the good one was about everythign > been possible to implement user-friendly. Please explain what is ugly in info-window. I can see that in find-window-for-help the name is ugly. Indeed, there is nothing specific to help in find-window-for-help. >>>> You can't avoid adding the window argument. Otherwise, you need >>>> to invent such hacks as sending the window selected by the user >>>> to the command body via a symbol property. >>>> >>>> But in the wrapper command like above there is no problem >>>> of adding the window argument to the new command. >>> >>> I wasn't familiar with interactive form enough and didn't know how to connect >>> the optional argument from within interactive form. Symbol-plist was just >>> workaround for lack of a better knowledge :). I stated that explicitly in the >>> mail with the patch, but you perhaps didn't have time to read the mail? >> >> I read all your mails carefully, and noted that there is no better alternative >> than adding the window argument. > > Ok, I am sorry if I missunderstand you, I thought you mean I want add the window > argument to *all commands*, which I don't want. Only those that are called > possibly more then once, either because they are called from other command, or > recursively. The argument is there just to avoid the prompting more then > once. Now if you want to wrap everything, then I think you can avoid the > argument completely *iff* you can switch to the info window before you call the > wrapped command. I agree that the window argument is not needed when you can wrap with e.g. (with-selected-window (info-window) (Info-index topic))) >>> Anyway, I understand now how is it done: >>> >>> (append (eval (cadr (interactive-form 'Info-index))) >>> (list (selected-window))) >>> >>> I have to return a list of all arguments from interactive form; it was that >>> simple. Thanks for the example. >> >> Indeed, this is only an example. More handling may be needed for >> the return value of interactive-form. > > I never wrote complex interactive forms before, so I was a unfamiliar with the > detials and a bit lazy to look around andd see how it is done, so I did a > hack, but from your example I understand now how it is used. It was a couple of > minutes to rework the patch without symbol-plist hack :). Thanks for showing it. > >>>> Maybe it's possible even to write a macro that will generate >>>> such wrapper commands automatically from existing commands. >>>> >>>> It seems you assume that all commands should take a window. >>> >>> Why would I assume that? I wrote explicitly which commands were extended with >>> an optional window argument and why. I don't feel for repeating entire text here, >>> if you have interest, please take a look at the original email, you don't seem >>> to have read it all. That mail answered your opening question, but I didn't want >>> to point it out earlier to not sound impolite, instead I tried to clarify the >>> problems and choices further. >> >> I was referring to your words from another mail that I read carefully: >> >>> What I am trying to say is that command has to be written with the assumptions >>> that user can call it from any window and Emacs frame, and that all prompting >>> should be done in the frame at which user types, so I have reworked info commands >>> to work with those assumptions. >> >> I don't agree that all commands should be written with the assumption >> that they can be called from any window. Only commands with global >> keybindings usually prepare the buffer with the predefined name and mode, >> then mode-local keybindings and mode-local commands assume that they are >> operating in the right buffer. > > With "all commands" I ment all commands that wish to be callable from other > buffers then those they act on. I didn't mean literally each and every in > existence, since as I mentioned, command in Emacs can be anything, I have one > that shutts down the computer. > > I am not sure if I would draw such absolut divider on what should and what would > should not be written as any-window-command or how to call them. As I wrote I > think it is really up to a command, but yes I do agree that not *all* commands > should be callable from everywhere. I don't think I have enough understanding to > yet draw some hard divider what should be and what should not be callable from > any buffer. What I have got from this experiment is the understanding what is > needed to write such command and what are possible problems. At least I think > so, but there is probably more to it, that I haven't yet encountered. > > What I also think is that you could encapsulate that into a macro, similar to > define-minor-mode, which encapsulate boring details and window switching, so > people don't have to thinkg about it. That would eliminate need for a wrapper > and perhaps made usage of prefix argument more uniform and predicatble. But that > is just an idea, I haven't experimented. Do you mean such a macro will eliminate the need of altering existing commands? This would be nice. Something like this: (keymap-global-set "M-i d" (with-window-wrapper 'Info-directory)) >>>> But there are no such assumption for most commands that work >>>> only in the selected window. >>> >>> I am not sure what you are trying to say here. If we have several live buffers >>> and wish to act on one, then the user has to choose one somehow, no? We can >>> either try to automate stuff as I have tried in this patch, by prompting the >>> user in ambigous case when system can't figure it on its own, or we can have a >>> dumb system that forces user to *always* select a window prior to acting on a >>> buffer. If you take a look at the original mail you will understand which commands >>> has got the optional window argument and why, but if you don't prompt the user, >>> than you don't need that argument at all. >> >> Only commands with global keybindings need a way to select the right buffer. >> The best example of such command is the entry point to the Info browser >> the command 'info' bound globally to 'C-h i'. This command allows the user >> to select the right buffer "*info*<N>" by using the prefix arg. >> >> What you are trying to do is to copy this logic to all Info-local commands. > > I am not sure how you mean to use this, and why do you think C-h i is enough? > > I don't know what your assumption and expectation is. From very beginning I said > I wish to minimize switching between windows. So I do wish more or less all info > and help mode commands to be callable from other windows. > > Also I definitely want to have Info-mode-map on a prefix key, so which-key can > show me the popup, since I have a memory like a gold fish and forgott which key > is bound to what. That was one of reasons why I went away from pre/post hook > thing. To be honest I am not sure I understand how you mean to use the thing. There is no problem with a prefix key since you can create a new keymap with other-window commands like (keymap-global-set "M-i" Info-other-window-map) (keymap-set Info-other-window-map "d" 'Info-directory-other-window) ... >> And here comes the disagreement about the ways of doing this. > > I am not sure what I can say here. I understand it is a lot, 2k sloc patch is > big; and I do understand there are potentiallys (or sure) bugs, but the work is > done, I am using it myself, and it seem to be fine. If more people tested it, I > don't think it would be too long before bugs are fixed. In my opinion, if we do > things, lets do them properly from the beginning :). Piling hack on hack will > just lead to more hacks. I don't that is how I see it, but I also don't think it > is very complicated patch conceptually. > > I have attached a patch I reworked today so I can use it with the help-mode and > with removed plist hack. If you look at the help-mode functions, you will see > the change to them is quite trivial. I do disslike the help-mode/info-mode though. > >> I know that you already said about this, so hereby I confirm that I already >> have seen your opinion:t >> >>> Making a wrapper just to put a call to a command into with-selected-window, >>> instead of wrapping the body of that command with the same macro, is not so much >>> better; just different. Sure "less intrusive" on a command itself, but there is >>> a new command and then we have two where one can do the jobb. >> >> Replying to your opinion, I expressed a preference to avoid massive changes >> in the existing functions. > > I totally understand your sentiment, and I myself would be all for a "magic > pedal" that just switches the right thing on, but there is a limit to hackiness > too. > >>> There is also a problem of prompting and input focus. As I wrote in response to >>> Eli, each command is its own little program, and can prompt user for whatever >>> reason whenever. Thus each command should be written with the assumption that >>> input should be presented to user on the frame where user types and with >>> assumption that user is not executing it from the buffer/window on which it >>> should act. You can achieve all this with tools already in Emacs, no need to >>> introduce any new concepts or macros, and it will also work regardless of the >>> window manager (I think). >> >> I have no idea how this would work for window managers that don't switch focus >> to the frame with the active minibuffer. > > If you pre-select window (and frame) before you call an Info-mode command, than > the focus will be on that frame. You don't need to do anything special, since > with-selected-frame should select frame, window and the visible buffer, so > original command will execute in the "right" context. Problem as stated was, > that when mouse cursor is on the "old frame", the old frame gets focus, which > messes things up. Try to set mouse-autoselect-window in your Emacs, and foucs > follow mouse in window manager, if you use some floating wm. I guess tiling WMs > don't have this problem, but I don't know really, I haven't used anything other > then dwm, and it was long time ago. 'mouse-autoselect-window' has no effect with my WM. So focus switches to another frame only with 'C-x 5 o'. But this is not surprising since 'other-frame' at the end explicitly calls 'select-frame-set-input-focus'. There is no such call in 'with-selected-frame'. >>> It is just that the old commands are not written with those assumptions, so I >>> rewrote Info commands in this patch. I am not sure you can achive that >>> with wrapping, but perhaps there is a way, you can try. >> >> Do you see some needs that can't be achieved with wrapping commands? > > Depends on your ambitions :). Focus and input are potential problems. How do you > plan to use wrappers? If you create double commands, then you will have a bunch of > "-other-window" commands, would you have a separate mode maps for them, or how > do you plan to present them to a user? Which one do you leave out and why? I > personally don't think it is pretty to switch focus to other frame, either in > vertical, left/right setup, or in horizontal tob/bottom setup. I prefer > minibufer popping up on the frame where I already type. Interesting. So the interactive form doesn't need to switch to another frame with 'with-selected-frame' and can use only 'with-current-buffer', so the minibuffer will use the contents of Info buffer. >>> In my opinion wrapping is OK if we for some reason can't alter the code, but in >>> the case of Info and help mode, I don't see such reason, especially since it is >>> possible to do everything backwards compatible. On the negative side, >>> wrapping introduces double set of commands, so what are you saving? You are >>> wrapping code from "outside", while I have done it from "inside", but overall, >>> the principle is the same. On a plus side for wrappers is that you can actually >>> write a codegen for wrappers, hopefully in form of a macro and it will work for >>> other modes. So it is not only negatives, but you could also have both >>> approaches at the same time too :). >> >> Indeed, duplication of the command set is a drawback of wrapping commands. >> OTOH, wrapping commands could be created mechanically, and they are very small. > > I think wrapping the function body in with-selected-window is even smaller, look > at help-mode in attached patch. I don't see how they are smaller. They are exactly like wrapper commands ;) ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-29 6:44 ` Juri Linkov @ 2023-06-29 12:42 ` Arthur Miller 2023-06-29 15:00 ` [External] : " Drew Adams 2023-06-29 17:44 ` Juri Linkov 0 siblings, 2 replies; 53+ messages in thread From: Arthur Miller @ 2023-06-29 12:42 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>>>>>> But it seems this is not enough because with-selected-frame >>>>>>> still fails to switch focus to another frame. You need also >>>>>>> to use select-frame-set-input-focus. >>>>>> Where it fails? For me it prompts me on correct frame. I didn't want to switch >>>>>> focus on the info frame. I am aware of select-frame-set-input-focus, have used >>>>>> it in some test actually. >>>>> >>>>> Probably the behaviour depends on the window manager. >>>> >>>> Yes, I am quite sure it isn't "probably" but "surely" :), as I wrote earlier by >>>> the way. >>>> >>>>> With my window manager with-selected-frame displays >>>>> the prompt in another frame, but input is inserted >>>>> into the original buffer. Maybe we should have >>>>> a new option whether to use select-frame-set-input-focus? >>>> >>>> I am not sure I understand what you mean with input being inserted in the >>>> original buffer. >>> >>> For example, the original buffer is *scratch*. The minibuffer >>> pops up in another frame. I start typing. But letters get >>> inserted to *scratch*, not to the minibuffer. This is because >>> focus is not switched to another frame. >> >> Aha, ok, I understand now. I haven't checked that one, tbh. When I realized the >> minibufer pops on other frame, I realized I didn't liked it, so I reworked >> everything so focus stays on the frame where I type. As I remember now, I >> also have set Emacs option to auto select window based on the cursor, >> mouse-autoselect-window, so it might be that one that interfers too. I totally >> forgott that one earlier. > > I tried to set mouse-autoselect-window, but still input focus > is not switched to another frame with e.g. > > (with-selected-frame (next-frame) (read-string "Prompt: ")) Yes with-selected-frame does switch focus to a freame; I have also used it in the patch, for the same purpose to switch focus back to user frame. >>>>>> Have you tested *everything*? Interactively and from lisp? >>>>> >>>>> I see no problems with this both interactively and from lisp: >>>> >>>> If you don't care to ask the user which window to choose when ambigous, then you >>>> don't have to care about this at all. If you don't want to take care of >>>> multiple windows with possibly ambigous names, user misstyping a name and >>>> possibly irregular name, then you don't need to prompt the user at all, just >>>> take the first info buffer you find or force user to *always* select manually a >>>> window and you are all good. But in my opinion it is not hard to have it slightly >>>> more polished and automated as I did. >>> >>> I don't know what do you mean by the words "don't care" >>> since this implementation still uses your function >>> 'info-window' that asks the user which window to choose. >>> Please look carefully at Info-index-other-window: >>> >>>>> #+begin_src emacs-lisp >>>>> (defmacro with-selected-window-frame (window &rest body) >>>>> `(let ((old-frame (selected-frame)) >>>>> (frame (window-frame ,window))) >>>>> (unless (eq frame old-frame) >>>>> (select-frame frame 'norecord) >>>>> (select-frame-set-input-focus frame 'norecord)) >>>>> (prog1 (with-selected-window ,window >>>>> ,@body) >>>>> (select-frame old-frame 'norecord) >>>>> (select-frame-set-input-focus old-frame 'norecord)))) >>>>> >>>>> (defun Info-index-other-window (topic &optional window) >>>>> (interactive >>>>> (with-selected-window-frame (info-window) >>>>> (append (eval (cadr (interactive-form 'Info-index))) >>>>> (list (selected-window))))) >>>>> (with-selected-window (or window (info-window)) >>>>> (Info-index topic))) >>>>> #+end_src >> >> I thought it was just left over while testing. Actually you want to keep the >> part I disslike most, and want to redo the part(s) I think are good :). When I >> wrote about the patch, I had three sections of text: the good, the bad and the >> ugly. Part about info-window and find-window-for-help was "the ugly" part. The >> bad one was about alterered signatures and the good one was about everythign >> been possible to implement user-friendly. > > Please explain what is ugly in info-window. I can see that in > find-window-for-help the name is ugly. Indeed, there is nothing > specific to help in find-window-for-help. Separating funcitonality in two places. Part of required functionality is in that one, and part is in info/help-window. In my opinion the implementation is messy. >>>>> You can't avoid adding the window argument. Otherwise, you need >>>>> to invent such hacks as sending the window selected by the user >>>>> to the command body via a symbol property. >>>>> >>>>> But in the wrapper command like above there is no problem >>>>> of adding the window argument to the new command. >>>> >>>> I wasn't familiar with interactive form enough and didn't know how to connect >>>> the optional argument from within interactive form. Symbol-plist was just >>>> workaround for lack of a better knowledge :). I stated that explicitly in the >>>> mail with the patch, but you perhaps didn't have time to read the mail? >>> >>> I read all your mails carefully, and noted that there is no better alternative >>> than adding the window argument. >> >> Ok, I am sorry if I missunderstand you, I thought you mean I want add the window >> argument to *all commands*, which I don't want. Only those that are called >> possibly more then once, either because they are called from other command, or >> recursively. The argument is there just to avoid the prompting more then >> once. Now if you want to wrap everything, then I think you can avoid the >> argument completely *iff* you can switch to the info window before you call the >> wrapped command. > > I agree that the window argument is not needed when you can wrap with e.g. > > (with-selected-window (info-window) > (Info-index topic))) No, not really, window argument is not about wrapping but about prompting, look through the code of the patch and you will understand. It is not needed when there is no risk of prompting user more than once. Again, ;), it is needed only in commands that call themselves recursively or in commands that are called from other commands, explicitly or via some other call. That is actually relatively *small* number of commands. Info-mode does that in several places. Help-mode does not do that at all. If you are going to switch to info window before you call original command you shouldn't need it at all. If you don't prompt user, then you wouldn't need it at all, you would just possibly select the same window multiple times, but without visible effect to the user, it does not matter. >>>> Anyway, I understand now how is it done: >>>> >>>> (append (eval (cadr (interactive-form 'Info-index))) >>>> (list (selected-window))) >>>> >>>> I have to return a list of all arguments from interactive form; it was that >>>> simple. Thanks for the example. >>> >>> Indeed, this is only an example. More handling may be needed for >>> the return value of interactive-form. >> >> I never wrote complex interactive forms before, so I was a unfamiliar with the >> detials and a bit lazy to look around andd see how it is done, so I did a >> hack, but from your example I understand now how it is used. It was a couple of >> minutes to rework the patch without symbol-plist hack :). Thanks for showing it. >> >>>>> Maybe it's possible even to write a macro that will generate >>>>> such wrapper commands automatically from existing commands. >>>>> >>>>> It seems you assume that all commands should take a window. >>>> >>>> Why would I assume that? I wrote explicitly which commands were extended with >>>> an optional window argument and why. I don't feel for repeating entire text here, >>>> if you have interest, please take a look at the original email, you don't seem >>>> to have read it all. That mail answered your opening question, but I didn't want >>>> to point it out earlier to not sound impolite, instead I tried to clarify the >>>> problems and choices further. >>> >>> I was referring to your words from another mail that I read carefully: >>> >>>> What I am trying to say is that command has to be written with the assumptions >>>> that user can call it from any window and Emacs frame, and that all prompting >>>> should be done in the frame at which user types, so I have reworked info commands >>>> to work with those assumptions. >>> >>> I don't agree that all commands should be written with the assumption >>> that they can be called from any window. Only commands with global >>> keybindings usually prepare the buffer with the predefined name and mode, >>> then mode-local keybindings and mode-local commands assume that they are >>> operating in the right buffer. >> >> With "all commands" I ment all commands that wish to be callable from other >> buffers then those they act on. I didn't mean literally each and every in >> existence, since as I mentioned, command in Emacs can be anything, I have one >> that shutts down the computer. >> >> I am not sure if I would draw such absolut divider on what should and what would >> should not be written as any-window-command or how to call them. As I wrote I >> think it is really up to a command, but yes I do agree that not *all* commands >> should be callable from everywhere. I don't think I have enough understanding to >> yet draw some hard divider what should be and what should not be callable from >> any buffer. What I have got from this experiment is the understanding what is >> needed to write such command and what are possible problems. At least I think >> so, but there is probably more to it, that I haven't yet encountered. >> >> What I also think is that you could encapsulate that into a macro, similar to >> define-minor-mode, which encapsulate boring details and window switching, so >> people don't have to thinkg about it. That would eliminate need for a wrapper >> and perhaps made usage of prefix argument more uniform and predicatble. But that >> is just an idea, I haven't experimented. > > Do you mean such a macro will eliminate the need of altering existing commands? > This would be nice. Something like this: > > (keymap-global-set "M-i d" (with-window-wrapper 'Info-directory)) > >>>>> But there are no such assumption for most commands that work >>>>> only in the selected window. >>>> >>>> I am not sure what you are trying to say here. If we have several live buffers >>>> and wish to act on one, then the user has to choose one somehow, no? We can >>>> either try to automate stuff as I have tried in this patch, by prompting the >>>> user in ambigous case when system can't figure it on its own, or we can have a >>>> dumb system that forces user to *always* select a window prior to acting on a >>>> buffer. If you take a look at the original mail you will understand which commands >>>> has got the optional window argument and why, but if you don't prompt the user, >>>> than you don't need that argument at all. >>> >>> Only commands with global keybindings need a way to select the right buffer. >>> The best example of such command is the entry point to the Info browser >>> the command 'info' bound globally to 'C-h i'. This command allows the user >>> to select the right buffer "*info*<N>" by using the prefix arg. >>> >>> What you are trying to do is to copy this logic to all Info-local commands. >> >> I am not sure how you mean to use this, and why do you think C-h i is enough? >> >> I don't know what your assumption and expectation is. From very beginning I said >> I wish to minimize switching between windows. So I do wish more or less all info >> and help mode commands to be callable from other windows. >> >> Also I definitely want to have Info-mode-map on a prefix key, so which-key can >> show me the popup, since I have a memory like a gold fish and forgott which key >> is bound to what. That was one of reasons why I went away from pre/post hook >> thing. To be honest I am not sure I understand how you mean to use the thing. > > There is no problem with a prefix key since you can create a new keymap > with other-window commands like For me it is because I would prefer having less code if I don't have to. I assumed you will do something like that; and my personal opinion is that I would definitely prefer not having foo, and foo-other-window, along with foo-mode-map and foo-mode-other-window-map, because I see it as a necessary duplication in an environment already full with numerous commands and symbols. > (keymap-global-set "M-i" Info-other-window-map) > (keymap-set Info-other-window-map "d" 'Info-directory-other-window) > ... >>> And here comes the disagreement about the ways of doing this. >> >> I am not sure what I can say here. I understand it is a lot, 2k sloc patch is >> big; and I do understand there are potentiallys (or sure) bugs, but the work is >> done, I am using it myself, and it seem to be fine. If more people tested it, I >> don't think it would be too long before bugs are fixed. In my opinion, if we do >> things, lets do them properly from the beginning :). Piling hack on hack will >> just lead to more hacks. I don't that is how I see it, but I also don't think it >> is very complicated patch conceptually. >> >> I have attached a patch I reworked today so I can use it with the help-mode and >> with removed plist hack. If you look at the help-mode functions, you will see >> the change to them is quite trivial. I do disslike the help-mode/info-mode though. >> >>> I know that you already said about this, so hereby I confirm that I already >>> have seen your opinion:t >>> >>>> Making a wrapper just to put a call to a command into with-selected-window, >>>> instead of wrapping the body of that command with the same macro, is not so much >>>> better; just different. Sure "less intrusive" on a command itself, but there is >>>> a new command and then we have two where one can do the jobb. >>> >>> Replying to your opinion, I expressed a preference to avoid massive changes >>> in the existing functions. >> >> I totally understand your sentiment, and I myself would be all for a "magic >> pedal" that just switches the right thing on, but there is a limit to hackiness >> too. >> >>>> There is also a problem of prompting and input focus. As I wrote in response to >>>> Eli, each command is its own little program, and can prompt user for whatever >>>> reason whenever. Thus each command should be written with the assumption that >>>> input should be presented to user on the frame where user types and with >>>> assumption that user is not executing it from the buffer/window on which it >>>> should act. You can achieve all this with tools already in Emacs, no need to >>>> introduce any new concepts or macros, and it will also work regardless of the >>>> window manager (I think). >>> >>> I have no idea how this would work for window managers that don't switch focus >>> to the frame with the active minibuffer. >> >> If you pre-select window (and frame) before you call an Info-mode command, than >> the focus will be on that frame. You don't need to do anything special, since >> with-selected-frame should select frame, window and the visible buffer, so >> original command will execute in the "right" context. Problem as stated was, >> that when mouse cursor is on the "old frame", the old frame gets focus, which >> messes things up. Try to set mouse-autoselect-window in your Emacs, and foucs >> follow mouse in window manager, if you use some floating wm. I guess tiling WMs >> don't have this problem, but I don't know really, I haven't used anything other >> then dwm, and it was long time ago. > > 'mouse-autoselect-window' has no effect with my WM. So focus switches to > another frame only with 'C-x 5 o'. But this is not surprising since > 'other-frame' at the end explicitly calls 'select-frame-set-input-focus'. > There is no such call in 'with-selected-frame'. I don't think you should set-input-focus for user. The point of the patch is that I don't want to switch from the buffer I am working in. If I wanted to switch focus and change buffer, I would just code a very simple "jump" function as I did initially with jump-info and jump-window. >>>> It is just that the old commands are not written with those assumptions, so I >>>> rewrote Info commands in this patch. I am not sure you can achive that >>>> with wrapping, but perhaps there is a way, you can try. >>> >>> Do you see some needs that can't be achieved with wrapping commands? >> >> Depends on your ambitions :). Focus and input are potential problems. How do you >> plan to use wrappers? If you create double commands, then you will have a bunch of >> "-other-window" commands, would you have a separate mode maps for them, or how >> do you plan to present them to a user? Which one do you leave out and why? I >> personally don't think it is pretty to switch focus to other frame, either in >> vertical, left/right setup, or in horizontal tob/bottom setup. I prefer >> minibufer popping up on the frame where I already type. > > Interesting. So the interactive form doesn't need to switch to another > frame with 'with-selected-frame' and can use only 'with-current-buffer', > so the minibuffer will use the contents of Info buffer. You said you have read my mail carefully, but you are constantly asking me things that tells me you have neither done that, nor did you bother to look through the code or test the patch. Seems like you have just seen a big patch and decided you will do it differently? That is a bit sad. The minibuffer does use nothing. If it is enough with just switching to the buffer or you need to switch to the window depends on the work in interactive form. Obviously some functions need access to the content of info-buffer. For example Info-menu need acces to the text of an info buffer to create a menu that will be presented to the user. Obviously in that case it is enough to just switch to the buffer, and since you are just using the buffer you don't need to switch to other frame so input is left on the user frame. You can use with-selected-window instead, if you take care of focus and input. >>>> In my opinion wrapping is OK if we for some reason can't alter the code, but in >>>> the case of Info and help mode, I don't see such reason, especially since it is >>>> possible to do everything backwards compatible. On the negative side, >>>> wrapping introduces double set of commands, so what are you saving? You are >>>> wrapping code from "outside", while I have done it from "inside", but overall, >>>> the principle is the same. On a plus side for wrappers is that you can actually >>>> write a codegen for wrappers, hopefully in form of a macro and it will work for >>>> other modes. So it is not only negatives, but you could also have both >>>> approaches at the same time too :). >>> >>> Indeed, duplication of the command set is a drawback of wrapping commands. >>> OTOH, wrapping commands could be created mechanically, and they are very small. >> >> I think wrapping the function body in with-selected-window is even smaller, look >> at help-mode in attached patch. > > I don't see how they are smaller. They are exactly like wrapper commands ;) They do not introduce an extra symbol, an extra function call and an extra documentation string, stashed in an extra keymap, as wrappers do. The patch is massive because diff has shifted a lot of code; perhaps if I stashed my functions on the end of info.el, the change would be less massive? :) I don't know, I understand your motiv, but I don't really think that is the best way to do things, but seems like I am the only one :). ^ permalink raw reply [flat|nested] 53+ messages in thread
* RE: [External] : Re: Info-mode patch 2023-06-29 12:42 ` Arthur Miller @ 2023-06-29 15:00 ` Drew Adams 2023-06-29 16:24 ` Arthur Miller 2023-06-29 17:44 ` Juri Linkov 1 sibling, 1 reply; 53+ messages in thread From: Drew Adams @ 2023-06-29 15:00 UTC (permalink / raw) To: Arthur Miller, Juri Linkov; +Cc: emacs-devel@gnu.org > For me it is because I would prefer having less code if > I don't have to. I assumed you will do something like > that; and my personal opinion is that I would definitely > prefer not having foo, and foo-other-window, along with > foo-mode-map and foo-mode-other-window-map, because I > see it as a necessary duplication in an environment > already full with numerous commands and symbols. 1. Sincere apologies: I'm not following this thread. Just a comment on this bit I quoted out of context. 2. There are lots of "I" occurrences there. That's fine, if the result is that something gets changed in Emacs only for some "I"s who might want to opt into that change. 3. There's an inherent trade-off in benefits, which means that different users and different code uses (libraries) can prefer different positions on the trade-off spectrum. I'm referring to this: If there is some semi-automatic way of predefining keys for other-window, other-frame, etc. behavior for "base" commands, then that might mean some simplification of some implementation code. And it could perhaps result in fewer commands. But fewer commands and predefining some systematic scheme for key-binding the window/frame variants is also _constraining_. Users also need to be able to easily bind different individual commands to whatever keys they like, in whatever keymaps they like. And users can also prefer to have separate commands, discoverable by name matches etc. I might want to find an `-other-window' or `-other-frame' version of a command - both to read its doc and to invoke it - specifically. Tying things up in a tight knot might be useful for implementers or for some users. But it could tie the hands of other users and implementers of other code. Having multiple `foo', `foo-other-window', and/or `foo-other-frame' really bothers some people. Me, I prefer that - greatly. I don't want to have to carve out some such behavior from an iron block, or, in effect, have to define my own variants. This applies to my use of commands interactively and in code. Again, I'm ignorant of what's being proposed. So maybe this is just a knee-jerk, alarmist response. But if it rings a bell, then please make any such changes 100% optional for users - and opt-in, not opt-out. Emacs is very different things for different users and use cases. Sweeping changes can break things for others, without any bad intentions. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [External] : Re: Info-mode patch 2023-06-29 15:00 ` [External] : " Drew Adams @ 2023-06-29 16:24 ` Arthur Miller 0 siblings, 0 replies; 53+ messages in thread From: Arthur Miller @ 2023-06-29 16:24 UTC (permalink / raw) To: Drew Adams; +Cc: Juri Linkov, emacs-devel@gnu.org Drew Adams <drew.adams@oracle.com> writes: >> For me it is because I would prefer having less code if >> I don't have to. I assumed you will do something like >> that; and my personal opinion is that I would definitely >> prefer not having foo, and foo-other-window, along with >> foo-mode-map and foo-mode-other-window-map, because I >> see it as a necessary duplication in an environment >> already full with numerous commands and symbols. > > 1. Sincere apologies: I'm not following this thread. > Just a comment on this bit I quoted out of context. > > 2. There are lots of "I" occurrences there. That's > fine, if the result is that something gets changed > in Emacs only for some "I"s who might want to opt > into that change. > > 3. There's an inherent trade-off in benefits, which > means that different users and different code uses > (libraries) can prefer different positions on the > trade-off spectrum. > > I'm referring to this: > > If there is some semi-automatic way of predefining > keys for other-window, other-frame, etc. behavior > for "base" commands, then that might mean some > simplification of some implementation code. And it > could perhaps result in fewer commands. > > But fewer commands and predefining some systematic > scheme for key-binding the window/frame variants is > also _constraining_. > > Users also need to be able to easily bind different > individual commands to whatever keys they like, in > whatever keymaps they like. > > And users can also prefer to have separate commands, > discoverable by name matches etc. I might want to > find an `-other-window' or `-other-frame' version > of a command - both to read its doc and to invoke > it - specifically. > > Tying things up in a tight knot might be useful for > implementers or for some users. But it could tie > the hands of other users and implementers of other > code. > > Having multiple `foo', `foo-other-window', and/or > `foo-other-frame' really bothers some people. Me, > I prefer that - greatly. I don't want to have to > carve out some such behavior from an iron block, > or, in effect, have to define my own variants. > This applies to my use of commands interactively > and in code. > > Again, I'm ignorant of what's being proposed. So > maybe this is just a knee-jerk, alarmist response. > But if it rings a bell, then please make any such > changes 100% optional for users - and opt-in, not > opt-out. I can just suggest you to try the patch. You should be able to use it as-is, as you always did, at least it is my intention, otherwise it is a bug; even with your multiple help buffers. But you can also bind all commands to any keymap you wish, and there is no need to "-other-window" commands at all; with other words, you don't need to neither discover nor search anything. > Emacs is very different things for different users > and use cases. Sweeping changes can break things > for others, without any bad intentions. Of course Drew, we are all aware of the fact. I tried for that explicit reason to be completely backwards compatible. If I have failed in some case, than it is a bug. I am al for automation myself, dispute is that I would pefer future commands in Emacs to be written with some assumptions, so we don't need double set of commands. For that I have already mentioned that we could automate that with something like define-minor-mode, and I do have a small framework idea in mind, but I haven't had time to code it yet. Juris approach is good for the old code so it don't have to be reworked, and if you want some occasional function to run on other window. I think it would be good to have it in Emacs as an option for people to use occaionally with older code. But I don't think it is very good as the only alternative, for all the future use, because I am sure we can avoid the duplication of commands, i.e. "-other-window" commands, and maps as the wrapper approach does. We only need to take care how we write a command, and they would work from everywhere, there is nothing to opt in/out. I have also suggested that we abstract boring details in some macro, something like define-command, that take care of prompting user, choosing a window, dealing with universal prefix etc. As Eli said, the point was to just make Info and help mode commands callable from other widows, which this patch does, so I haven't coded anything general, but I am quite confident it would be relatively simple to do. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-29 12:42 ` Arthur Miller 2023-06-29 15:00 ` [External] : " Drew Adams @ 2023-06-29 17:44 ` Juri Linkov 2023-06-29 22:28 ` Arthur Miller 1 sibling, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-06-29 17:44 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >>>>>>>> But it seems this is not enough because with-selected-frame >>>>>>>> still fails to switch focus to another frame. You need also >>>>>>>> to use select-frame-set-input-focus. >>>>>>> Where it fails? For me it prompts me on correct frame. I didn't want to switch >>>>>>> focus on the info frame. I am aware of select-frame-set-input-focus, have used >>>>>>> it in some test actually. >>>>>> >>>>>> Probably the behaviour depends on the window manager. >>>>> >>>>> Yes, I am quite sure it isn't "probably" but "surely" :), as I wrote earlier by >>>>> the way. >>>>> >>>>>> With my window manager with-selected-frame displays >>>>>> the prompt in another frame, but input is inserted >>>>>> into the original buffer. Maybe we should have >>>>>> a new option whether to use select-frame-set-input-focus? >>>>> >>>>> I am not sure I understand what you mean with input being inserted in the >>>>> original buffer. >>>> >>>> For example, the original buffer is *scratch*. The minibuffer >>>> pops up in another frame. I start typing. But letters get >>>> inserted to *scratch*, not to the minibuffer. This is because >>>> focus is not switched to another frame. >>> >>> Aha, ok, I understand now. I haven't checked that one, tbh. When I realized the >>> minibufer pops on other frame, I realized I didn't liked it, so I reworked >>> everything so focus stays on the frame where I type. As I remember now, I >>> also have set Emacs option to auto select window based on the cursor, >>> mouse-autoselect-window, so it might be that one that interfers too. I totally >>> forgott that one earlier. >> >> I tried to set mouse-autoselect-window, but still input focus >> is not switched to another frame with e.g. >> >> (with-selected-frame (next-frame) (read-string "Prompt: ")) > > Yes with-selected-frame does switch focus to a freame; I have also used it in > the patch, for the same purpose to switch focus back to user frame. Well, it works for you. I don't know why it's not supported for any WM. >>>>>>> Have you tested *everything*? Interactively and from lisp? >>>>>> >>>>>> I see no problems with this both interactively and from lisp: >>>>> >>>>> If you don't care to ask the user which window to choose when ambigous, then you >>>>> don't have to care about this at all. If you don't want to take care of >>>>> multiple windows with possibly ambigous names, user misstyping a name and >>>>> possibly irregular name, then you don't need to prompt the user at all, just >>>>> take the first info buffer you find or force user to *always* select manually a >>>>> window and you are all good. But in my opinion it is not hard to have it slightly >>>>> more polished and automated as I did. >>>> >>>> I don't know what do you mean by the words "don't care" >>>> since this implementation still uses your function >>>> 'info-window' that asks the user which window to choose. >>>> Please look carefully at Info-index-other-window: >>>> >>>>>> #+begin_src emacs-lisp >>>>>> (defmacro with-selected-window-frame (window &rest body) >>>>>> `(let ((old-frame (selected-frame)) >>>>>> (frame (window-frame ,window))) >>>>>> (unless (eq frame old-frame) >>>>>> (select-frame frame 'norecord) >>>>>> (select-frame-set-input-focus frame 'norecord)) >>>>>> (prog1 (with-selected-window ,window >>>>>> ,@body) >>>>>> (select-frame old-frame 'norecord) >>>>>> (select-frame-set-input-focus old-frame 'norecord)))) >>>>>> >>>>>> (defun Info-index-other-window (topic &optional window) >>>>>> (interactive >>>>>> (with-selected-window-frame (info-window) >>>>>> (append (eval (cadr (interactive-form 'Info-index))) >>>>>> (list (selected-window))))) >>>>>> (with-selected-window (or window (info-window)) >>>>>> (Info-index topic))) >>>>>> #+end_src >>> >>> I thought it was just left over while testing. Actually you want to keep the >>> part I disslike most, and want to redo the part(s) I think are good :). When I >>> wrote about the patch, I had three sections of text: the good, the bad and the >>> ugly. Part about info-window and find-window-for-help was "the ugly" part. The >>> bad one was about alterered signatures and the good one was about everythign >>> been possible to implement user-friendly. >> >> Please explain what is ugly in info-window. I can see that in >> find-window-for-help the name is ugly. Indeed, there is nothing >> specific to help in find-window-for-help. > > Separating funcitonality in two places. Part of required functionality is in > that one, and part is in info/help-window. In my opinion the implementation is > messy. > >>>>>> You can't avoid adding the window argument. Otherwise, you need >>>>>> to invent such hacks as sending the window selected by the user >>>>>> to the command body via a symbol property. >>>>>> >>>>>> But in the wrapper command like above there is no problem >>>>>> of adding the window argument to the new command. >>>>> >>>>> I wasn't familiar with interactive form enough and didn't know how to connect >>>>> the optional argument from within interactive form. Symbol-plist was just >>>>> workaround for lack of a better knowledge :). I stated that explicitly in the >>>>> mail with the patch, but you perhaps didn't have time to read the mail? >>>> >>>> I read all your mails carefully, and noted that there is no better alternative >>>> than adding the window argument. >>> >>> Ok, I am sorry if I missunderstand you, I thought you mean I want add the window >>> argument to *all commands*, which I don't want. Only those that are called >>> possibly more then once, either because they are called from other command, or >>> recursively. The argument is there just to avoid the prompting more then >>> once. Now if you want to wrap everything, then I think you can avoid the >>> argument completely *iff* you can switch to the info window before you call the >>> wrapped command. >> >> I agree that the window argument is not needed when you can wrap with e.g. >> >> (with-selected-window (info-window) >> (Info-index topic))) > > No, not really, window argument is not about wrapping but about prompting, look > through the code of the patch and you will understand. > > It is not needed when there is no risk of prompting user more than once. Again, > ;), it is needed only in commands that call themselves recursively or in > commands that are called from other commands, explicitly or via some other > call. That is actually relatively *small* number of commands. Info-mode does that > in several places. Help-mode does not do that at all. If you are going to switch > to info window before you call original command you shouldn't need it at all. If > you don't prompt user, then you wouldn't need it at all, you would just possibly > select the same window multiple times, but without visible effect to the user, > it does not matter. Please note that risk of prompting user more than once doesn't exist in case of using wrapper commands. Only the wrapper command prompts the user. Then all other intermediate functions called recursively or via function call chain don't need to prompt the user again because they all work in the right window. >>>>> Anyway, I understand now how is it done: >>>>> >>>>> (append (eval (cadr (interactive-form 'Info-index))) >>>>> (list (selected-window))) >>>>> >>>>> I have to return a list of all arguments from interactive form; it was that >>>>> simple. Thanks for the example. >>>> >>>> Indeed, this is only an example. More handling may be needed for >>>> the return value of interactive-form. >>> >>> I never wrote complex interactive forms before, so I was a unfamiliar with the >>> detials and a bit lazy to look around andd see how it is done, so I did a >>> hack, but from your example I understand now how it is used. It was a couple of >>> minutes to rework the patch without symbol-plist hack :). Thanks for showing it. >>> >>>>>> Maybe it's possible even to write a macro that will generate >>>>>> such wrapper commands automatically from existing commands. >>>>>> >>>>>> It seems you assume that all commands should take a window. >>>>> >>>>> Why would I assume that? I wrote explicitly which commands were extended with >>>>> an optional window argument and why. I don't feel for repeating entire text here, >>>>> if you have interest, please take a look at the original email, you don't seem >>>>> to have read it all. That mail answered your opening question, but I didn't want >>>>> to point it out earlier to not sound impolite, instead I tried to clarify the >>>>> problems and choices further. >>>> >>>> I was referring to your words from another mail that I read carefully: >>>> >>>>> What I am trying to say is that command has to be written with the assumptions >>>>> that user can call it from any window and Emacs frame, and that all prompting >>>>> should be done in the frame at which user types, so I have reworked info commands >>>>> to work with those assumptions. >>>> >>>> I don't agree that all commands should be written with the assumption >>>> that they can be called from any window. Only commands with global >>>> keybindings usually prepare the buffer with the predefined name and mode, >>>> then mode-local keybindings and mode-local commands assume that they are >>>> operating in the right buffer. >>> >>> With "all commands" I ment all commands that wish to be callable from other >>> buffers then those they act on. I didn't mean literally each and every in >>> existence, since as I mentioned, command in Emacs can be anything, I have one >>> that shutts down the computer. >>> >>> I am not sure if I would draw such absolut divider on what should and what would >>> should not be written as any-window-command or how to call them. As I wrote I >>> think it is really up to a command, but yes I do agree that not *all* commands >>> should be callable from everywhere. I don't think I have enough understanding to >>> yet draw some hard divider what should be and what should not be callable from >>> any buffer. What I have got from this experiment is the understanding what is >>> needed to write such command and what are possible problems. At least I think >>> so, but there is probably more to it, that I haven't yet encountered. >>> >>> What I also think is that you could encapsulate that into a macro, similar to >>> define-minor-mode, which encapsulate boring details and window switching, so >>> people don't have to thinkg about it. That would eliminate need for a wrapper >>> and perhaps made usage of prefix argument more uniform and predicatble. But that >>> is just an idea, I haven't experimented. >> >> Do you mean such a macro will eliminate the need of altering existing commands? >> This would be nice. Something like this: >> >> (keymap-global-set "M-i d" (with-window-wrapper 'Info-directory)) >> >>>>>> But there are no such assumption for most commands that work >>>>>> only in the selected window. >>>>> >>>>> I am not sure what you are trying to say here. If we have several live buffers >>>>> and wish to act on one, then the user has to choose one somehow, no? We can >>>>> either try to automate stuff as I have tried in this patch, by prompting the >>>>> user in ambigous case when system can't figure it on its own, or we can have a >>>>> dumb system that forces user to *always* select a window prior to acting on a >>>>> buffer. If you take a look at the original mail you will understand which commands >>>>> has got the optional window argument and why, but if you don't prompt the user, >>>>> than you don't need that argument at all. >>>> >>>> Only commands with global keybindings need a way to select the right buffer. >>>> The best example of such command is the entry point to the Info browser >>>> the command 'info' bound globally to 'C-h i'. This command allows the user >>>> to select the right buffer "*info*<N>" by using the prefix arg. >>>> >>>> What you are trying to do is to copy this logic to all Info-local commands. >>> >>> I am not sure how you mean to use this, and why do you think C-h i is enough? >>> >>> I don't know what your assumption and expectation is. From very beginning I said >>> I wish to minimize switching between windows. So I do wish more or less all info >>> and help mode commands to be callable from other windows. >>> >>> Also I definitely want to have Info-mode-map on a prefix key, so which-key can >>> show me the popup, since I have a memory like a gold fish and forgott which key >>> is bound to what. That was one of reasons why I went away from pre/post hook >>> thing. To be honest I am not sure I understand how you mean to use the thing. >> >> There is no problem with a prefix key since you can create a new keymap >> with other-window commands like > > For me it is because I would prefer having less code if I don't have to. I > assumed you will do something like that; and my personal opinion is that I would > definitely prefer not having foo, and foo-other-window, along with foo-mode-map > and foo-mode-other-window-map, because I see it as a necessary duplication in an > environment already full with numerous commands and symbols. It's possible to bind wrappers as lambdas without creating command names, but this has own disadvantages. >> (keymap-global-set "M-i" Info-other-window-map) >> (keymap-set Info-other-window-map "d" 'Info-directory-other-window) >> ... >>>> And here comes the disagreement about the ways of doing this. >>> >>> I am not sure what I can say here. I understand it is a lot, 2k sloc patch is >>> big; and I do understand there are potentiallys (or sure) bugs, but the work is >>> done, I am using it myself, and it seem to be fine. If more people tested it, I >>> don't think it would be too long before bugs are fixed. In my opinion, if we do >>> things, lets do them properly from the beginning :). Piling hack on hack will >>> just lead to more hacks. I don't that is how I see it, but I also don't think it >>> is very complicated patch conceptually. >>> >>> I have attached a patch I reworked today so I can use it with the help-mode and >>> with removed plist hack. If you look at the help-mode functions, you will see >>> the change to them is quite trivial. I do disslike the help-mode/info-mode though. >>> >>>> I know that you already said about this, so hereby I confirm that I already >>>> have seen your opinion:t >>>> >>>>> Making a wrapper just to put a call to a command into with-selected-window, >>>>> instead of wrapping the body of that command with the same macro, is not so much >>>>> better; just different. Sure "less intrusive" on a command itself, but there is >>>>> a new command and then we have two where one can do the jobb. >>>> >>>> Replying to your opinion, I expressed a preference to avoid massive changes >>>> in the existing functions. >>> >>> I totally understand your sentiment, and I myself would be all for a "magic >>> pedal" that just switches the right thing on, but there is a limit to hackiness >>> too. >>> >>>>> There is also a problem of prompting and input focus. As I wrote in response to >>>>> Eli, each command is its own little program, and can prompt user for whatever >>>>> reason whenever. Thus each command should be written with the assumption that >>>>> input should be presented to user on the frame where user types and with >>>>> assumption that user is not executing it from the buffer/window on which it >>>>> should act. You can achieve all this with tools already in Emacs, no need to >>>>> introduce any new concepts or macros, and it will also work regardless of the >>>>> window manager (I think). >>>> >>>> I have no idea how this would work for window managers that don't switch focus >>>> to the frame with the active minibuffer. >>> >>> If you pre-select window (and frame) before you call an Info-mode command, than >>> the focus will be on that frame. You don't need to do anything special, since >>> with-selected-frame should select frame, window and the visible buffer, so >>> original command will execute in the "right" context. Problem as stated was, >>> that when mouse cursor is on the "old frame", the old frame gets focus, which >>> messes things up. Try to set mouse-autoselect-window in your Emacs, and foucs >>> follow mouse in window manager, if you use some floating wm. I guess tiling WMs >>> don't have this problem, but I don't know really, I haven't used anything other >>> then dwm, and it was long time ago. >> >> 'mouse-autoselect-window' has no effect with my WM. So focus switches to >> another frame only with 'C-x 5 o'. But this is not surprising since >> 'other-frame' at the end explicitly calls 'select-frame-set-input-focus'. >> There is no such call in 'with-selected-frame'. > > I don't think you should set-input-focus for user. The point of the patch is > that I don't want to switch from the buffer I am working in. If I wanted to > switch focus and change buffer, I would just code a very simple "jump" function > as I did initially with jump-info and jump-window. Yes, I already noticed that you use with-current-buffer instead of with-selected-frame and then read arguments in the selected frame. >>>>> It is just that the old commands are not written with those assumptions, so I >>>>> rewrote Info commands in this patch. I am not sure you can achive that >>>>> with wrapping, but perhaps there is a way, you can try. >>>> >>>> Do you see some needs that can't be achieved with wrapping commands? >>> >>> Depends on your ambitions :). Focus and input are potential problems. How do you >>> plan to use wrappers? If you create double commands, then you will have a bunch of >>> "-other-window" commands, would you have a separate mode maps for them, or how >>> do you plan to present them to a user? Which one do you leave out and why? I >>> personally don't think it is pretty to switch focus to other frame, either in >>> vertical, left/right setup, or in horizontal tob/bottom setup. I prefer >>> minibufer popping up on the frame where I already type. >> >> Interesting. So the interactive form doesn't need to switch to another >> frame with 'with-selected-frame' and can use only 'with-current-buffer', >> so the minibuffer will use the contents of Info buffer. > > You said you have read my mail carefully, but you are constantly asking me > things that tells me you have neither done that, nor did you bother to look > through the code or test the patch. Seems like you have just seen a big patch > and decided you will do it differently? That is a bit sad. I have read all your mails and whole patch carefully (but can't test it because your patch does not apply to master/emacs-29). Now it's difficult to read this mail because quotes are no more highlighted in Gnus. I don't know if this is because you mail is too long, or there is some bug in Gnus, but this is a minor problem. > The minibuffer does use nothing. If it is enough with just switching to the > buffer or you need to switch to the window depends on the work in interactive form. > > Obviously some functions need access to the content of info-buffer. For example > Info-menu need acces to the text of an info buffer to create a menu that will be > presented to the user. Obviously in that case it is enough to just switch to the > buffer, and since you are just using the buffer you don't need to switch to > other frame so input is left on the user frame. You can use with-selected-window > instead, if you take care of focus and input. Yes, this would be better instead of with-selected-frame. >>>>> In my opinion wrapping is OK if we for some reason can't alter the code, but in >>>>> the case of Info and help mode, I don't see such reason, especially since it is >>>>> possible to do everything backwards compatible. On the negative side, >>>>> wrapping introduces double set of commands, so what are you saving? You are >>>>> wrapping code from "outside", while I have done it from "inside", but overall, >>>>> the principle is the same. On a plus side for wrappers is that you can actually >>>>> write a codegen for wrappers, hopefully in form of a macro and it will work for >>>>> other modes. So it is not only negatives, but you could also have both >>>>> approaches at the same time too :). >>>> >>>> Indeed, duplication of the command set is a drawback of wrapping commands. >>>> OTOH, wrapping commands could be created mechanically, and they are very small. >>> >>> I think wrapping the function body in with-selected-window is even smaller, look >>> at help-mode in attached patch. >> >> I don't see how they are smaller. They are exactly like wrapper commands ;) > > They do not introduce an extra symbol, an extra function call and an extra > documentation string, stashed in an extra keymap, as wrappers do. I see an extra map as an advantage because users will be able to put both help-other-window and info-other-window commands on the same prefix map. Then e.g. 'M-i l' will go back either in the nearest Help or Info window. > The patch is massive because diff has shifted a lot of code; perhaps if I > stashed my functions on the end of info.el, the change would be less massive? :) Yes, probably a short section of autoloaded wrapper commands at the end of info.el would look nicer. > I don't know, I understand your motiv, but I don't really think that is the best > way to do things, but seems like I am the only one :). This is just my opinion. If people would think your patch is a good way to go, that would be fine with me. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-29 17:44 ` Juri Linkov @ 2023-06-29 22:28 ` Arthur Miller 2023-06-30 7:13 ` Juri Linkov 2023-07-01 9:59 ` Getting Gnus to highlight citations in long mails (was: Info-mode patch) Kévin Le Gouguec 0 siblings, 2 replies; 53+ messages in thread From: Arthur Miller @ 2023-06-29 22:28 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 7427 bytes --] Juri Linkov <juri@linkov.net> writes: > I have read all your mails and whole patch carefully (but can't test it > because your patch does not apply to master/emacs-29). Now it's difficult > to read this mail because quotes are no more highlighted in Gnus. > I don't know if this is because you mail is too long, or there is > some bug in Gnus, but this is a minor problem. I also use Gnus, and it works fine for me, but the mail did got very long. I have removed old parts in the response, and just left those of your last question/comments on which I have answered. I have put this one on top, it was somewhere in the middle; the rest is as they come in your last response. >> Yes with-selected-frame does switch focus to a freame; I have also used it in >> the patch, for the same purpose to switch focus back to user frame. > > Well, it works for you. I don't know why it's not supported for any WM. I believe there is a missunderstanding here. I didn't meant that with-selected-frame is not supported; it is, I used it myself. I mean if you switch to an info window in your wrapper, then the focus will be on that frame by the time original command executes. Now if I have focus follow mouse, or mouse autoselct, or something, then if the mouse cursor is in the old frame, then the minibuffer will popup in info frame, but system will switch focus on old frame where mouse cursor is, so I can't type in the minibuffer in info frame. I don't know if that clears confusion :). I have actually wrapped the prompt in interactive form(s) into with-selected-frame in some place to correct for that bahvour, so it switches minbibuffer to the old frame. Hopefully it is not too confusing. I think you will have that problem with the wrapper approach. Is there some way to tell interactive where all propts will be placed, without parsing interactive form and checking strings for interactive codes or something similar awkward? > Please note that risk of prompting user more than once doesn't exist in case > of using wrapper commands. Only the wrapper command prompts the user. > Then all other intermediate functions called recursively or via > function call chain don't need to prompt the user again because > they all work in the right window. Yes, that is expected since you are calling old command from a new function. A new function is a clean slate, so yepp, I am aware of that and I was aware that you want to pre-select window before calling the original command. So yes, that is a positive thing with wrapper approach, you get that one for free. I just clarified what commands needed the optional window and why, since you seemed to think that I meant all commands need it. > I have read all your mails and whole patch carefully (but can't test it > because your patch does not apply to master/emacs-29). Hmm ? When I sent the first patch, it built without any warnings or errors in current master. Then when I built earlier today after fixing that plist hack, window.el got stuck in loadup.el when building dump file, because of cl-lib. So I reworked it without cl-lib. That is the one I sent last, with the help commands too, it should build without errors, I am typing from Emacs with that patch. I have attached patched files if you want to just eval them and test. I am not sure if I pulled from the git today. >> The minibuffer does use nothing. If it is enough with just switching to the >> buffer or you need to switch to the window depends on the work in interactive form. >> >> Obviously some functions need access to the content of info-buffer. For example >> Info-menu need acces to the text of an info buffer to create a menu that will be >> presented to the user. Obviously in that case it is enough to just switch to the >> buffer, and since you are just using the buffer you don't need to switch to >> other frame so input is left on the user frame. You can use with-selected-window >> instead, if you take care of focus and input. > > Yes, this would be better instead of with-selected-frame. Because with-selected-window will also make visible buffer current and select frame as well. Notice also that you can have more than one window visible, so just selecting the frame might not work correctly, while with-selected-window probably will. > I see an extra map as an advantage because users will be able > to put both help-other-window and info-other-window commands > on the same prefix map. Then e.g. 'M-i l' will go back either > in the nearest Help or Info window. What does that mean? You are building some general "go-back-in-help-or-info-mode" command that will work specifically for info and help and switch based on window buffer, or what is your plan there? How will you deal with 's' which does two completely different things, search and view source? Are you building some key dispatcher into your wrapper? Under which name will users file the command in that collective map? I am not sure I understand you there. How am I supposed to go back in info buffer, if help window is nearest, or how am I supposed to go back in some other info buffer than the nearest one? What does "nearest" even mean? >> The patch is massive because diff has shifted a lot of code; perhaps if I >> stashed my functions on the end of info.el, the change would be less massive? :) > > Yes, probably a short section of autoloaded wrapper commands at the end of info.el > would look nicer. I thought it was about good work and improving things, not about looks and magic pedals. >> I don't know, I understand your motiv, but I don't really think that is the best >> way to do things, but seems like I am the only one :). > > This is just my opinion. In my opinion, what you suggest is a good for old code and in case of few commands, as a first aid. But you seem to want to use wrappers as some general tool in all cases, which means lots of unnecessary duplication if that will be the only tool offered. I think if we just take care how to write commands in the future, no magic pedals would be needed, so at least put a few words in the manual on things to think about when writing commands, and possibly create a macro that abstracts those parts that make a command callable from other window. That would benefit Emacs in longer run much better in my opinion. With other words, to wrapper approach you suggest certainly has its place, but I wouldn't use it as a general tool for the future commands, and I wouldn't throw away written and tested work, just to replace it with the generated code. > If people would think your patch is a good way > to go, that would be fine with me. My patch was not some general suggestion; I have just rewrote info and help modes specifically, but I certainly don't think rewriting each and every command is "a way to go". Interested that Eli said I was looking for some generic solution. I do suggest automation in form of a codegen, a little macro helper, as said in pevious discussion, some sort of define-command, similar to define-minor-mode, but I didn't want to do it as part of this patch. I am ok to help, and the patch was a suggestion; I have done it for myself anyway. I would probably just skipped the frames part, since I don't use multiple frames. Anyway, attached is patched version of files if anyone is interested to try it, if the prevously sent patch does not apply. If you find a bug let me know. [-- Attachment #2: window.el --] [-- Type: application/emacs-lisp, Size: 468020 bytes --] [-- Attachment #3: help-mode.el --] [-- Type: application/emacs-lisp, Size: 44814 bytes --] [-- Attachment #4: info.el --] [-- Type: application/emacs-lisp, Size: 237477 bytes --] ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-29 22:28 ` Arthur Miller @ 2023-06-30 7:13 ` Juri Linkov 2023-06-30 8:41 ` Arthur Miller 2023-07-01 9:59 ` Getting Gnus to highlight citations in long mails (was: Info-mode patch) Kévin Le Gouguec 1 sibling, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-06-30 7:13 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >> Please note that risk of prompting user more than once doesn't exist in case >> of using wrapper commands. Only the wrapper command prompts the user. >> Then all other intermediate functions called recursively or via >> function call chain don't need to prompt the user again because >> they all work in the right window. > > Yes, that is expected since you are calling old command from a new function. A > new function is a clean slate, so yepp, I am aware of that and I was aware that > you want to pre-select window before calling the original command. So yes, that > is a positive thing with wrapper approach, you get that one for free. I just > clarified what commands needed the optional window and why, since you seemed to > think that I meant all commands need it. Another advantage of wrapper commands is that you can implement what Manuel asked you to do to make key sequences for another window repeatable. This is possible only when you have separate wrapper commands like Info-directory-other-window and a separate keymap with them like Info-other-window-map. >> I see an extra map as an advantage because users will be able >> to put both help-other-window and info-other-window commands >> on the same prefix map. Then e.g. 'M-i l' will go back either >> in the nearest Help or Info window. > > What does that mean? You are building some general > "go-back-in-help-or-info-mode" command that will work specifically for info and > help and switch based on window buffer, or what is your plan there? Yes, you can build combined commands, so users need to bind only 1 key. > How will you deal with 's' which does two completely different things, > search and view source? Are you building some key dispatcher into your wrapper? > Under which name will users file the command in that collective map? You can implement 's' with something like this (defun Info-search-or-help-view-source-other-window () (with-selected-window (help-or-info-window) (if (derived-mode-p 'help-mode) (help-view-source) (Info-search))) But such convenience is optional, this is just an example to demonstrate flexibility of the wrapper commands approach. > I am not sure I understand you there. How am I supposed to go back in info > buffer, if help window is nearest, or how am I supposed to go back in some other > info buffer than the nearest one? What does "nearest" even mean? The nearness can be defined by the combined help-or-info-window that handles both modes. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-30 7:13 ` Juri Linkov @ 2023-06-30 8:41 ` Arthur Miller 2023-06-30 17:57 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-06-30 8:41 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>> Please note that risk of prompting user more than once doesn't exist in case >>> of using wrapper commands. Only the wrapper command prompts the user. >>> Then all other intermediate functions called recursively or via >>> function call chain don't need to prompt the user again because >>> they all work in the right window. >> >> Yes, that is expected since you are calling old command from a new function. A >> new function is a clean slate, so yepp, I am aware of that and I was aware that >> you want to pre-select window before calling the original command. So yes, that >> is a positive thing with wrapper approach, you get that one for free. I just >> clarified what commands needed the optional window and why, since you seemed to >> think that I meant all commands need it. > > Another advantage of wrapper commands is that you can implement what > Manuel asked you to do to make key sequences for another window repeatable. > This is possible only when you have separate wrapper commands like > Info-directory-other-window and a separate keymap with them like > Info-other-window-map. What exactly would not make my patch work with repeating commands? >>> I see an extra map as an advantage because users will be able >>> to put both help-other-window and info-other-window commands >>> on the same prefix map. Then e.g. 'M-i l' will go back either >>> in the nearest Help or Info window. >> >> What does that mean? You are building some general >> "go-back-in-help-or-info-mode" command that will work specifically for info and >> help and switch based on window buffer, or what is your plan there? > > Yes, you can build combined commands, so users need to bind only 1 key. I can also build entire Emacs if I want, that wasn't the question :). Of course I can build new commands, if I wanted that I would release a 3rd patry package and wouldn't want to alter the core info and help-mode files that get into dumped image. I didn't want to have bunch of spaghetti stuff you are suggesting. >> How will you deal with 's' which does two completely different things, >> search and view source? Are you building some key dispatcher into your wrapper? >> Under which name will users file the command in that collective map? > > You can implement 's' with something like this > > (defun Info-search-or-help-view-source-other-window () > (with-selected-window (help-or-info-window) > (if (derived-mode-p 'help-mode) > (help-view-source) > (Info-search))) > > But such convenience is optional, this is just an example to demonstrate > flexibility of the wrapper commands approach. That is not a convenience, this is classical inheritance entaglement you are suggesting here. Any user who would want to bind a thrid mode would have to write their own mode and re-wrap your stuff in his/her own wrapper. Idea of combined method is actually not so bad, it is also a sort of automation, but there is a better way to do it, with or without my patch. >> I am not sure I understand you there. How am I supposed to go back in info >> buffer, if help window is nearest, or how am I supposed to go back in some other >> info buffer than the nearest one? What does "nearest" even mean? > > The nearness can be defined by the combined help-or-info-window > that handles both modes. "Can be defined" is not an answer on "what does it mean". ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-30 8:41 ` Arthur Miller @ 2023-06-30 17:57 ` Juri Linkov 2023-07-01 9:11 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-06-30 17:57 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >> Another advantage of wrapper commands is that you can implement what >> Manuel asked you to do to make key sequences for another window repeatable. >> This is possible only when you have separate wrapper commands like >> Info-directory-other-window and a separate keymap with them like >> Info-other-window-map. > > What exactly would not make my patch work with repeating commands? You can't add repeat symbols on existing Info commands used in Info buffers, only wrapper commands with a prefix key should be repeatable. >>> I am not sure I understand you there. How am I supposed to go back in info >>> buffer, if help window is nearest, or how am I supposed to go back in some other >>> info buffer than the nearest one? What does "nearest" even mean? >> >> The nearness can be defined by the combined help-or-info-window >> that handles both modes. > > "Can be defined" is not an answer on "what does it mean". You decide what does it mean. It can be anything that you want. For example, next-window or the most recently used window, it's up to you ;) ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-30 17:57 ` Juri Linkov @ 2023-07-01 9:11 ` Arthur Miller 2023-07-02 17:53 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-07-01 9:11 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>> Another advantage of wrapper commands is that you can implement what >>> Manuel asked you to do to make key sequences for another window repeatable. >>> This is possible only when you have separate wrapper commands like >>> Info-directory-other-window and a separate keymap with them like >>> Info-other-window-map. >> >> What exactly would not make my patch work with repeating commands? > > You can't add repeat symbols on existing Info commands used in Info buffers, > only wrapper commands with a prefix key should be repeatable. I am not sure what that means, but I have tried repeat mode now with my patch and it works out of the box. Both from an info buffer, and from other buffer. It is kind-a cool, was not familiar with repeat mode before. Can I get *some* help? When rewriting Info-mode-map definition, so I can use :repeat keword to skip typing bunch of "put" statements, I couldn't add tool-bar shortcuts in the map definitions. How do I use those with kbd syntax? (defvar-keymap Info-mode-map ( ... ) ;;"<tool-bar> <C-Back in history>" 'Info-history-back-menu ;;"<tool-bar> <C-Forward in history>" 'Info-history-forward-menu ) (define-key Info-mode-map [tool-bar C-Back\ in\ history] 'Info-history-back-menu) (define-key Info-mode-map [tool-bar C-Forward\ in\ history] 'Info-history-forward-menu) (put 'Info-beginning-of-buffer :advertised-binding "b") I would like to not have to use those two define-key statements after the definition. >>>> I am not sure I understand you there. How am I supposed to go back in info >>>> buffer, if help window is nearest, or how am I supposed to go back in some other >>>> info buffer than the nearest one? What does "nearest" even mean? >>> >>> The nearness can be defined by the combined help-or-info-window >>> that handles both modes. >> >> "Can be defined" is not an answer on "what does it mean". > > You decide what does it mean. It can be anything that you want. For > example, next-window or the most recently used window, it's up to you ;) :). Ok, so we are getting another "pluggable framework"? "Up to you" s still not an answer. What will it do out of the box, what is do YOU mean with "nearest", what will be the default? That is an honest consideration. Isn't it better to first *solve* the problem and get understanding of the issues before engineering yet another framework? From your response throught this conservaton and I understand that you have not done the research and don't have understanding of the problems involved. I think it is a be arrogant and disrespectfull to someone who has put work and testing like I did to just dismiss it without even trying the solution, but anyway I have helped you to understand the problems involved. A part of those prombs help you with those issues was a question you have left unanswered: > Is there some way to tell interactive where all propts will be placed, without > parsing interactive form and checking strings for interactive codes or something > similar awkward? That was an real question, not a trolling, how do you solve that? Is there some piece in Emacs that can tell in which minibuffer all prompts should appear, without wrapping prompts into with-selected-frame or something similar? I would like to know myself. If there would be an option in Emacs to either let-bind minibuffer for prompt which with-selected-frame/window & co would ignore, or some option or whatever. Otherwise, wrapper approach can not deal with this, at least not what I know, but perhaps you know? You can if you do a parser for interactive form but I consider that an overkill. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-01 9:11 ` Arthur Miller @ 2023-07-02 17:53 ` Juri Linkov 2023-07-02 18:39 ` Eli Zaretskii 2023-07-02 22:05 ` Arthur Miller 0 siblings, 2 replies; 53+ messages in thread From: Juri Linkov @ 2023-07-02 17:53 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >>>> Another advantage of wrapper commands is that you can implement what >>>> Manuel asked you to do to make key sequences for another window repeatable. >>>> This is possible only when you have separate wrapper commands like >>>> Info-directory-other-window and a separate keymap with them like >>>> Info-other-window-map. >>> >>> What exactly would not make my patch work with repeating commands? >> >> You can't add repeat symbols on existing Info commands used in Info buffers, >> only wrapper commands with a prefix key should be repeatable. > > I am not sure what that means, but I have tried repeat mode now with > my patch and it works out of the box. Both from an info buffer, and from other > buffer. It is kind-a cool, was not familiar with repeat mode before. But it's needed only for Info-other-window commands, not for Info-same-window commands. > Can I get *some* help? When rewriting Info-mode-map definition, so I can use > :repeat keword to skip typing bunch of "put" statements, I couldn't add tool-bar > shortcuts in the map definitions. How do I use those with kbd syntax? > > (defvar-keymap Info-mode-map > ( ... ) > ;;"<tool-bar> <C-Back in history>" 'Info-history-back-menu > ;;"<tool-bar> <C-Forward in history>" 'Info-history-forward-menu > ) > > (define-key Info-mode-map [tool-bar C-Back\ in\ history] 'Info-history-back-menu) > (define-key Info-mode-map [tool-bar C-Forward\ in\ history] 'Info-history-forward-menu) > > I would like to not have to use those two define-key statements after the > definition. It seems you found a bug in 'key-valid-p'! And indeed it returns nil: (key-valid-p "<tool-bar> <C-Back in history>") Whereas (kbd "<tool-bar> <C-Back in history>") correctly returns [tool-bar C-Back\ in\ history]. This is a clear bug in emacs-29. > Is there some way to tell interactive where all propts will be placed, without > parsing interactive form and checking strings for interactive codes or something > similar awkward? Yes, there is such way by using the variable 'minibuffer-follows-selected-frame'. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-02 17:53 ` Juri Linkov @ 2023-07-02 18:39 ` Eli Zaretskii 2023-07-02 22:43 ` Arthur Miller 2023-07-02 22:05 ` Arthur Miller 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2023-07-02 18:39 UTC (permalink / raw) To: Juri Linkov; +Cc: arthur.miller, emacs-devel > From: Juri Linkov <juri@linkov.net> > Cc: emacs-devel@gnu.org > Date: Sun, 02 Jul 2023 20:53:51 +0300 > > It seems you found a bug in 'key-valid-p'! > And indeed it returns nil: > > (key-valid-p "<tool-bar> <C-Back in history>") Why is it a bug? "<tool-bar> <C-Back in history>" has no meaning, since it isn't a mouse event. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-02 18:39 ` Eli Zaretskii @ 2023-07-02 22:43 ` Arthur Miller 2023-07-03 11:46 ` Eli Zaretskii 2023-07-03 17:07 ` Eli Zaretskii 0 siblings, 2 replies; 53+ messages in thread From: Arthur Miller @ 2023-07-02 22:43 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Juri Linkov, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Juri Linkov <juri@linkov.net> >> Cc: emacs-devel@gnu.org >> Date: Sun, 02 Jul 2023 20:53:51 +0300 >> >> It seems you found a bug in 'key-valid-p'! >> And indeed it returns nil: >> >> (key-valid-p "<tool-bar> <C-Back in history>") > > Why is it a bug? "<tool-bar> <C-Back in history>" has no meaning, > since it isn't a mouse event. Is "remap" a mouse event? I am not familiar with the work behind the new defvar-keymap, I was just looking at the existing code and expected it to be consistent with the old vector syntax, since some other constructs seems to work with it. It would be also nice if everything was in the same definition. It is not necessary, but nice. Just an opinon. #+begin_src emacs-lisp (defvar-keymap Info-mode-map :doc "Keymap containing Info commands." :repeat t :suppress t "." #'Info-beginning-of-buffer "SPC" #'Info-scroll-up "S-SPC" #'Info-scroll-down "C-m" #'Info-follow-nearest-node "<tab>" #'Info-next-reference "S-TAB" #'Info-prev-reference "<backtab>" #'Info-prev-reference "1" #'Info-nth-menu-item "2" #'Info-nth-menu-item "3" #'Info-nth-menu-item "4" #'Info-nth-menu-item "5" #'Info-nth-menu-item "6" #'Info-nth-menu-item "7" #'Info-nth-menu-item "8" #'Info-nth-menu-item "9" #'Info-nth-menu-item "0" #'undefined "?" #'Info-summary "]" #'Info-forward-node "[" #'Info-backward-node "<" #'Info-top-node ">" #'Info-final-node "b" #'Info-beginning-of-buffer "d" #'Info-directory "e" #'Info-end-of-buffer "f" #'Info-follow-reference "g" #'Info-goto-node "G" #'Info-goto-node-web "h" #'Info-help ;; 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>. "H" #'Info-describe-mode "i" #'Info-index "I" #'Info-virtual-index "l" #'Info-history-back "L" #'Info-history "m" #'Info-menu "n" #'Info-next "p" #'Info-prev "q" #'Info-quit-window "r" #'Info-history-forward "s" #'Info-search "S" #'Info-search-case-sensitively "M-n" #'clone-buffer "t" #'Info-top-node "T" #'Info-toc "u" #'Info-up ;; `w' for consistency with `dired-copy-filename-as-kill'. "w" #'Info-copy-current-node-name "c" #'Info-copy-current-node-name ;; `^' for consistency with `dired-up-directory'. "^" #'Info-up "," #'Info-index-next "DEL" #'Info-scroll-down "<remap> <goto-line>" #'goto-line-relative "<mouse-2>" #'Info-mouse-follow-nearest-node "<follow-link>" 'mouse-face "<XF86Back>" #'Info-history-back "<XF86Forward>" #'Info-history-forward) #+end_src By the way, as a last thing, is this a bug (found it while I was parsing Info-mode-map and generated wrappers for commands in it some time ago): (insert (pp Info-mode-map)) (keymap #^[nil nil keymap #^^[3 0 nil nil nil nil nil nil nil nil nil Info-next-reference nil nil nil Info-follow-nearest-node nil nil nil nil nil nil nil nil nil nil nil nil nil I have removed rest of printed code for the brewity for the mail, but if you eval above and place the cursor somewhere after the "keymap", or before the char table, and then try to read the subtype with: (char-table-subtype (read (current-buffer))) Then Emacs dumps core in version 26 up to current master. Emacs 25 actually ends up in debugger instead of crashing: Debugger entered--Lisp error: (wrong-type-argument char-table-p #^^[3 0 nil nil nil nil nil nil nil nil nil Info-next-reference nil nil nil Info-follow-nearest-node nil nil nil nil nil nil nil nil nil nil nil nil nil (keymap (110 . clone-buffer) (9 . Info-prev-reference)) nil nil nil nil Info-scroll-up nil nil nil nil nil nil nil nil nil nil nil Info-index-next negative-argument beginning-of-buffer nil undefined Info-nth-menu-item Info-nth-menu-item Info-nth-menu-item Info-nth-menu-item Info-nth-menu-item Info-nth-menu-item Info-nth-menu-item Info-nth-menu-item Info-nth-menu-item nil nil Info-top-node nil Info-final-node Info-summary nil nil nil nil nil nil nil nil describe-mode Info-virtual-index nil nil Info-history nil nil nil nil nil nil Info-search-case-sensitively Info-toc nil nil nil nil nil nil Info-backward-node nil Info-forward-node Info-up nil nil nil beginning-of-buffer Info-copy-current-node-name Info-directory end-of-buffer Info-follow-reference Info-goto-node Info-help Info-index nil nil Info-history-back Info-menu Info-next nil Info-prev Info-exit Info-history-forward Info-search Info-top-node Info-up nil Info-copy-current-node-name nil nil nil nil nil nil nil Info-scroll-down]) #^^[3 0 nil nil nil nil nil nil nil nil nil Info-next-reference nil nil nil Info-follow-nearest-node nil nil nil nil nil nil nil nil nil nil nil nil nil (keymap (110 . clone-buffer) (9 . Info-prev-reference)) nil nil nil nil Info-scroll-up nil nil nil nil nil nil nil nil nil nil nil Info-index-next negative-argument beginning-of-buffer nil undefined ...])) char-table-subtype( #^^[3 0 nil nil nil nil nil nil nil nil nil Info-next-reference nil nil nil Info-follow-nearest-node nil nil nil nil nil nil nil nil nil nil nil nil nil (keymap (110 . clone-buffer) (9 . Info-prev-reference)) nil nil nil nil Info-scroll-up nil nil nil nil nil nil nil nil nil nil nil Info-index-next negative-argument beginning-of-buffer nil undefined ...]) eval((char-table-subtype (read (current-buffer))) nil) eval-expression((char-table-subtype (read (current-buffer))) nil) funcall-interactively(eval-expression (char-table-subtype (read (current-buffer))) nil) call-interactively(eval-expression nil nil) command-execute(eval-expression) Not of crucial importance, but perhaps of inerest to know if it is not already known. Perhaps it is known? I found it some time ago, but was testing today with different Emacs versions, and found now it seems to appear first in version 26. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-02 22:43 ` Arthur Miller @ 2023-07-03 11:46 ` Eli Zaretskii 2023-07-03 12:57 ` Arthur Miller 2023-07-03 17:07 ` Eli Zaretskii 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2023-07-03 11:46 UTC (permalink / raw) To: Arthur Miller; +Cc: juri, emacs-devel > From: Arthur Miller <arthur.miller@live.com> > Cc: Juri Linkov <juri@linkov.net>, emacs-devel@gnu.org > Date: Mon, 03 Jul 2023 00:43:15 +0200 > > >> It seems you found a bug in 'key-valid-p'! > >> And indeed it returns nil: > >> > >> (key-valid-p "<tool-bar> <C-Back in history>") > > > > Why is it a bug? "<tool-bar> <C-Back in history>" has no meaning, > > since it isn't a mouse event. > > Is "remap" a mouse event? Why is "remap" relevant to the issue at hand? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-03 11:46 ` Eli Zaretskii @ 2023-07-03 12:57 ` Arthur Miller 2023-07-03 13:17 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-07-03 12:57 UTC (permalink / raw) To: Eli Zaretskii; +Cc: juri, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Arthur Miller <arthur.miller@live.com> >> Cc: Juri Linkov <juri@linkov.net>, emacs-devel@gnu.org >> Date: Mon, 03 Jul 2023 00:43:15 +0200 >> >> >> It seems you found a bug in 'key-valid-p'! >> >> And indeed it returns nil: >> >> >> >> (key-valid-p "<tool-bar> <C-Back in history>") >> > >> > Why is it a bug? "<tool-bar> <C-Back in history>" has no meaning, >> > since it isn't a mouse event. >> >> Is "remap" a mouse event? > > Why is "remap" relevant to the issue at hand? I converted from "old idiom" in info.el to new defvar-keymap. When seeing "remap" (a dummy event) works, I just expected defvar-keymap was done the way that it is possible to use all keys, fake-kyes, events, and whatnot the same way. Similar as I expected 'frame to have same meaning everywhere for the uniformity. Would look more tidy if it was possible to keep all key definitions in the same place as in the old code. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-03 12:57 ` Arthur Miller @ 2023-07-03 13:17 ` Eli Zaretskii 2023-07-03 18:40 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2023-07-03 13:17 UTC (permalink / raw) To: Arthur Miller; +Cc: juri, emacs-devel > From: Arthur Miller <arthur.miller@live.com> > Cc: juri@linkov.net, emacs-devel@gnu.org > Date: Mon, 03 Jul 2023 14:57:54 +0200 > > Eli Zaretskii <eliz@gnu.org> writes: > > >> From: Arthur Miller <arthur.miller@live.com> > >> Cc: Juri Linkov <juri@linkov.net>, emacs-devel@gnu.org > >> Date: Mon, 03 Jul 2023 00:43:15 +0200 > >> > >> >> It seems you found a bug in 'key-valid-p'! > >> >> And indeed it returns nil: > >> >> > >> >> (key-valid-p "<tool-bar> <C-Back in history>") > >> > > >> > Why is it a bug? "<tool-bar> <C-Back in history>" has no meaning, > >> > since it isn't a mouse event. > >> > >> Is "remap" a mouse event? > > > > Why is "remap" relevant to the issue at hand? > > I converted from "old idiom" in info.el to new defvar-keymap. When > seeing "remap" (a dummy event) works, I just expected defvar-keymap was > done the way that it is possible to use all keys, fake-kyes, events, > and whatnot the same way. Similar as I expected 'frame to have same > meaning everywhere for the uniformity. > > Would look more tidy if it was possible to keep all key definitions in > the same place as in the old code. Do you mean that we had a binding like [tool-bar C-Back\ in\ history] somewhere? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-03 13:17 ` Eli Zaretskii @ 2023-07-03 18:40 ` Juri Linkov 2023-07-03 18:57 ` Eli Zaretskii 2023-07-03 21:07 ` Info-mode patch Arthur Miller 0 siblings, 2 replies; 53+ messages in thread From: Juri Linkov @ 2023-07-03 18:40 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Arthur Miller, emacs-devel >> >> >> It seems you found a bug in 'key-valid-p'! >> >> >> And indeed it returns nil: >> >> >> >> >> >> (key-valid-p "<tool-bar> <C-Back in history>") >> >> > >> >> > Why is it a bug? "<tool-bar> <C-Back in history>" has no meaning, >> >> > since it isn't a mouse event. >> >> >> >> Is "remap" a mouse event? >> > >> > Why is "remap" relevant to the issue at hand? >> >> I converted from "old idiom" in info.el to new defvar-keymap. When >> seeing "remap" (a dummy event) works, I just expected defvar-keymap was >> done the way that it is possible to use all keys, fake-kyes, events, >> and whatnot the same way. Similar as I expected 'frame to have same >> meaning everywhere for the uniformity. >> >> Would look more tidy if it was possible to keep all key definitions in >> the same place as in the old code. > > Do you mean that we had a binding like [tool-bar C-Back\ in\ history] > somewhere? This symbol is generated automatically by 'easy-menu-define' from the menu item "Back in History" with spaces inside. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-03 18:40 ` Juri Linkov @ 2023-07-03 18:57 ` Eli Zaretskii 2023-07-04 6:50 ` easy-menu-define keys for key-valid-p (was: Info-mode patch) Juri Linkov 2023-07-03 21:07 ` Info-mode patch Arthur Miller 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2023-07-03 18:57 UTC (permalink / raw) To: Juri Linkov; +Cc: arthur.miller, emacs-devel > From: Juri Linkov <juri@linkov.net> > Cc: Arthur Miller <arthur.miller@live.com>, emacs-devel@gnu.org > Date: Mon, 03 Jul 2023 21:40:10 +0300 > > >> >> >> It seems you found a bug in 'key-valid-p'! > >> >> >> And indeed it returns nil: > >> >> >> > >> >> >> (key-valid-p "<tool-bar> <C-Back in history>") > >> >> > > >> >> > Why is it a bug? "<tool-bar> <C-Back in history>" has no meaning, > >> >> > since it isn't a mouse event. > >> >> > >> >> Is "remap" a mouse event? > >> > > >> > Why is "remap" relevant to the issue at hand? > >> > >> I converted from "old idiom" in info.el to new defvar-keymap. When > >> seeing "remap" (a dummy event) works, I just expected defvar-keymap was > >> done the way that it is possible to use all keys, fake-kyes, events, > >> and whatnot the same way. Similar as I expected 'frame to have same > >> meaning everywhere for the uniformity. > >> > >> Would look more tidy if it was possible to keep all key definitions in > >> the same place as in the old code. > > > > Do you mean that we had a binding like [tool-bar C-Back\ in\ history] > > somewhere? > > This symbol is generated automatically by 'easy-menu-define' > from the menu item "Back in History" with spaces inside. key-valid-p invalidates keys that have embedded whitespace, so we need to decide whether we want to add that or modify easy-menu-define to produce more reasonable symbols. ^ permalink raw reply [flat|nested] 53+ messages in thread
* easy-menu-define keys for key-valid-p (was: Info-mode patch) 2023-07-03 18:57 ` Eli Zaretskii @ 2023-07-04 6:50 ` Juri Linkov 2023-07-04 11:33 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-07-04 6:50 UTC (permalink / raw) To: Eli Zaretskii; +Cc: arthur.miller, emacs-devel >> >> >> >> It seems you found a bug in 'key-valid-p'! >> >> >> >> And indeed it returns nil: >> >> >> >> >> >> >> >> (key-valid-p "<tool-bar> <C-Back in history>") >> >> >> > >> >> >> > Why is it a bug? "<tool-bar> <C-Back in history>" has no meaning, >> >> >> > since it isn't a mouse event. >> >> >> >> >> >> Is "remap" a mouse event? >> >> > >> >> > Why is "remap" relevant to the issue at hand? >> >> >> >> I converted from "old idiom" in info.el to new defvar-keymap. When >> >> seeing "remap" (a dummy event) works, I just expected defvar-keymap was >> >> done the way that it is possible to use all keys, fake-kyes, events, >> >> and whatnot the same way. Similar as I expected 'frame to have same >> >> meaning everywhere for the uniformity. >> >> >> >> Would look more tidy if it was possible to keep all key definitions in >> >> the same place as in the old code. >> > >> > Do you mean that we had a binding like [tool-bar C-Back\ in\ history] >> > somewhere? >> >> This symbol is generated automatically by 'easy-menu-define' >> from the menu item "Back in History" with spaces inside. > > key-valid-p invalidates keys that have embedded whitespace, so we need > to decide whether we want to add that or modify easy-menu-define to > produce more reasonable symbols. Currently easy-menu-intern keeps spaces in strings: (defsubst easy-menu-intern (s) (if (stringp s) (intern s) s)) Replacing it with: (defsubst easy-menu-intern (s) (if (stringp s) (intern (string-replace " " "-" (downcase s))) s)) produces from the menu item "Back in History" a more reasonable symbol 'back-in-history' for which (key-valid-p "<tool-bar> C-<back-in-history>") returns t and can be used in defvar-keymap. OTOH, [tool-bar C-Back\ in\ history] worked in 27.2 but broke in 28.2 with "<tool-bar> C-<Back in History> is undefined". After the change above, [tool-bar C-back-in-history] works again. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: easy-menu-define keys for key-valid-p (was: Info-mode patch) 2023-07-04 6:50 ` easy-menu-define keys for key-valid-p (was: Info-mode patch) Juri Linkov @ 2023-07-04 11:33 ` Eli Zaretskii 0 siblings, 0 replies; 53+ messages in thread From: Eli Zaretskii @ 2023-07-04 11:33 UTC (permalink / raw) To: Juri Linkov; +Cc: arthur.miller, emacs-devel > From: Juri Linkov <juri@linkov.net> > Cc: arthur.miller@live.com, emacs-devel@gnu.org > Date: Tue, 04 Jul 2023 09:50:47 +0300 > > >> This symbol is generated automatically by 'easy-menu-define' > >> from the menu item "Back in History" with spaces inside. > > > > key-valid-p invalidates keys that have embedded whitespace, so we need > > to decide whether we want to add that or modify easy-menu-define to > > produce more reasonable symbols. > > Currently easy-menu-intern keeps spaces in strings: > > (defsubst easy-menu-intern (s) > (if (stringp s) (intern s) s)) > > Replacing it with: > > (defsubst easy-menu-intern (s) > (if (stringp s) (intern (string-replace " " "-" (downcase s))) s)) > > produces from the menu item "Back in History" a more reasonable > symbol 'back-in-history' for which > > (key-valid-p "<tool-bar> C-<back-in-history>") > > returns t and can be used in defvar-keymap. > > OTOH, [tool-bar C-Back\ in\ history] worked in 27.2 but broke in 28.2 > with "<tool-bar> C-<Back in History> is undefined". > > After the change above, [tool-bar C-back-in-history] works again. Stefan, WDYT about this? What is the best way of fixing this inconsistency between easy-menu-define and key-valid-p? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-03 18:40 ` Juri Linkov 2023-07-03 18:57 ` Eli Zaretskii @ 2023-07-03 21:07 ` Arthur Miller 2023-07-04 7:59 ` Andreas Schwab 1 sibling, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-07-03 21:07 UTC (permalink / raw) To: Juri Linkov; +Cc: Eli Zaretskii, emacs-devel Juri Linkov <juri@linkov.net> writes: >>> >> >> It seems you found a bug in 'key-valid-p'! >>> >> >> And indeed it returns nil: >>> >> >> >>> >> >> (key-valid-p "<tool-bar> <C-Back in history>") >>> >> > >>> >> > Why is it a bug? "<tool-bar> <C-Back in history>" has no meaning, >>> >> > since it isn't a mouse event. >>> >> >>> >> Is "remap" a mouse event? >>> > >>> > Why is "remap" relevant to the issue at hand? >>> >>> I converted from "old idiom" in info.el to new defvar-keymap. When >>> seeing "remap" (a dummy event) works, I just expected defvar-keymap was >>> done the way that it is possible to use all keys, fake-kyes, events, >>> and whatnot the same way. Similar as I expected 'frame to have same >>> meaning everywhere for the uniformity. >>> >>> Would look more tidy if it was possible to keep all key definitions in >>> the same place as in the old code. >> >> Do you mean that we had a binding like [tool-bar C-Back\ in\ history] >> somewhere? It is in info.el since version 27: (defvar Info-mode-map (let ((map (make-keymap))) (suppress-keymap map) (define-key map "." '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) (define-key map "\t" 'Info-next-reference) (define-key map "\e\t" 'Info-prev-reference) (define-key map [backtab] 'Info-prev-reference) (define-key map "1" 'Info-nth-menu-item) (define-key map "2" 'Info-nth-menu-item) (define-key map "3" 'Info-nth-menu-item) (define-key map "4" 'Info-nth-menu-item) (define-key map "5" 'Info-nth-menu-item) (define-key map "6" 'Info-nth-menu-item) (define-key map "7" 'Info-nth-menu-item) (define-key map "8" 'Info-nth-menu-item) (define-key map "9" 'Info-nth-menu-item) (define-key map "0" 'undefined) (define-key map "?" 'Info-summary) (define-key map "]" 'Info-forward-node) (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) (put 'beginning-of-buffer :advertised-binding "b") (define-key map "d" 'Info-directory) (define-key map "e" '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) (define-key map "h" 'Info-help) ;; 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 "i" 'Info-index) (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 "r" 'Info-history-forward) (define-key map "s" 'Info-search) (define-key map "S" 'Info-search-case-sensitively) (define-key map "\M-n" 'clone-buffer) (define-key map "t" 'Info-top-node) (define-key map "T" 'Info-toc) (define-key map "u" 'Info-up) ;; `w' for consistency with `dired-copy-filename-as-kill'. (define-key map "w" 'Info-copy-current-node-name) (define-key map "c" 'Info-copy-current-node-name) ;; `^' for consistency with `dired-up-directory'. (define-key map "^" 'Info-up) (define-key map "," 'Info-index-next) (define-key map "\177" 'Info-scroll-down) (define-key map [remap goto-line] 'goto-line-relative) (define-key map [mouse-2] 'Info-mouse-follow-nearest-node) (define-key map [follow-link] 'mouse-face) (define-key map [XF86Back] 'Info-history-back) (define-key map [XF86Forward] 'Info-history-forward) (define-key map [tool-bar C-Back\ in\ history] 'Info-history-back-menu) (define-key map [tool-bar C-Forward\ in\ history] 'Info-history-forward-menu) map) "Keymap containing Info commands.") > This symbol is generated automatically by 'easy-menu-define' > from the menu item "Back in History" with spaces inside. Since menus in Emacs are implemented as keymaps, I thought toolbar is "sort of a menu" and probably implemented as a keymap too, and that tool-bar was some "special event". ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-03 21:07 ` Info-mode patch Arthur Miller @ 2023-07-04 7:59 ` Andreas Schwab 2023-07-04 8:44 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Andreas Schwab @ 2023-07-04 7:59 UTC (permalink / raw) To: Arthur Miller; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel On Jul 03 2023, Arthur Miller wrote: > (define-key map [tool-bar C-Back\ in\ history] 'Info-history-back-menu) > (define-key map [tool-bar C-Forward\ in\ history] 'Info-history-forward-menu) Does that actually work? When I ctrl-click on the Back icon in the tool bar, I get "<tool-bar> C-<Back in History> is undefined". -- Andreas Schwab, SUSE Labs, schwab@suse.de GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7 "And now for something completely different." ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-04 7:59 ` Andreas Schwab @ 2023-07-04 8:44 ` Arthur Miller 0 siblings, 0 replies; 53+ messages in thread From: Arthur Miller @ 2023-07-04 8:44 UTC (permalink / raw) To: Andreas Schwab; +Cc: Juri Linkov, Eli Zaretskii, emacs-devel Andreas Schwab <schwab@suse.de> writes: > On Jul 03 2023, Arthur Miller wrote: > >> (define-key map [tool-bar C-Back\ in\ history] 'Info-history-back-menu) >> (define-key map [tool-bar C-Forward\ in\ history] 'Info-history-forward-menu) > > Does that actually work? When I ctrl-click on the Back icon in the tool > bar, I get "<tool-bar> C-<Back in History> is undefined". No idea; I didn't tested; I don't use menubar and toolbar normally. I just copied what was in the info.el already and assumed that was tested and working, I had no reason to mistrust something that was in info.el since before, and as said it was there since version 27. If it does not work, than it is yet another bug I guess. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-02 22:43 ` Arthur Miller 2023-07-03 11:46 ` Eli Zaretskii @ 2023-07-03 17:07 ` Eli Zaretskii 2023-07-04 23:58 ` Stefan Monnier 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2023-07-03 17:07 UTC (permalink / raw) To: Arthur Miller, Stefan Monnier; +Cc: juri, emacs-devel > From: Arthur Miller <arthur.miller@live.com> > Cc: Juri Linkov <juri@linkov.net>, emacs-devel@gnu.org > Date: Mon, 03 Jul 2023 00:43:15 +0200 > > By the way, as a last thing, is this a bug (found it while I was parsing > Info-mode-map and generated wrappers for commands in it some time ago): > > (insert (pp Info-mode-map)) > > (keymap > #^[nil nil keymap > #^^[3 0 nil nil nil nil nil nil nil nil nil Info-next-reference nil nil nil Info-follow-nearest-node nil nil nil nil nil nil nil nil nil nil nil nil nil > > I have removed rest of printed code for the brewity for the mail, but if you > eval above and place the cursor somewhere after the "keymap", or before the char > table, and then try to read the subtype with: > > (char-table-subtype (read (current-buffer))) > > Then Emacs dumps core in version 26 up to current master. Emacs 25 actually ends > up in debugger instead of crashing: (It is not a crash, it's abort.) Please report a bug with all these details. AFAICT, this happens because the debugger now uses cl-print to display objects in the backtrace, and cl-print calls type-of, which aborts if presented with a sub-char-table: /* "Impossible" cases. */ case PVEC_MISC_PTR: case PVEC_OTHER: case PVEC_SUB_CHAR_TABLE: case PVEC_FREE: ; } emacs_abort (); ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-03 17:07 ` Eli Zaretskii @ 2023-07-04 23:58 ` Stefan Monnier 2023-07-08 8:14 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Stefan Monnier @ 2023-07-04 23:58 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Arthur Miller, juri, emacs-devel >> (keymap >> #^[nil nil keymap >> #^^[3 0 nil nil nil nil nil nil nil nil nil Info-next-reference nil nil nil Info-follow-nearest-node nil nil nil nil nil nil nil nil nil nil nil nil nil >> >> I have removed rest of printed code for the brewity for the mail, but if you >> eval above and place the cursor somewhere after the "keymap", or before the char >> table, and then try to read the subtype with: >> >> (char-table-subtype (read (current-buffer))) Hmm... I now see that `read` does work on a sub-char-table and returns that sub-char-table, so contrary to what I thought, they are exposed to ELisp, and also contrary to what `lisp.h` suggests: /* A char-table is a kind of vectorlike, with contents like a vector, but with a few additional slots. [...] [...] A sub-char-table appears only in an element of a char-table, and there's no way to access it directly from a Lisp program. */ I wonder what ELisp can do with it (other than abort/crash, that is :-) > AFAICT, this happens because the debugger now uses cl-print to display > objects in the backtrace, and cl-print calls type-of, which aborts if > presented with a sub-char-table: > > /* "Impossible" cases. */ > case PVEC_MISC_PTR: > case PVEC_OTHER: > case PVEC_SUB_CHAR_TABLE: > case PVEC_FREE: ; > } > emacs_abort (); The easy fix is to make `type-of` return `sub-char-table`, of course, as in the patch below. But if we go that way, then we should at least update the comment in `lisp.h`. Another option is to change `read` so it refuses to return a sub-char-table. Stefan diff --git a/src/data.c b/src/data.c index 9d7e7effdcd..6de8e0cf1a1 100644 --- a/src/data.c +++ b/src/data.c @@ -269,10 +269,11 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, return Qtreesit_compiled_query; case PVEC_SQLITE: return Qsqlite; + case PVEC_SUB_CHAR_TABLE: + return Qsub_char_table; /* "Impossible" cases. */ case PVEC_MISC_PTR: case PVEC_OTHER: - case PVEC_SUB_CHAR_TABLE: case PVEC_FREE: ; } emacs_abort (); @@ -4215,6 +4216,7 @@ #define PUT_ERROR(sym, tail, msg) \ DEFSYM (Qvector, "vector"); DEFSYM (Qrecord, "record"); DEFSYM (Qchar_table, "char-table"); + DEFSYM (Qsub_char_table, "sub-char-table"); DEFSYM (Qbool_vector, "bool-vector"); DEFSYM (Qhash_table, "hash-table"); DEFSYM (Qthread, "thread"); ^ permalink raw reply related [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-04 23:58 ` Stefan Monnier @ 2023-07-08 8:14 ` Eli Zaretskii 0 siblings, 0 replies; 53+ messages in thread From: Eli Zaretskii @ 2023-07-08 8:14 UTC (permalink / raw) To: Stefan Monnier; +Cc: arthur.miller, juri, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: Arthur Miller <arthur.miller@live.com>, juri@linkov.net, > emacs-devel@gnu.org > Date: Tue, 04 Jul 2023 19:58:12 -0400 > > Hmm... I now see that `read` does work on a sub-char-table and returns > that sub-char-table, so contrary to what I thought, they are exposed to > ELisp, and also contrary to what `lisp.h` suggests: > > /* A char-table is a kind of vectorlike, with contents like a vector, > but with a few additional slots. [...] > [...] A sub-char-table appears only in an element of a > char-table, and there's no way to access it directly from a Lisp > program. */ > > I wonder what ELisp can do with it (other than abort/crash, that is :-) > > > AFAICT, this happens because the debugger now uses cl-print to display > > objects in the backtrace, and cl-print calls type-of, which aborts if > > presented with a sub-char-table: > > > > /* "Impossible" cases. */ > > case PVEC_MISC_PTR: > > case PVEC_OTHER: > > case PVEC_SUB_CHAR_TABLE: > > case PVEC_FREE: ; > > } > > emacs_abort (); > > The easy fix is to make `type-of` return `sub-char-table`, of course, as > in the patch below. But if we go that way, then we should at least > update the comment in `lisp.h`. > Another option is to change `read` so it refuses to return > a sub-char-table. Thanks, I installed the patch in your name (and updated the comment), and I'm therefore closing bug#64450. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-02 17:53 ` Juri Linkov 2023-07-02 18:39 ` Eli Zaretskii @ 2023-07-02 22:05 ` Arthur Miller 2023-07-03 18:45 ` Juri Linkov 1 sibling, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-07-02 22:05 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>>>> Another advantage of wrapper commands is that you can implement what >>>>> Manuel asked you to do to make key sequences for another window repeatable. >>>>> This is possible only when you have separate wrapper commands like >>>>> Info-directory-other-window and a separate keymap with them like >>>>> Info-other-window-map. >>>> >>>> What exactly would not make my patch work with repeating commands? >>> >>> You can't add repeat symbols on existing Info commands used in Info buffers, >>> only wrapper commands with a prefix key should be repeatable. >> >> I am not sure what that means, but I have tried repeat mode now with >> my patch and it works out of the box. Both from an info buffer, and from other >> buffer. It is kind-a cool, was not familiar with repeat mode before. > > But it's needed only for Info-other-window commands, > not for Info-same-window commands. Yes, I agree, and I don't need I need repeat mode for too many commands from other window either; but since I have rebuilt the info mode itself, I have "only" Info-mode-map, there are no "same-window/other-window" commands, and it worked just fine with putting repeat mode on all, but that was expected. For the record, I was able to port Info from version current master back to version 25 today. Only bad deal was a mailformad Info-spec, but everything seems to work well when I test. >> Can I get *some* help? When rewriting Info-mode-map definition, so I can use >> :repeat keword to skip typing bunch of "put" statements, I couldn't add tool-bar >> shortcuts in the map definitions. How do I use those with kbd syntax? >> >> (defvar-keymap Info-mode-map >> ( ... ) >> ;;"<tool-bar> <C-Back in history>" 'Info-history-back-menu >> ;;"<tool-bar> <C-Forward in history>" 'Info-history-forward-menu >> ) >> >> (define-key Info-mode-map [tool-bar C-Back\ in\ history] 'Info-history-back-menu) >> (define-key Info-mode-map [tool-bar C-Forward\ in\ history] 'Info-history-forward-menu) >> >> I would like to not have to use those two define-key statements after the >> definition. > > It seems you found a bug in 'key-valid-p'! > And indeed it returns nil: > > (key-valid-p "<tool-bar> <C-Back in history>") > > Whereas > > (kbd "<tool-bar> <C-Back in history>") > > correctly returns [tool-bar C-Back\ in\ history]. > > This is a clear bug in emacs-29. Ok. Thanks, good to know. Then I'll keep those two outside of keymap definition. >> Is there some way to tell interactive where all propts will be placed, without >> parsing interactive form and checking strings for interactive codes or something >> similar awkward? > > Yes, there is such way by using the variable 'minibuffer-follows-selected-frame'. So at least in theory, by let-bidinging it to nil, it should keep prompt on the original frame. I did just quick naive test, and ended up in debugger: (defun info-menu-wrapper () (interactive) (let ((minibuffer-follows-selected-frame nil)) (with-selected-window (info-window) (call-interactively #'Info-menu)))) but perhaps there are more tweacks to it which I am not familiar with, so I'll leave it at that. About wrapping; I agree that it is messy to go through each and every command as I did to modify them, so for old existing commands, it is definitely easier to do the wrapping, if possible. I just hope we get a better way for future command writing. Thanks for the answer ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-02 22:05 ` Arthur Miller @ 2023-07-03 18:45 ` Juri Linkov 2023-07-03 22:24 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-07-03 18:45 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >>> Is there some way to tell interactive where all propts will be placed, without >>> parsing interactive form and checking strings for interactive codes or something >>> similar awkward? >> >> Yes, there is such way by using the variable 'minibuffer-follows-selected-frame'. > > So at least in theory, by let-bidinging it to nil, it should keep prompt > on the original frame. I did just quick naive test, and ended up in debugger: > > (defun info-menu-wrapper () > (interactive) > (let ((minibuffer-follows-selected-frame nil)) > (with-selected-window (info-window) > (call-interactively #'Info-menu)))) > > but perhaps there are more tweacks to it which I am not familiar with, so I'll > leave it at that. What happens when you customize minibuffer-follows-selected-frame to a third possible value that is not nil and not t. From docs: Any other value means the minibuffer will move onto another frame, but only when the user starts using a minibuffer there. I guess this variable is not intended to do what you expected. But AFAIU, what you need is only to use with-current-buffer wrapped around the interactive spec? There is no need to select another window/frame while reading from the minibuffer? > About wrapping; I agree that it is messy to go through each and every command as > I did to modify them, so for old existing commands, it is definitely easier to > do the wrapping, if possible. I just hope we get a better way for future command > writing. I don't like creating wrapper commands too, but it seems there is no better way, at least no one proposed anything better. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-03 18:45 ` Juri Linkov @ 2023-07-03 22:24 ` Arthur Miller 2023-07-04 6:54 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-07-03 22:24 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>>> Is there some way to tell interactive where all propts will be placed, without >>>> parsing interactive form and checking strings for interactive codes or something >>>> similar awkward? >>> >>> Yes, there is such way by using the variable 'minibuffer-follows-selected-frame'. >> >> So at least in theory, by let-bidinging it to nil, it should keep prompt >> on the original frame. I did just quick naive test, and ended up in debugger: >> >> (defun info-menu-wrapper () >> (interactive) >> (let ((minibuffer-follows-selected-frame nil)) >> (with-selected-window (info-window) >> (call-interactively #'Info-menu)))) >> >> but perhaps there are more tweacks to it which I am not familiar with, so I'll >> leave it at that. > > What happens when you customize minibuffer-follows-selected-frame > to a third possible value that is not nil and not t. From docs: > > Any other value means the minibuffer will move onto another frame, but > only when the user starts using a minibuffer there. The very same. I used a symbol 'something as the third value. > I guess this variable is not intended to do what you expected. What should I have expected? :) > But AFAIU, what you need is only to use with-current-buffer > wrapped around the interactive spec? There is no need > to select another window/frame while reading from the minibuffer? As said earlier, that highly depends on the work done in the interactive form; but for the majority of commands, and those in info.el specifically, it should be enough I believe. This works: (defun info-menu-wrapper () (interactive) (let ((window (info-window))) (with-current-buffer (window-buffer window) (let ((args (eval (cadr (interactive-form 'Info-menu))))) (with-selected-window window (apply #'Info-menu args)))))) I would still take it with a grain of salt that it will do in all cases, you should test each and every, but in majority cases it should work I think. >> About wrapping; I agree that it is messy to go through each and every command as >> I did to modify them, so for old existing commands, it is definitely easier to >> do the wrapping, if possible. I just hope we get a better way for future command >> writing. > > I don't like creating wrapper commands too, but it seems there is no > better way, at least no one proposed anything better. You were against wrapping everything into with-selected-window, now you everything wrapped into another function :). The positive about wrappers is they will work with old commands, and if you turn that into a:core package in Elpa, then even users of older Emacsens can use it. So I am definitely not against wrappers per se; nor do I believe we should rewrite each and every user command. But for writing new commands, I do suggest to implement better macro; because all this can abstracted away, so we don't double all the commands in the future. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-03 22:24 ` Arthur Miller @ 2023-07-04 6:54 ` Juri Linkov 2023-07-04 9:43 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-07-04 6:54 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >> But AFAIU, what you need is only to use with-current-buffer >> wrapped around the interactive spec? There is no need >> to select another window/frame while reading from the minibuffer? > > As said earlier, that highly depends on the work done in the interactive form; > but for the majority of commands, and those in info.el specifically, it should > be enough I believe. I agree, so commands that don't read the default value from the buffer don't need even with-current-buffer. > This works: > > (defun info-menu-wrapper () > (interactive) > (let ((window (info-window))) > (with-current-buffer (window-buffer window) > (let ((args (eval (cadr (interactive-form 'Info-menu))))) > (with-selected-window window > (apply #'Info-menu args)))))) > > I would still take it with a grain of salt that it will do in all cases, you > should test each and every, but in majority cases it should work I think. If you prefer calling the original command from the body then better to use 'call-interactively'. 'interactive-form' is more suitable for being called from the interactive spec of the wrapper. >>> About wrapping; I agree that it is messy to go through each and every command as >>> I did to modify them, so for old existing commands, it is definitely easier to >>> do the wrapping, if possible. I just hope we get a better way for future command >>> writing. >> >> I don't like creating wrapper commands too, but it seems there is no >> better way, at least no one proposed anything better. > > You were against wrapping everything into with-selected-window, now you > everything wrapped into another function :). I still think that adding new wrapper commands is less wrong than wrapping existing commands into with-selected-window. > The positive about wrappers is they will work with old commands, and if you turn > that into a:core package in Elpa, then even users of older Emacsens can use > it. So I am definitely not against wrappers per se; nor do I believe we should > rewrite each and every user command. > > But for writing new commands, I do suggest to implement better macro; because > all this can abstracted away, so we don't double all the commands in the future. I'm not sure if this should be a new coding convention for writing new commands that should be mentioned in (info "(elisp) Programming Tips"). ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-04 6:54 ` Juri Linkov @ 2023-07-04 9:43 ` Arthur Miller 2023-07-04 17:51 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-07-04 9:43 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>> But AFAIU, what you need is only to use with-current-buffer >>> wrapped around the interactive spec? There is no need >>> to select another window/frame while reading from the minibuffer? >> >> As said earlier, that highly depends on the work done in the interactive form; >> but for the majority of commands, and those in info.el specifically, it should >> be enough I believe. > > I agree, so commands that don't read the default value from the buffer > don't need even with-current-buffer. I believe that generally: * commands that do not prompt user in any way don't need to care at all (just switch to other window) * commands that prompt, but don't need data from the other window in any way, do not need to care either, don't need with-current-buffer either, but can't switch before they are done with prompting * commands that need to do something with data from the target buffer, need to use with-current-buffer and can't switch before they are done with prompting In those cases I believe you can generally just wrap interactive part in with-current-buffer and not worry about it; I wouldn't try to figure out which one needs to be wrapped, which one does not. I have used with-selected-window in all help-mode commands because they are in almost all cases simple, and it will work in all cases (they don't prompt user generally). But there can be other classes of commands, for example if command have to access some data in current buffer, but will act on some other buffer or window or commands that do not act on buffers but on windows, layout, etc, for example quit-window. Again, you have no guarantee what will happen with a command you haven't looked at since they can do anything. In most cases they will probably work by just wrapping them in with-current-buffer, but there is no guarantee. >> This works: >> >> (defun info-menu-wrapper () >> (interactive) >> (let ((window (info-window))) >> (with-current-buffer (window-buffer window) >> (let ((args (eval (cadr (interactive-form 'Info-menu))))) >> (with-selected-window window >> (apply #'Info-menu args)))))) >> >> I would still take it with a grain of salt that it will do in all cases, you >> should test each and every, but in majority cases it should work I think. > > If you prefer calling the original command from the body I am callling the function, not the command, on purpose. > then better to use 'call-interactively'. 'interactive-form' is > more suitable for being called from the interactive spec of the wrapper. Sounds like a complication to me, but I am not as advanced with elisp. Anyway according to the docs interactive-form returns what it says, the interactive form, which as I understand, can be evaled separately in another context to create the list of args one can just pass to the function and not risk extra prompts. I am sure it is possible to do it in more complicated ways then the above too. >>>> About wrapping; I agree that it is messy to go through each and every command as >>>> I did to modify them, so for old existing commands, it is definitely easier to >>>> do the wrapping, if possible. I just hope we get a better way for future command >>>> writing. >>> >>> I don't like creating wrapper commands too, but it seems there is no >>> better way, at least no one proposed anything better. >> >> You were against wrapping everything into with-selected-window, now you >> everything wrapped into another function :). > > I still think that adding new wrapper commands is less wrong than > wrapping existing commands into with-selected-window. Well, for the info.el and help-mode, I do prefer to use the work I have done. Were some bugs; I forgott the search, but I have fixed it and everything works fine so I don't need double keymap and wrappers. But I wouldn't do the same for all modes. >> The positive about wrappers is they will work with old commands, and if you turn >> that into a:core package in Elpa, then even users of older Emacsens can use >> it. So I am definitely not against wrappers per se; nor do I believe we should >> rewrite each and every user command. >> >> But for writing new commands, I do suggest to implement better macro; because >> all this can abstracted away, so we don't double all the commands in the future. > > I'm not sure if this should be a new coding convention for writing new commands > that should be mentioned in (info "(elisp) Programming Tips"). I think that should definitely be written about in the manual. I have used nothing but with-current-buffer, with-selected-frame and with-selected-window to achieve this. We could have had this all the time, if we just coded commands in a better way, and we wouldn't even needed "-other-window" wrappers. This can also be wrapped in a macro, say define-command, or defcmd, that does this automatically, so the future commands are callable from either buffer they act on or an other buffer. If there was an option to say to Emacs that prompts should always stay on the frame where a command loop is initiated, then the gymnastics with wrapping interactive-from into target buffer could be skipped, and new commands would only need to wrap their body into with-selected-window. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-04 9:43 ` Arthur Miller @ 2023-07-04 17:51 ` Juri Linkov 2023-07-04 21:40 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-07-04 17:51 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel >>> (defun info-menu-wrapper () >>> (interactive) >>> (let ((window (info-window))) >>> (with-current-buffer (window-buffer window) >>> (let ((args (eval (cadr (interactive-form 'Info-menu))))) >>> (with-selected-window window >>> (apply #'Info-menu args)))))) >>> >>> I would still take it with a grain of salt that it will do in all cases, you >>> should test each and every, but in majority cases it should work I think. >> >> If you prefer calling the original command from the body > > I am callling the function, not the command, on purpose. > >> then better to use 'call-interactively'. 'interactive-form' is >> more suitable for being called from the interactive spec of the wrapper. > > Sounds like a complication to me, but I am not as advanced with > elisp. Anyway according to the docs interactive-form returns what it > says, the interactive form, which as I understand, can be evaled > separately in another context to create the list of args one can just > pass to the function and not risk extra prompts. I am sure it is > possible to do it in more complicated ways then the above too. It would be inconsistent to call interactive-form in the body. Better to do this in the interactive spec: (defun info-menu-wrapper (window &rest args) (interactive (let ((window (info-window))) (with-current-buffer (window-buffer window) (cons window (eval (cadr (interactive-form 'Info-menu))))))) (with-selected-window window (apply #'Info-menu args))) > If there was an option to say to Emacs that prompts should always stay > on the frame where a command loop is initiated, then the gymnastics with > wrapping interactive-from into target buffer could be skipped, and new > commands would only need to wrap their body into with-selected-window. Such option could be added to the third argument of the interactive spec. There is a new feature added in Emacs 28 where the second argument defines to which mode the command is applicable, for example, Info-mode: (interactive "P" Info-mode) The third argument could define which window the command should select. For example: (interactive "P" Info-mode (info-window)) ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-04 17:51 ` Juri Linkov @ 2023-07-04 21:40 ` Arthur Miller 2023-07-05 6:17 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-07-04 21:40 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>>> (defun info-menu-wrapper () >>>> (interactive) >>>> (let ((window (info-window))) >>>> (with-current-buffer (window-buffer window) >>>> (let ((args (eval (cadr (interactive-form 'Info-menu))))) >>>> (with-selected-window window >>>> (apply #'Info-menu args)))))) >>>> >>>> I would still take it with a grain of salt that it will do in all cases, you >>>> should test each and every, but in majority cases it should work I think. >>> >>> If you prefer calling the original command from the body >> >> I am callling the function, not the command, on purpose. >> >>> then better to use 'call-interactively'. 'interactive-form' is >>> more suitable for being called from the interactive spec of the wrapper. >> >> Sounds like a complication to me, but I am not as advanced with >> elisp. Anyway according to the docs interactive-form returns what it >> says, the interactive form, which as I understand, can be evaled >> separately in another context to create the list of args one can just >> pass to the function and not risk extra prompts. I am sure it is >> possible to do it in more complicated ways then the above too. > > It would be inconsistent to call interactive-form in the body. Incosistent with what? > Better to do this in the interactive spec: > > (defun info-menu-wrapper (window &rest args) > (interactive > (let ((window (info-window))) > (with-current-buffer (window-buffer window) > (cons window (eval (cadr (interactive-form 'Info-menu))))))) > (with-selected-window window > (apply #'Info-menu args))) As said; there are always more complicated ways :). To start with, observe that I was not discussing how you should implement your patch. You wanted to confirm if it is enough to just wrap in with-current-buffer, so I prototyped a few lines just for the illustration of the idea. If you are going to send aguments to your wrapper, sure, I can understand you would want to do it in the interactive form, but for your example, I am not sure why are you sending the twindow and args in. I guess you are making some sore of a framework, but if you are keeping *some* sort of automated guessing framework, such as info-window than I see no point of sending in that window argument. Now, I don't know what you do, and how your framework looks like, but I would either keep exactly the same arguments as in the wrapped function, or take the opportunity and get rid of all arguments in the wrapper. I don't think you need them. That would make possible code generator easier to implement, and it would be easier to potential users write their own lambdas and so on. >> If there was an option to say to Emacs that prompts should always stay >> on the frame where a command loop is initiated, then the gymnastics with >> wrapping interactive-from into target buffer could be skipped, and new >> commands would only need to wrap their body into with-selected-window. > > Such option could be added to the third argument of the interactive spec. *Iff* such hypothetical variable existed, then it certainly should be the users choice to decide where prompts are shown, not the 3rd party code. I think the variable you pointed me too that didn't do "what I expected", could be used, perhaps with some new symbol as the value, or perhaps the nil value just does not work as advertised? Anyway, it should ceartainly be left to the user to customize where prompts are shown. Mode/library/command writers should neither have to bother with that decision or be allowed to decide for the user where prompts should display. > There is a new feature added in Emacs 28 where the second argument defines > to which mode the command is applicable, for example, Info-mode: I know. Two days ago I ported my patched info.el to separate package and to older versions. Porting it back all the way to 25 was relatively painless and trivial, minus reworked interactive spec which causes older version to omit a sea of warnings about maligned interactive spec. Seems like the the warning is not defined as a symbol, I just see byte-compile-warn-x emit it as a string, I don't see it defined as a symbol so I am not sure how to turn it off without further hacking. > (interactive "P" Info-mode) > > The third argument could define which window the command should select. > For example: > > (interactive "P" Info-mode (info-window)) I personally wouldn't do that. You only need this for commands that are already written, not for new commands. For those that already are in existence, I think you can simplify with a simple wrapper that does not take arguments as I did in the protytope and as discussed above. At least I think so; pehaps I am missing some case? There is no reason to complicate meaning of interactive spec and to hardcode what things mean in this regard, at least not just yet. Once you hardcode that meaning we will have to live with that for the rest of Emacs history, but perhaps it is not needed at all? I understand your enthusiasm, but let us live with this for a version or two until we get better understanding how this works in practice and what we need. I sincerely think you should port few relatively complex commands as I did in info.el to get understanding for this, if you didn't already. For new code, I am quite sure we can have a nice macro that takes care of the details so we don't need to generate wrappers for any new command that is yet to be written. I would also wait to introduce those wrappers into Emacs. Perhaps, there are other solutions too. Perhaps a compiler/gengerato could be written to translate old commands to more flexible implementation that can work when called from other windows too, since it is a mechanical process. Info.el and help-mode.el, as reworked manually, don't require "-other-window" wrappers; they are fully compatible with repeat mode as expected and are completely backward compatible with existing lisp code and user experience. It might be a long shot, but perhaps it is possible to write a generator that rewrites commands automatically, or better to say, I know it is possible, perhaps not in 100% cases, but for the majority of cases. The technical issue is just how to decide which commands need to be so flexible and which don't. But the real question is if it would be acceptible. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-04 21:40 ` Arthur Miller @ 2023-07-05 6:17 ` Juri Linkov 2023-07-05 14:25 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2023-07-05 6:17 UTC (permalink / raw) To: Arthur Miller; +Cc: emacs-devel > Incosistent with what? Incosistent with 'C-x M-:' (repeat-complex-command) that is used to repeat a previous command with its arguments. With this additional 'declare' form: (defun Info-menu-other-window (window &rest args) (declare (interactive-args (window (info-window)))) (interactive (let ((window (info-window))) (with-current-buffer (window-buffer window) (cons window (eval (cadr (interactive-form 'Info-menu))))))) (with-selected-window window (apply #'Info-menu args))) 'C-x M-:' will show the arguments of the previous command as (Info-menu-other-window (info-window) args...) that you can safely repeat by just typing RET. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-07-05 6:17 ` Juri Linkov @ 2023-07-05 14:25 ` Arthur Miller 0 siblings, 0 replies; 53+ messages in thread From: Arthur Miller @ 2023-07-05 14:25 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >> Incosistent with what? > > Incosistent with 'C-x M-:' (repeat-complex-command) that is used > to repeat a previous command with its arguments. With this > additional 'declare' form: > > (defun Info-menu-other-window (window &rest args) > (declare (interactive-args (window (info-window)))) > (interactive > (let ((window (info-window))) > (with-current-buffer (window-buffer window) > (cons window (eval (cadr (interactive-form 'Info-menu))))))) > (with-selected-window window > (apply #'Info-menu args))) > > 'C-x M-:' will show the arguments of the previous command as > > (Info-menu-other-window (info-window) args...) > > that you can safely repeat by just typing RET. I see. I understand what you are after to, but I think there are several problems with what you are doing there in other areas then what you gain with repeat-complex-command. Also, as a remark, I extended the "window" command in just certain cases as discussed earlier, because I perecieved Zaretski that he didn't like using a global var as I did initially in Info-jump in some mail. But you can certainly do without that window argument. You don't need to use plist hack I used. So you can totally skip those "window" gymnastics and just use &rest args. Also isn't repeat-complex-command a command too? If you are after the consistency, then you should perhaps have repeat-complex-command-other-window too? :). ^ permalink raw reply [flat|nested] 53+ messages in thread
* Getting Gnus to highlight citations in long mails (was: Info-mode patch) 2023-06-29 22:28 ` Arthur Miller 2023-06-30 7:13 ` Juri Linkov @ 2023-07-01 9:59 ` Kévin Le Gouguec 2023-07-01 12:40 ` Getting Gnus to highlight citations in long mails Arthur Miller 2023-07-02 17:56 ` Juri Linkov 1 sibling, 2 replies; 53+ messages in thread From: Kévin Le Gouguec @ 2023-07-01 9:59 UTC (permalink / raw) To: Arthur Miller; +Cc: Juri Linkov, emacs-devel Arthur Miller <arthur.miller@live.com> writes: > Juri Linkov <juri@linkov.net> writes: > >> I have read all your mails and whole patch carefully (but can't test it >> because your patch does not apply to master/emacs-29). Now it's difficult >> to read this mail because quotes are no more highlighted in Gnus. >> I don't know if this is because you mail is too long, or there is >> some bug in Gnus, but this is a minor problem. > > I also use Gnus, and it works fine for me, but the mail did got very long. I > have removed old parts in the response, and just left those of your last > question/comments on which I have answered. I have put this one on top, it was > somewhere in the middle; the rest is as they come in your last response. Does (setopt gnus-cite-parse-max-size nil) help? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Getting Gnus to highlight citations in long mails 2023-07-01 9:59 ` Getting Gnus to highlight citations in long mails (was: Info-mode patch) Kévin Le Gouguec @ 2023-07-01 12:40 ` Arthur Miller 2023-07-02 17:56 ` Juri Linkov 1 sibling, 0 replies; 53+ messages in thread From: Arthur Miller @ 2023-07-01 12:40 UTC (permalink / raw) To: Kévin Le Gouguec; +Cc: Juri Linkov, emacs-devel Kévin Le Gouguec <kevin.legouguec@gmail.com> writes: > Arthur Miller <arthur.miller@live.com> writes: > >> Juri Linkov <juri@linkov.net> writes: >> >>> I have read all your mails and whole patch carefully (but can't test it >>> because your patch does not apply to master/emacs-29). Now it's difficult >>> to read this mail because quotes are no more highlighted in Gnus. >>> I don't know if this is because you mail is too long, or there is >>> some bug in Gnus, but this is a minor problem. >> >> I also use Gnus, and it works fine for me, but the mail did got very long. I >> have removed old parts in the response, and just left those of your last >> question/comments on which I have answered. I have put this one on top, it was >> somewhere in the middle; the rest is as they come in your last response. > > Does (setopt gnus-cite-parse-max-size nil) help? For me this option says 25000; I don't think I have changed that one from the defaults, but for me font-lock worked fine. I am using a relatively old laptop with very weak cpu, not so much RAM and windows on it, and it worked fine, but thanks for the tip anyway. It might be useful in the future. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Getting Gnus to highlight citations in long mails 2023-07-01 9:59 ` Getting Gnus to highlight citations in long mails (was: Info-mode patch) Kévin Le Gouguec 2023-07-01 12:40 ` Getting Gnus to highlight citations in long mails Arthur Miller @ 2023-07-02 17:56 ` Juri Linkov 1 sibling, 0 replies; 53+ messages in thread From: Juri Linkov @ 2023-07-02 17:56 UTC (permalink / raw) To: Kévin Le Gouguec; +Cc: Arthur Miller, emacs-devel >>> I have read all your mails and whole patch carefully (but can't test it >>> because your patch does not apply to master/emacs-29). Now it's difficult >>> to read this mail because quotes are no more highlighted in Gnus. >>> I don't know if this is because you mail is too long, or there is >>> some bug in Gnus, but this is a minor problem. >> >> I also use Gnus, and it works fine for me, but the mail did got very long. I >> have removed old parts in the response, and just left those of your last >> question/comments on which I have answered. I have put this one on top, it was >> somewhere in the middle; the rest is as they come in your last response. > > Does (setopt gnus-cite-parse-max-size nil) help? Thanks, after customizing it to nil, all messages are fontified now. No idea why it has so small value by default. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-26 17:56 ` Juri Linkov 2023-06-26 20:17 ` Arthur Miller @ 2023-06-27 11:45 ` Eli Zaretskii 2023-06-27 12:15 ` Arthur Miller 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2023-06-27 11:45 UTC (permalink / raw) To: Juri Linkov; +Cc: arthur.miller, emacs-devel > From: Juri Linkov <juri@linkov.net> > Cc: emacs-devel@gnu.org > Date: Mon, 26 Jun 2023 20:56:19 +0300 > > > -(defun Info-directory () > > +(defun Info-directory (&optional window) > > "Go to the Info directory node." > > (interactive) > > - (Info-find-node "dir" "top")) > > + (with-selected-window (or window (info-window)) > > + (Info-find-node "dir" "top"))) > > I wonder why you modified a lot of commands instead of implementing > a non-intrusive approach of creating standard wrapper commands like > > (defun Info-directory-other-window (&optional window) > "Go to the Info directory node in another window." > (interactive) > (with-selected-window (or window (info-window)) > (Info-directory))) Someone, I think it was Stefan, suggested to use a kind of "universal window argument", similar to "C-x RET c" (which is bound to universal-coding-system-argument, and allows to specify a coding-system for the next command. Wouldn't that approach resulted in a cleaner, more elegant solution? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-27 11:45 ` Info-mode patch Eli Zaretskii @ 2023-06-27 12:15 ` Arthur Miller 2023-06-27 12:42 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-06-27 12:15 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Juri Linkov, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Juri Linkov <juri@linkov.net> >> Cc: emacs-devel@gnu.org >> Date: Mon, 26 Jun 2023 20:56:19 +0300 >> >> > -(defun Info-directory () >> > +(defun Info-directory (&optional window) >> > "Go to the Info directory node." >> > (interactive) >> > - (Info-find-node "dir" "top")) >> > + (with-selected-window (or window (info-window)) >> > + (Info-find-node "dir" "top"))) >> >> I wonder why you modified a lot of commands instead of implementing >> a non-intrusive approach of creating standard wrapper commands like >> >> (defun Info-directory-other-window (&optional window) >> "Go to the Info directory node in another window." >> (interactive) >> (with-selected-window (or window (info-window)) >> (Info-directory))) > > Someone, I think it was Stefan, suggested to use a kind of "universal > window argument", similar to "C-x RET c" (which is bound to > universal-coding-system-argument, and allows to specify a > coding-system for the next command. Wouldn't that approach resulted > in a cleaner, more elegant solution? In my personal opinion, after wrestling with this, I don't think there is one single "unisex" modell for all. As I see Lisp functions, especially interactive commands in Emacs, they are more akin to applications, or small shell scripts, that can basically do whatever they want. Some of them work on buffers, some on windows, some can just download stuff from the web. With other words, they should be looked at individually what they do, on case by case basis. It can also happen that wrapping ends up in as much work, just different in the long run. One has to create wrappers, in some cases not so trivial, otherwise user will be thrown between minibuffers on different frames, which I don't think would be pretty from the user perspective, then there is the manual and docs everything etc. It can happen that it is not much less work that has to be done, but just different work, and it can also happen that it ends up in glitches on some window managers as described in the original mail and response to Juri. It is definitely possible to implement this in other ways too. As a wise man said to me: it is all software, everything is possible, it is just the work that is needed. By the way, I could possible remove that optional "window" argument in those few functions that introduce it and use plist as I did in some other place; it was just a convenience. In that case no signatures are changed at all; no need to change the manual, possibly it is enough to just mention in News that info/help commands are callable from other buffers. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-27 12:15 ` Arthur Miller @ 2023-06-27 12:42 ` Eli Zaretskii 2023-06-27 15:28 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2023-06-27 12:42 UTC (permalink / raw) To: Arthur Miller; +Cc: juri, emacs-devel > From: Arthur Miller <arthur.miller@live.com> > Cc: Juri Linkov <juri@linkov.net>, emacs-devel@gnu.org > Date: Tue, 27 Jun 2023 14:15:37 +0200 > > Eli Zaretskii <eliz@gnu.org> writes: > > >> From: Juri Linkov <juri@linkov.net> > >> Cc: emacs-devel@gnu.org > >> Date: Mon, 26 Jun 2023 20:56:19 +0300 > >> > >> > -(defun Info-directory () > >> > +(defun Info-directory (&optional window) > >> > "Go to the Info directory node." > >> > (interactive) > >> > - (Info-find-node "dir" "top")) > >> > + (with-selected-window (or window (info-window)) > >> > + (Info-find-node "dir" "top"))) > >> > >> I wonder why you modified a lot of commands instead of implementing > >> a non-intrusive approach of creating standard wrapper commands like > >> > >> (defun Info-directory-other-window (&optional window) > >> "Go to the Info directory node in another window." > >> (interactive) > >> (with-selected-window (or window (info-window)) > >> (Info-directory))) > > > > Someone, I think it was Stefan, suggested to use a kind of "universal > > window argument", similar to "C-x RET c" (which is bound to > > universal-coding-system-argument, and allows to specify a > > coding-system for the next command. Wouldn't that approach resulted > > in a cleaner, more elegant solution? > > In my personal opinion, after wrestling with this, I don't think there is one > single "unisex" modell for all. As I see Lisp functions, especially interactive > commands in Emacs, they are more akin to applications, or small shell scripts, > that can basically do whatever they want. Some of them work on buffers, some on > windows, some can just download stuff from the web. With other words, they > should be looked at individually what they do, on case by case basis. > > It can also happen that wrapping ends up in as much work, just different in the > long run. One has to create wrappers, in some cases not so trivial, otherwise > user will be thrown between minibuffers on different frames, which I don't think > would be pretty from the user perspective, then there is the manual and docs > everything etc. It can happen that it is not much less work that has to be done, > but just different work, and it can also happen that it ends up in glitches on > some window managers as described in the original mail and response to Juri. I think you are trying to solve a much wider, more general, and more hard problem than you need to. Remember, this started as a feature to allow the user invoke help-mode and Info-mode commands from a window other than the one showing (help* or *info* buffers. You are describing issues far away from that. > By the way, I could possible remove that optional "window" argument in those few > functions that introduce it and use plist as I did in some other place; it was > just a convenience. In that case no signatures are changed at all; no need to > change the manual, possibly it is enough to just mention in News that info/help > commands are callable from other buffers. The "universal-window-argument" suggestion is precisely so you could pass such a "window argument" without actually touching the signature of the functions. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-27 12:42 ` Eli Zaretskii @ 2023-06-27 15:28 ` Arthur Miller 2023-06-27 16:03 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2023-06-27 15:28 UTC (permalink / raw) To: Eli Zaretskii; +Cc: juri, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Arthur Miller <arthur.miller@live.com> >> Cc: Juri Linkov <juri@linkov.net>, emacs-devel@gnu.org >> Date: Tue, 27 Jun 2023 14:15:37 +0200 >> >> Eli Zaretskii <eliz@gnu.org> writes: >> >> >> From: Juri Linkov <juri@linkov.net> >> >> Cc: emacs-devel@gnu.org >> >> Date: Mon, 26 Jun 2023 20:56:19 +0300 >> >> >> >> > -(defun Info-directory () >> >> > +(defun Info-directory (&optional window) >> >> > "Go to the Info directory node." >> >> > (interactive) >> >> > - (Info-find-node "dir" "top")) >> >> > + (with-selected-window (or window (info-window)) >> >> > + (Info-find-node "dir" "top"))) >> >> >> >> I wonder why you modified a lot of commands instead of implementing >> >> a non-intrusive approach of creating standard wrapper commands like >> >> >> >> (defun Info-directory-other-window (&optional window) >> >> "Go to the Info directory node in another window." >> >> (interactive) >> >> (with-selected-window (or window (info-window)) >> >> (Info-directory))) >> > >> > Someone, I think it was Stefan, suggested to use a kind of "universal >> > window argument", similar to "C-x RET c" (which is bound to >> > universal-coding-system-argument, and allows to specify a >> > coding-system for the next command. Wouldn't that approach resulted >> > in a cleaner, more elegant solution? >> >> In my personal opinion, after wrestling with this, I don't think there is one >> single "unisex" modell for all. As I see Lisp functions, especially interactive >> commands in Emacs, they are more akin to applications, or small shell scripts, >> that can basically do whatever they want. Some of them work on buffers, some on >> windows, some can just download stuff from the web. With other words, they >> should be looked at individually what they do, on case by case basis. >> >> It can also happen that wrapping ends up in as much work, just different in the >> long run. One has to create wrappers, in some cases not so trivial, otherwise >> user will be thrown between minibuffers on different frames, which I don't think >> would be pretty from the user perspective, then there is the manual and docs >> everything etc. It can happen that it is not much less work that has to be done, >> but just different work, and it can also happen that it ends up in glitches on >> some window managers as described in the original mail and response to Juri. > > I think you are trying to solve a much wider, more general, and more > hard problem than you need to. Remember, this started as a feature to Not really. It might appear so, because I talk about general problems with what a command can do, but on contrary, I said in the mail with patch that it is possible to achive this without altering Emacs, with the tools already in. We just need to pay attention how we write commands. See below. > allow the user invoke help-mode and Info-mode commands from a window > other than the one showing (help* or *info* buffers. You are > describing issues far away from that. What I am trying to say is that command has to be written with the assumptions that user can call it from any window and Emacs frame, and that all prompting should be done in the frame at which user types, so I have reworked info commands to work with those assumptions. Making a wrapper just to put a call to a command into with-selected-window, instead of wrapping the body of that command with the same macro, is not so much better; just different. Sure "less intrusive" on a command itself, but there is a new command and then we have two where one can do the jobb. >> By the way, I could possible remove that optional "window" argument in those few >> functions that introduce it and use plist as I did in some other place; it was >> just a convenience. In that case no signatures are changed at all; no need to >> change the manual, possibly it is enough to just mention in News that info/help >> commands are callable from other buffers. > > The "universal-window-argument" suggestion is precisely so you could > pass such a "window argument" without actually touching the signature > of the functions. Ok. I am not familiar with universal-window-agument suggestion, but sounds to me like also an attempt to offer you a general solution. In that case you will have to alter other parts of Emacs instead of info and help only. I have just rewrote Info functions to work without need to alter anything else in Emacs. Anyway, if the patch is unnaceptable because of most of info commands being reworked, then I assume it is not interested to continue with this, so I'll thank for the valuable input and insights and wait with the excitement for the solution. Best regards. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-27 15:28 ` Arthur Miller @ 2023-06-27 16:03 ` Eli Zaretskii 2023-06-27 16:33 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2023-06-27 16:03 UTC (permalink / raw) To: Arthur Miller; +Cc: juri, emacs-devel > From: Arthur Miller <arthur.miller@live.com> > Cc: juri@linkov.net, emacs-devel@gnu.org > Date: Tue, 27 Jun 2023 17:28:22 +0200 > > Anyway, if the patch is unnaceptable because of most of info commands being > reworked, then I assume it is not interested to continue with this, so I'll > thank for the valuable input and insights and wait with the excitement for the > solution. FWIW, I'm very interested in being able to invoke Help and Info commands without having to switch to a window that shows *Help* or *info* buffers, but I hoped the solution would be simpler, both in use and in the implementation. Thanks for working on this. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Info-mode patch 2023-06-27 16:03 ` Eli Zaretskii @ 2023-06-27 16:33 ` Arthur Miller 0 siblings, 0 replies; 53+ messages in thread From: Arthur Miller @ 2023-06-27 16:33 UTC (permalink / raw) To: Eli Zaretskii; +Cc: juri, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Arthur Miller <arthur.miller@live.com> >> Cc: juri@linkov.net, emacs-devel@gnu.org >> Date: Tue, 27 Jun 2023 17:28:22 +0200 >> >> Anyway, if the patch is unnaceptable because of most of info commands being >> reworked, then I assume it is not interested to continue with this, so I'll >> thank for the valuable input and insights and wait with the excitement for the >> solution. > > FWIW, I'm very interested in being able to invoke Help and Info > commands without having to switch to a window that shows *Help* or > *info* buffers, but I hoped the solution would be simpler, both in use > and in the implementation. The solution is conceptually simple, just not trivially simple :). We have to take care of details with prompting which come into play when multiple frames are involved. The patch is big, but there are lots of commands in Info mode. So is the usage. The point of the patch is to make things simple to use. I don't want extra prompting, and I don't want to type C-1, C-2, C-u or C-something before each and every Info or help command from other window neither, and I don't want to have to move my mouse cursor manually because focus is messed up by my window manager. If M-i is my prefix key for Info-mode-map, With the patch you can either use C-1 M-i + key for command if you want to do something in say *info*<1> buffer, and you should not be promoted or asked anything, it should just work. Or you could (as alternative) type C-u C-u M-i + command key once, and then in all subsequent uses, you can just type M-i + command key. I am not sure how that window prefix will work, but I guess you will have to type it before each command or you will probably also find some scheme to tell Emacs to favour some of the Info buffers. > Thanks for working on this. NP. I obviously can't help with something I am not familiar with, like that window prefix thing; but at least you have got input about problems involved. ^ permalink raw reply [flat|nested] 53+ messages in thread
end of thread, other threads:[~2023-07-08 8:14 UTC | newest] Thread overview: 53+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-06-26 16:09 Info-mode patch Arthur Miller 2023-06-26 17:56 ` Juri Linkov 2023-06-26 20:17 ` Arthur Miller 2023-06-27 6:32 ` Juri Linkov 2023-06-27 7:54 ` Arthur Miller 2023-06-27 18:11 ` Juri Linkov 2023-06-27 23:09 ` Arthur Miller 2023-06-28 6:50 ` Juri Linkov 2023-06-28 21:52 ` Arthur Miller 2023-06-29 6:44 ` Juri Linkov 2023-06-29 12:42 ` Arthur Miller 2023-06-29 15:00 ` [External] : " Drew Adams 2023-06-29 16:24 ` Arthur Miller 2023-06-29 17:44 ` Juri Linkov 2023-06-29 22:28 ` Arthur Miller 2023-06-30 7:13 ` Juri Linkov 2023-06-30 8:41 ` Arthur Miller 2023-06-30 17:57 ` Juri Linkov 2023-07-01 9:11 ` Arthur Miller 2023-07-02 17:53 ` Juri Linkov 2023-07-02 18:39 ` Eli Zaretskii 2023-07-02 22:43 ` Arthur Miller 2023-07-03 11:46 ` Eli Zaretskii 2023-07-03 12:57 ` Arthur Miller 2023-07-03 13:17 ` Eli Zaretskii 2023-07-03 18:40 ` Juri Linkov 2023-07-03 18:57 ` Eli Zaretskii 2023-07-04 6:50 ` easy-menu-define keys for key-valid-p (was: Info-mode patch) Juri Linkov 2023-07-04 11:33 ` Eli Zaretskii 2023-07-03 21:07 ` Info-mode patch Arthur Miller 2023-07-04 7:59 ` Andreas Schwab 2023-07-04 8:44 ` Arthur Miller 2023-07-03 17:07 ` Eli Zaretskii 2023-07-04 23:58 ` Stefan Monnier 2023-07-08 8:14 ` Eli Zaretskii 2023-07-02 22:05 ` Arthur Miller 2023-07-03 18:45 ` Juri Linkov 2023-07-03 22:24 ` Arthur Miller 2023-07-04 6:54 ` Juri Linkov 2023-07-04 9:43 ` Arthur Miller 2023-07-04 17:51 ` Juri Linkov 2023-07-04 21:40 ` Arthur Miller 2023-07-05 6:17 ` Juri Linkov 2023-07-05 14:25 ` Arthur Miller 2023-07-01 9:59 ` Getting Gnus to highlight citations in long mails (was: Info-mode patch) Kévin Le Gouguec 2023-07-01 12:40 ` Getting Gnus to highlight citations in long mails Arthur Miller 2023-07-02 17:56 ` Juri Linkov 2023-06-27 11:45 ` Info-mode patch Eli Zaretskii 2023-06-27 12:15 ` Arthur Miller 2023-06-27 12:42 ` Eli Zaretskii 2023-06-27 15:28 ` Arthur Miller 2023-06-27 16:03 ` Eli Zaretskii 2023-06-27 16:33 ` Arthur Miller
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).