diff options
Diffstat (limited to 'lisp/info.el')
-rw-r--r-- | lisp/info.el | 235 |
1 files changed, 148 insertions, 87 deletions
diff --git a/lisp/info.el b/lisp/info.el index 7f169f4b556..e6b5f3e5a7c 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -1,4 +1,4 @@ -;; info.el --- Info package for Emacs -*- lexical-binding:t -*- +;;; info.el --- Info package for Emacs -*- lexical-binding:t -*- ;; Copyright (C) 1985-1986, 1992-2021 Free Software Foundation, Inc. @@ -391,6 +391,14 @@ where SUPPORTS-INDEX-COOKIES can be either t or nil.") (defvar-local Info-index-alternatives nil "List of possible matches for last `Info-index' command.") +(defvar-local Info--current-index-alternative 0 + "Current displayed index alternative.") + +(defcustom Info-warn-on-index-alternatives-wrap t + "Warn when wrapping to the beginning/end when displaying index alternatives." + :type 'boolean + :version "28.1") + (defvar Info-point-loc nil "Point location within a selected node. If string, the point is moved to the proper occurrence of the @@ -916,7 +924,8 @@ find a node." (when (and (not no-pop-to-dir) (not Info-current-file)) (Info-directory)) - (user-error "Info file %s does not exist" filename))) + (user-error "Info file `%s' does not exist; consider installing it" + filename))) filename)))) (defun Info-find-node (filename nodename &optional no-going-back strict-case) @@ -1723,14 +1732,14 @@ escaped (\\\",\\\\)." (concat " (" (if (stringp Info-current-file) - (replace-regexp-in-string + (string-replace "%" "%%" (file-name-sans-extension (file-name-nondirectory Info-current-file))) (format "*%S*" Info-current-file)) ") " (if Info-current-node - (propertize (replace-regexp-in-string + (propertize (string-replace "%" "%%" Info-current-node) 'face 'mode-line-buffer-id 'help-echo @@ -1854,7 +1863,8 @@ See `completing-read' for a description of arguments and usage." (lambda (string pred action) (complete-with-action action - (Info-build-node-completions (Info-find-file file1 nil t)) + (when-let ((file2 (Info-find-file file1 'noerror t))) + (Info-build-node-completions file2)) string pred)) nodename predicate code)))) ;; Otherwise use Info-read-node-completion-table. @@ -1880,10 +1890,17 @@ the Top node in FILENAME." (or (cdr (assoc filename Info-file-completions)) (with-temp-buffer (Info-mode) - (Info-goto-node (format "(%s)Top" filename)) - (Info-build-node-completions-1) - (push (cons filename Info-current-file-completions) Info-file-completions) - Info-current-file-completions)) + (condition-case nil + (Info-goto-node (format "(%s)Top" filename)) + ;; `Info-goto-node' signals a `user-error' when there + ;; are no nodes in the file in question (for instance, + ;; if it's not actually an Info file). + (user-error nil) + (:success + (Info-build-node-completions-1) + (push (cons filename Info-current-file-completions) + Info-file-completions) + Info-current-file-completions)))) (or Info-current-file-completions (Info-build-node-completions-1)))) @@ -1972,7 +1989,8 @@ If DIRECTION is `backward', search in the reverse direction." (format-prompt "Regexp search%s" (car Info-search-history) (if case-fold-search "" " case-sensitively")) - nil 'Info-search-history))) + nil 'Info-search-history)) + Info-mode) (when (equal regexp "") (setq regexp (car Info-search-history))) (when regexp @@ -2080,13 +2098,13 @@ If DIRECTION is `backward', search in the reverse direction." (defun Info-search-case-sensitively () "Search for a regexp case-sensitively." - (interactive) + (interactive nil Info-mode) (let ((case-fold-search nil)) (call-interactively 'Info-search))) (defun Info-search-next () "Search for next regexp from a previous `Info-search' command." - (interactive) + (interactive nil Info-mode) (let ((case-fold-search Info-search-case-fold)) (if Info-search-history (Info-search (car Info-search-history)) @@ -2098,7 +2116,8 @@ If DIRECTION is `backward', search in the reverse direction." (format-prompt "Regexp search%s backward" (car Info-search-history) (if case-fold-search "" " case-sensitively")) - nil 'Info-search-history))) + nil 'Info-search-history)) + Info-mode) (Info-search regexp bound noerror count 'backward)) (defun Info-isearch-search () @@ -2137,8 +2156,10 @@ If DIRECTION is `backward', search in the reverse direction." (goto-char (if isearch-forward (point-min) (point-max))))) (defun Info-isearch-push-state () - `(lambda (cmd) - (Info-isearch-pop-state cmd ',Info-current-file ',Info-current-node))) + (let ((file Info-current-file) + (node Info-current-node)) + (lambda (cmd) + (Info-isearch-pop-state cmd file node)))) (defun Info-isearch-pop-state (_cmd file node) (or (and (equal Info-current-file file) @@ -2235,7 +2256,7 @@ End of submatch 0, 1, and 3 are the same, so you can safely concat." (defun Info-next () "Go to the \"next\" node, staying on the same hierarchical level. This command doesn't descend into sub-nodes, like \\<Info-mode-map>\\[Info-forward-node] does." - (interactive) + (interactive nil Info-mode) ;; In case another window is currently selected (save-window-excursion (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) @@ -2244,7 +2265,7 @@ This command doesn't descend into sub-nodes, like \\<Info-mode-map>\\[Info-forwa (defun Info-prev () "Go to the \"previous\" node, staying on the same hierarchical level. This command doesn't go up to the parent node, like \\<Info-mode-map>\\[Info-backward-node] does." - (interactive) + (interactive nil Info-mode) ;; In case another window is currently selected (save-window-excursion (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) @@ -2253,7 +2274,7 @@ This command doesn't go up to the parent node, like \\<Info-mode-map>\\[Info-bac (defun Info-up (&optional same-file) "Go to the superior node of this node. If SAME-FILE is non-nil, do not move to a different Info file." - (interactive) + (interactive nil Info-mode) ;; In case another window is currently selected (save-window-excursion (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) @@ -2284,7 +2305,7 @@ If SAME-FILE is non-nil, do not move to a different Info file." (defun Info-history-back () "Go back in the history to the last node visited." - (interactive) + (interactive nil Info-mode) (or Info-history (user-error "This is the first Info node you looked at")) (let ((history-forward @@ -2304,7 +2325,7 @@ If SAME-FILE is non-nil, do not move to a different Info file." (defun Info-history-forward () "Go forward in the history of visited nodes." - (interactive) + (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)) @@ -2378,7 +2399,7 @@ If SAME-FILE is non-nil, do not move to a different Info file." (defun Info-history () "Go to a node with a menu of visited nodes." - (interactive) + (interactive nil Info-mode) (Info-find-node "*History*" "Top") (Info-next-reference) (Info-next-reference)) @@ -2415,7 +2436,7 @@ If SAME-FILE is non-nil, do not move to a different Info file." (defun 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) + (interactive nil Info-mode) (Info-find-node Info-current-file "*TOC*") (let ((prev-node (nth 1 (car Info-history))) p) (goto-char (point-min)) @@ -2462,7 +2483,7 @@ Table of contents is created from the tree structure of menus." (match-string-no-properties 1))) (section "Top") menu-items) - (when (and upnode (string-match "(" upnode)) (setq upnode nil)) + (when (and upnode (string-search "(" upnode)) (setq upnode nil)) (when (and (not (Info-index-node nodename file)) (re-search-forward "^\\* Menu:" bound t)) (forward-line 1) @@ -2587,14 +2608,15 @@ new buffer." (list (if (equal input "") default input) current-prefix-arg)) - (user-error "No cross-references in this node")))) + (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-match " " str i)) + (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 @@ -2789,7 +2811,8 @@ new buffer." (completing-read (format-prompt "Menu item" default) #'Info-complete-menu-item nil t nil nil default)))) - (list item current-prefix-arg)))) + (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) @@ -2833,19 +2856,19 @@ new buffer." (defun Info-nth-menu-item () "Go to the node of the Nth menu item. N is the digit argument used to invoke this command." - (interactive) + (interactive nil Info-mode) (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) + (interactive nil Info-mode) (Info-goto-node "Top")) (defun Info-final-node () "Go to the final node in this file." - (interactive) + (interactive nil Info-mode) (Info-goto-node "Top") (let ((Info-history nil) (case-fold-search t)) @@ -2869,7 +2892,7 @@ to the parent node. When called from Lisp, NOT-DOWN non-nil means don't descend into sub-nodes, 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) + (interactive nil Info-mode) (goto-char (point-min)) (forward-line 1) (let ((case-fold-search t)) @@ -2906,11 +2929,11 @@ don't signal a user-error if there's no node to go to." "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) + (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-match "(" upnode)) + (cond ((and upnode (string-search "(" upnode)) (user-error "First node in file")) ((and upnode (or (null prevnode) ;; Use string-equal, not equal, @@ -2935,7 +2958,7 @@ last sub-node, if any; otherwise go \"up\" to the parent node." (defun Info-next-menu-item () "Go to the node of the next menu item." - (interactive) + (interactive nil Info-mode) ;; Bind this in case the user sets it to nil. (let* ((case-fold-search t) (node @@ -2949,7 +2972,7 @@ last sub-node, if any; otherwise go \"up\" to the parent node." (defun Info-last-menu-item () "Go to the node of the previous menu item." - (interactive) + (interactive nil Info-mode) (save-excursion (forward-line 1) ;; Bind this in case the user sets it to nil. @@ -2968,7 +2991,7 @@ last sub-node, if any; otherwise go \"up\" to the parent node." (defun Info-next-preorder () "Go to the next subnode or the next node, or go up a level." - (interactive) + (interactive nil Info-mode) (cond ((Info-no-error (Info-next-menu-item))) ((Info-no-error (Info-next))) ((Info-no-error (Info-up t)) @@ -2987,7 +3010,7 @@ last sub-node, if any; otherwise go \"up\" to the parent node." (defun Info-last-preorder () "Go to the last node, popping up a level if there is none." - (interactive) + (interactive nil Info-mode) (cond ((and Info-scroll-prefer-subnodes (Info-no-error (Info-last-menu-item) @@ -3039,7 +3062,7 @@ 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) + (interactive nil Info-mode) (if (or (< (window-start) (point-min)) (> (window-start) (point-max))) (set-window-start (selected-window) (point))) @@ -3061,7 +3084,7 @@ in other ways.)" (defun Info-mouse-scroll-up (e) "Scroll one screenful forward in Info, using the mouse. See `Info-scroll-up'." - (interactive "e") + (interactive "e" Info-mode) (save-selected-window (if (eventp e) (select-window (posn-window (event-start e)))) @@ -3073,7 +3096,7 @@ If point is within the menu of a node, and `Info-scroll-prefer-subnodes' is non-nil, this goes to its last subnode. When you scroll past the beginning of a node, that goes to the previous node or back up to the parent node." - (interactive) + (interactive nil Info-mode) (if (or (< (window-start) (point-min)) (> (window-start) (point-max))) (set-window-start (selected-window) (point))) @@ -3093,7 +3116,7 @@ parent node." (defun Info-mouse-scroll-down (e) "Scroll one screenful backward in Info, using the mouse. See `Info-scroll-down'." - (interactive "e") + (interactive "e" Info-mode) (save-selected-window (if (eventp e) (select-window (posn-window (event-start e)))) @@ -3139,7 +3162,7 @@ Return the new position of point, or nil." "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") + (interactive "i\np" Info-mode) (unless count (setq count 1)) (if (< count 0) @@ -3167,7 +3190,7 @@ COUNT cross-references." "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") + (interactive "i\np" Info-mode) (unless count (setq count 1)) (if (< count 0) @@ -3360,39 +3383,56 @@ Give an empty topic name to go to the Index node itself." (setq exact (cons found exact) matches (delq found matches))) (setq Info-history-list ohist-list) - (setq Info-index-alternatives (nconc exact (nreverse matches))) + (setq Info-index-alternatives (nconc exact (nreverse matches)) + Info--current-index-alternative 0) (Info-index-next 0))))) (defun Info-index-next (num) - "Go to the next matching index item from the last \\<Info-mode-map>\\[Info-index] command." - (interactive "p") - (or Info-index-alternatives - (user-error "No previous `i' command")) - (while (< num 0) - (setq num (+ num (length Info-index-alternatives)))) - (while (> num 0) - (setq Info-index-alternatives - (nconc (cdr Info-index-alternatives) - (list (car Info-index-alternatives))) - num (1- num))) - (Info-goto-node (nth 1 (car Info-index-alternatives))) - (if (> (nth 3 (car Info-index-alternatives)) 0) - ;; Forward 2 lines less because `Info-find-node-2' initially - ;; puts point to the 2nd line. - (forward-line (- (nth 3 (car Info-index-alternatives)) 2)) - (forward-line 3) ; don't search in headers - (let ((name (car (car Info-index-alternatives)))) - (Info-find-index-name name))) - (message "Found `%s' in %s. %s" - (car (car Info-index-alternatives)) - (nth 2 (car Info-index-alternatives)) - (if (cdr Info-index-alternatives) - (format-message - "(%s total; use `%s' for next)" - (length Info-index-alternatives) - (key-description (where-is-internal - 'Info-index-next overriding-local-map t))) - "(Only match)"))) + "Go to the next matching index item from the last \\<Info-mode-map>\\[Info-index] command. +If given a numeric prefix, skip that many index items forward (or +backward). + +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)")))))) (defun Info-find-index-name (name) "Move point to the place within the current node where NAME is defined." @@ -3487,7 +3527,8 @@ search results." (with-current-buffer Info-complete-menu-buffer (Info-goto-index) (completing-read "Index topic: " #'Info-complete-menu-item)) - (kill-buffer Info-complete-menu-buffer))))) + (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) @@ -3737,7 +3778,7 @@ Build a menu of the possible matches." "The following packages match the keyword ‘" nodename "’:\n\n") (insert "* Menu:\n\n") (let ((keywords - (mapcar #'intern (if (string-match-p "," nodename) + (mapcar #'intern (if (string-search "," nodename) (split-string nodename ",[ \t\n]*" t) (list nodename)))) hits desc) @@ -3793,7 +3834,7 @@ with a list of packages that contain all specified keywords." (defun Info-undefined () "Make command be undefined in Info." - (interactive) + (interactive nil Info-mode) (ding)) (defun Info-help () @@ -3870,7 +3911,7 @@ ERRORSTRING optional fourth argument, controls action on no match: "\\<Info-mode-map>Follow a node reference near point. Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where you click. At end of the node's text, moves to the next node, or up if none." - (interactive "e") + (interactive "e" Info-mode) (mouse-set-point click) (and (not (Info-follow-nearest-node)) (save-excursion (forward-line 1) (eobp)) @@ -3884,7 +3925,7 @@ if point is in a menu item description, follow that menu item. If FORK is non-nil (interactively with a prefix arg), show the node in a new Info buffer. If FORK is a string, it is the name to use for the new buffer." - (interactive "P") + (interactive "P" Info-mode) (or (Info-try-follow-nearest-node fork) (when (save-excursion (search-backward "\n* menu:" nil t)) @@ -3954,7 +3995,7 @@ If FORK is non-nil, it is passed to `Info-goto-node'." (defun Info-mouse-follow-link (click) "Follow a link where you click." - (interactive "@e") + (interactive "@e" Info-mode) (let* ((position (event-start click)) (posn-string (and position (posn-string position))) (link-args (if posn-string @@ -4076,9 +4117,9 @@ If FORK is non-nil, it is passed to `Info-goto-node'." :help "Search for another occurrence of regular expression"] "---" ("History" - ["Back in history" Info-history-back :active Info-history + ["Back in History" Info-history-back :active Info-history :help "Go back in history to the last node you were at"] - ["Forward in history" Info-history-forward :active Info-history-forward + ["Forward in History" Info-history-forward :active Info-history-forward :help "Go forward in history"] ["Show History" Info-history :active Info-history-list :help "Go to menu of visited nodes"]) @@ -4105,6 +4146,25 @@ If FORK is non-nil, it is passed to `Info-goto-node'." "---" ["Exit" quit-window :help "Stop reading Info"])) +(defun Info-context-menu (menu) + (define-key menu [Info-separator] menu-bar-separator) + (let ((easy-menu (make-sparse-keymap "Info"))) + (easy-menu-define nil easy-menu nil + '("Info" + ["Back in History" Info-history-back :visible Info-history + :help "Go back in history to the last node you were at"] + ["Forward in History" Info-history-forward :visible Info-history-forward + :help "Go forward in history"])) + (dolist (item (reverse (lookup-key easy-menu [menu-bar info]))) + (when (consp item) + (define-key menu (vector (car item)) (cdr item))))) + + (when (mouse-posn-property (event-start last-input-event) 'mouse-face) + (define-key menu [Info-mouse-follow-nearest-node] + '(menu-item "Follow Link" Info-mouse-follow-nearest-node + :help "Follow a link where you click"))) + + menu) (defvar info-tool-bar-map (let ((map (make-sparse-keymap))) @@ -4158,12 +4218,12 @@ If FORK is non-nil, it is passed to `Info-goto-node'." (defun Info-history-back-menu (e) "Pop up the menu with a list of previously visited Info nodes." - (interactive "e") + (interactive "e" Info-mode) (Info-history-menu e "Back in history" Info-history 'Info-history-back)) (defun Info-history-forward-menu (e) "Pop up the menu with a list of Info nodes visited with ‘Info-history-back’." - (interactive "e") + (interactive "e" Info-mode) (Info-history-menu e "Forward in history" Info-history-forward 'Info-history-forward)) (defvar Info-menu-last-node nil) @@ -4237,7 +4297,7 @@ If FORK is non-nil, it is passed to `Info-goto-node'." "Put the name of the current Info node into the kill ring. 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") + (interactive "P" Info-mode) (unless Info-current-node (user-error "No current Info node")) (let ((node (if (stringp Info-current-file) @@ -4405,6 +4465,7 @@ Advanced commands: (add-hook 'clone-buffer-hook 'Info-clone-buffer nil t) (add-hook 'change-major-mode-hook 'font-lock-defontify nil t) (add-hook 'isearch-mode-hook 'Info-isearch-start nil t) + (add-hook 'context-menu-functions 'Info-context-menu 5 t) (when Info-standalone (add-hook 'quit-window-hook 'save-buffers-kill-emacs nil t)) (setq-local isearch-search-fun-function #'Info-isearch-search) @@ -4792,10 +4853,10 @@ first line or header line, and for breadcrumb links.") (skip-syntax-backward " (")) (setq other-tag (cond ((save-match-data (looking-back "\\(^\\| \\)see" - (- (point) 3))) + (- (point) 4))) "") ((save-match-data (looking-back "\\(^\\| \\)in" - (- (point) 2))) + (- (point) 3))) "") ((memq (char-before) '(nil ?\. ?! ??)) "See ") @@ -5203,7 +5264,7 @@ The INDENT level is ignored." TEXT is the text of the button we clicked on, a + or - item. TOKEN is data related to this node (NAME . FILE). INDENT is the current indentation depth." - (cond ((string-match "\\+" text) ;we have to expand this file + (cond ((string-search "+" text) ;we have to expand this file (speedbar-change-expand-button-char ?-) (if (speedbar-with-writable (save-excursion @@ -5211,7 +5272,7 @@ INDENT is the current indentation depth." (Info-speedbar-hierarchy-buttons nil (1+ indent) token))) (speedbar-change-expand-button-char ?-) (speedbar-change-expand-button-char ??))) - ((string-match "-" text) ;we have to contract this node + ((string-search "-" text) ;we have to contract this node (speedbar-change-expand-button-char ?+) (speedbar-delete-subblock indent)) (t (error "Ooops... not sure what to do"))) |