diff options
Diffstat (limited to 'lisp/info.el')
-rw-r--r-- | lisp/info.el | 446 |
1 files changed, 316 insertions, 130 deletions
diff --git a/lisp/info.el b/lisp/info.el index 96c22e15110..58ebf0b17da 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -59,15 +59,6 @@ to the user." :group 'info :version "24.1") -(defcustom Info-enable-edit nil - "Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node. -This is convenient if you want to write Info files by hand. -However, we recommend that you not do this. -It is better to write a Texinfo file and generate the Info file from that, -because that gives you a printed manual as well." - :type 'boolean - :group 'info) - (defvar Info-enable-active-nodes nil "Non-nil allows Info to execute Lisp code associated with nodes. The Lisp code is executed when the node is selected.") @@ -167,6 +158,12 @@ A header-line does not scroll with the rest of the buffer." "Face for Info nodes in a node header." :group 'info) +(defface info-index-match + '((t :inherit match)) + "Face used to highlight matches in an index entry." + :group 'info + :version "24.4") + ;; This is a defcustom largely so that we can get the benefit ;; of custom-initialize-delay. Perhaps it would work to make it a ;; defvar and explicitly give it a standard-value property, and @@ -375,6 +372,9 @@ with wrapping around the current Info node." (defvar Info-edit-mode-hook nil "Hooks run when `Info-edit-mode' is called.") +(make-obsolete-variable 'Info-edit-mode-hook + "editing Info nodes by hand is not recommended." "24.4") + (defvar Info-current-file nil "Info file that Info is now looking at, or nil. This is the name that was specified in Info, not the actual file name. @@ -397,6 +397,10 @@ Marker points nowhere if file has no tag table.") (defvar Info-current-file-completions nil "Cached completion list for current Info file.") +(defvar Info-file-completions nil + "Cached completion alist of visited Info files. +Each element of the alist is (FILE . COMPLETIONS)") + (defvar Info-file-supports-index-cookies nil "Non-nil if current Info file supports index cookies.") @@ -742,11 +746,15 @@ in `Info-file-supports-index-cookies-list'." (push dir Info-directory-list))))))) ;;;###autoload -(defun info-other-window (&optional file-or-node) +(defun info-other-window (&optional file-or-node buffer) "Like `info' but show the Info buffer in another window." - (interactive (if current-prefix-arg - (list (read-file-name "Info file name: " nil nil t)))) - (info-setup file-or-node (switch-to-buffer-other-window "*info*"))) + (interactive (list + (if (and current-prefix-arg (not (numberp current-prefix-arg))) + (read-file-name "Info file name: " nil nil t)) + (if (numberp current-prefix-arg) + (format "*info*<%s>" current-prefix-arg)))) + (info-setup file-or-node + (switch-to-buffer-other-window (or buffer "*info*")))) ;;;###autoload (put 'info 'info-file (purecopy "emacs")) ;;;###autoload @@ -763,8 +771,9 @@ with the top-level Info directory. In interactive use, a non-numeric prefix argument directs this command to read a file name from the minibuffer. -A numeric prefix argument selects an Info buffer with the prefix number -appended to the Info buffer name. + +A numeric prefix argument N selects an Info buffer named +\"*info*<%s>\". The search path for Info files is in the variable `Info-directory-list'. The top-level Info directory is made by combining all the files named `dir' @@ -1668,7 +1677,9 @@ escaped (\\\",\\\\)." " (" (if (stringp Info-current-file) (replace-regexp-in-string - "%" "%%" (file-name-nondirectory Info-current-file)) + "%" "%%" + (file-name-sans-extension + (file-name-nondirectory Info-current-file))) (format "*%S*" Info-current-file)) ") " (if Info-current-node @@ -1692,7 +1703,9 @@ escaped (\\\",\\\\)." If NODENAME is of the form (FILENAME)NODENAME, the node is in the Info file FILENAME; otherwise, NODENAME should be in the current Info file (or one of its sub-files). -Completion is available, but only for node names in the current Info file. +Completion is available for node names in the current Info file as well as +in the Info file FILENAME after the closing parenthesis in (FILENAME). +Empty NODENAME in (FILENAME) defaults to the Top node. 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." @@ -1729,6 +1742,7 @@ list of valid filename suffixes for Info files. See (when (file-name-absolute-p string) (setq dirs (list (file-name-directory string)))) (let ((names nil) + (names-sans-suffix nil) (suffix (concat (regexp-opt suffixes t) "\\'")) (string-dir (file-name-directory string))) (dolist (dir dirs) @@ -1751,7 +1765,14 @@ list of valid filename suffixes for Info files. See ;; add the unsuffixed name as a completion option. (when (string-match suffix file) (setq file (substring file 0 (match-beginning 0))) - (push (if string-dir (concat string-dir file) file) names))))) + (push (if string-dir (concat string-dir file) file) + names-sans-suffix))))) + ;; If there is just one file, don't duplicate it with suffixes, + ;; so `Info-read-node-name-1' will be able to complete a single + ;; candidate and to add the terminating ")". + (if (and (= (length names) 1) (= (length names-sans-suffix) 1)) + (setq names names-sans-suffix) + (setq names (append names-sans-suffix names))) (complete-with-action action names string pred))) (defun Info-read-node-name-1 (string predicate code) @@ -1769,12 +1790,23 @@ See `completing-read' for a description of arguments and usage." (substring string 1) predicate code)) - ;; If a file name was given, then any node is fair game. - ((string-match "\\`(" string) - (cond - ((eq code nil) string) - ((eq code t) nil) - (t t))) + ;; If a file name was given, complete nodes in the file. + ((string-match "\\`(\\([^)]+\\))" string) + (let ((file0 (match-string 0 string)) + (file1 (match-string 1 string)) + (nodename (substring string (match-end 0)))) + (if (and (equal nodename "") (eq code 'lambda)) + ;; Empty node name is permitted that means "Top". + t + (completion-table-with-context + file0 + (apply-partially + (lambda (string pred action) + (complete-with-action + action + (Info-build-node-completions (Info-find-file file1)) + string pred))) + nodename predicate code)))) ;; Otherwise use Info-read-node-completion-table. (t (complete-with-action code Info-read-node-completion-table string predicate)))) @@ -1783,7 +1815,9 @@ See `completing-read' for a description of arguments and usage." (defun Info-read-node-name (prompt) "Read an Info node name with completion, prompting with PROMPT. A node name can have the form \"NODENAME\", referring to a node -in the current Info file, or \"(FILENAME)NODENAME\"." +in the current Info file, or \"(FILENAME)NODENAME\", referring to +a node in FILENAME. \"(FILENAME)\" is a short format to go to +the Top node in FILENAME." (let* ((completion-ignore-case t) (Info-read-node-completion-table (Info-build-node-completions)) (nodename (completing-read prompt 'Info-read-node-name-1 nil t))) @@ -1791,41 +1825,54 @@ in the current Info file, or \"(FILENAME)NODENAME\"." (Info-read-node-name prompt) nodename))) -(defun Info-build-node-completions () - (or Info-current-file-completions - (let ((compl nil) - ;; Bind this in case the user sets it to nil. - (case-fold-search t) - (node-regexp "Node: *\\([^,\n]*\\) *[,\n\t]")) - (save-excursion - (save-restriction - (or Info-tag-table-marker - (error "No Info tags found")) - (if (marker-buffer Info-tag-table-marker) - (let ((marker Info-tag-table-marker)) - (set-buffer (marker-buffer marker)) - (widen) - (goto-char marker) - (while (re-search-forward "\n\\(Node\\|Ref\\): \\(.*\\)\177" nil t) - (setq compl - (cons (list (match-string-no-properties 2)) - compl)))) +(defun Info-build-node-completions (&optional filename) + (if 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)) + (or Info-current-file-completions + (Info-build-node-completions-1)))) + +(defun Info-build-node-completions-1 () + (let ((compl nil) + ;; Bind this in case the user sets it to nil. + (case-fold-search t) + (node-regexp "Node: *\\([^,\n]*\\) *[,\n\t]")) + (save-excursion + (save-restriction + (or Info-tag-table-marker + (error "No Info tags found")) + (if (marker-buffer Info-tag-table-marker) + (let ((marker Info-tag-table-marker)) + (set-buffer (marker-buffer marker)) (widen) - (goto-char (point-min)) - ;; If the buffer begins with a node header, process that first. - (if (Info-node-at-bob-matching node-regexp) - (setq compl (list (match-string-no-properties 1)))) - ;; Now for the rest of the nodes. - (while (search-forward "\n\^_" nil t) - (forward-line 1) - (let ((beg (point))) - (forward-line 1) - (if (re-search-backward node-regexp beg t) - (setq compl - (cons (list (match-string-no-properties 1)) - compl)))))))) - (setq compl (cons '("*") compl)) - (set (make-local-variable 'Info-current-file-completions) compl)))) + (goto-char marker) + (while (re-search-forward "\n\\(Node\\|Ref\\): \\(.*\\)\177" nil t) + (setq compl + (cons (list (match-string-no-properties 2)) + compl)))) + (widen) + (goto-char (point-min)) + ;; If the buffer begins with a node header, process that first. + (if (Info-node-at-bob-matching node-regexp) + (setq compl (list (match-string-no-properties 1)))) + ;; Now for the rest of the nodes. + (while (search-forward "\n\^_" nil t) + (forward-line 1) + (let ((beg (point))) + (forward-line 1) + (if (re-search-backward node-regexp beg t) + (setq compl + (cons (list (match-string-no-properties 1)) + compl)))))))) + (setq compl (cons '("*") (nreverse compl))) + (set (make-local-variable 'Info-current-file-completions) compl) + compl)) + (defun Info-restore-point (hl) "If this node has been visited, restore the point value when we left." @@ -2104,7 +2151,7 @@ and is not in the header line or a tag table." (let ((backward (< found beg-found))) (not (or - (and (not (eq search-invisible t)) + (and (not search-invisible) (if backward (or (text-property-not-all found beg-found 'invisible nil) (text-property-not-all found beg-found 'display nil)) @@ -3016,48 +3063,92 @@ See `Info-scroll-down'." (select-window (posn-window (event-start e)))) (Info-scroll-down))) -(defun Info-next-reference (&optional recur) - "Move cursor to the next cross-reference or menu item in the node." - (interactive) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") - (old-pt (point)) - (case-fold-search t)) - (or (eobp) (forward-char 1)) - (or (re-search-forward pat nil t) - (progn - (goto-char (point-min)) - (or (re-search-forward pat nil t) - (progn - (goto-char old-pt) - (user-error "No cross references in this node"))))) - (goto-char (or (match-beginning 1) (match-beginning 0))) - (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) - "Move cursor to the previous cross-reference or menu item in the node." - (interactive) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") - (old-pt (point)) - (case-fold-search t)) - (or (re-search-backward pat nil t) - (progn - (goto-char (point-max)) - (or (re-search-backward pat nil t) - (progn - (goto-char old-pt) - (user-error "No cross references in this node"))))) - (goto-char (or (match-beginning 1) (match-beginning 0))) - (if (looking-at "\\* Menu:") - (if recur - (user-error "No cross references in this node") - (Info-prev-reference t)) - (if (looking-at "^\\* ") - (forward-char 2))))) +(defun Info-next-reference-or-link (pat prop) + "Move point to the next pattern-based cross-reference or property-based link. +The next cross-reference is searched using the regexp PAT, and the next link +is searched using the text property PROP. Move point to the closest found position +of either a cross-reference found by `re-search-forward' or a link found by +`next-single-char-property-change'. Return the new position of point, or nil." + (let ((pxref (save-excursion (re-search-forward pat nil t))) + (plink (next-single-char-property-change (point) prop))) + (when (and (< plink (point-max)) (not (get-char-property plink prop))) + (setq plink (next-single-char-property-change plink prop))) + (if (< plink (point-max)) + (if (and pxref (<= pxref plink)) + (goto-char (or (match-beginning 1) (match-beginning 0))) + (goto-char plink)) + (if pxref (goto-char (or (match-beginning 1) (match-beginning 0))))))) + +(defun Info-prev-reference-or-link (pat prop) + "Move point to the previous pattern-based cross-reference or property-based link. +The previous cross-reference is searched using the regexp PAT, and the previous link +is searched using the text property PROP. Move point to the closest found position +of either a cross-reference found by `re-search-backward' or a link found by +`previous-single-char-property-change'. Return the new position of point, or nil." + (let ((pxref (save-excursion (re-search-backward pat nil t))) + (plink (previous-single-char-property-change (point) prop))) + (when (and (> plink (point-min)) (not (get-char-property plink prop))) + (setq plink (previous-single-char-property-change plink prop))) + (if (> plink (point-min)) + (if (and pxref (>= pxref plink)) + (goto-char (or (match-beginning 1) (match-beginning 0))) + (goto-char plink)) + (if pxref (goto-char (or (match-beginning 1) (match-beginning 0))))))) + +(defun Info-next-reference (&optional recur count) + "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") + (unless count + (setq count 1)) + (if (< count 0) + (Info-prev-reference recur (- count)) + (while (unless (zerop count) (setq count (1- count))) + (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") + (old-pt (point)) + (case-fold-search t)) + (or (eobp) (forward-char 1)) + (or (Info-next-reference-or-link pat 'link) + (progn + (goto-char (point-min)) + (or (Info-next-reference-or-link pat 'link) + (progn + (goto-char old-pt) + (user-error "No cross references in this node"))))) + (if (looking-at "\\* Menu:") + (if recur + (user-error "No cross references in this node") + (Info-next-reference t)) + (if (looking-at "^\\* ") + (forward-char 2))))))) + +(defun Info-prev-reference (&optional recur count) + "Move cursor to the previous cross-reference or menu item in the node. +If COUNT is non-nil (interactively with a prefix arg), jump over +COUNT cross-references." + (interactive "i\np") + (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))))))) (defun Info-index-nodes (&optional file) "Return a list of names of all index nodes in Info FILE. @@ -3191,7 +3282,7 @@ Give an empty topic name to go to the Index node itself." (= (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]+\\))\\)?" + (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) @@ -3210,12 +3301,14 @@ Give an empty topic name to go to the Index node itself." (progn (goto-char (point-min)) (while (re-search-forward pattern nil t) - (push (list (match-string-no-properties 1) - (match-string-no-properties 2) - Info-current-node - (string-to-number (concat "0" - (match-string 3)))) - matches)) + (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 @@ -3441,7 +3534,7 @@ MATCHES is a list of index matches found by `Info-apropos-matches'.") Return a list of matches where each element is in the format \((FILENAME INDEXTEXT NODENAME LINENUMBER))." (unless (string= string "") - (let ((pattern (format "\n\\* +\\([^\n]*%s[^\n]*\\):[ \t]+\\([^\n]+\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" + (let ((pattern (format "\n\\* +\\([^\n]*\\(%s\\)[^\n]*\\):[ \t]+\\([^\n]+\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" (regexp-quote string))) (ohist Info-history) (ohist-list Info-history-list) @@ -3474,12 +3567,15 @@ Return a list of matches where each element is in the format (progn (goto-char (point-min)) (while (re-search-forward pattern nil t) - (setq matches - (cons (list manual - (match-string-no-properties 1) - (match-string-no-properties 2) - (match-string-no-properties 3)) - matches))) + (let ((entry (match-string-no-properties 1)) + (nodename (match-string-no-properties 3)) + (line (match-string-no-properties 4))) + (add-text-properties + (- (match-beginning 2) (match-beginning 1)) + (- (match-end 2) (match-beginning 1)) + '(face info-index-match) entry) + (setq matches (cons (list manual entry nodename line) + matches)))) (setq nodes (cdr nodes) node (car nodes))) (Info-goto-node node)))) (error @@ -3785,7 +3881,25 @@ If FORK is non-nil, it is passed to `Info-goto-node'." ((setq node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)")) (Info-goto-node "Top" fork)) ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)")) - (Info-goto-node node fork))) + (Info-goto-node node fork)) + ;; footnote + ((setq node (Info-get-token (point) "(" "\\(([0-9]+)\\)")) + (let ((old-point (point)) new-point) + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "^[ \t]*-+ Footnotes -+$" nil t) + (setq new-point (if (< old-point (point)) + ;; Go to footnote reference + (and (search-forward node nil t) + ;; Put point at beginning of link + (match-beginning 0)) + ;; Go to footnote definition + (search-backward node nil t))))) + (if new-point + (progn + (goto-char new-point) + (setq node t)) + (setq node nil))))) node)) (defun Info-mouse-follow-link (click) @@ -3813,6 +3927,7 @@ If FORK is non-nil, it is passed to `Info-goto-node'." (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) @@ -4032,7 +4147,9 @@ With a zero prefix arg, put the name inside a function call to `info'." (unless Info-current-node (user-error "No current Info node")) (let ((node (if (stringp Info-current-file) - (concat "(" (file-name-nondirectory 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 "\")"))) @@ -4189,6 +4306,10 @@ Advanced commands: map) "Local keymap used within `e' command of Info.") +(make-obsolete-variable 'Info-edit-map + "editing Info nodes by hand is not recommended." + "24.4") + ;; Info-edit mode is suitable only for specially formatted data. (put 'Info-edit-mode 'mode-class 'special) @@ -4206,16 +4327,22 @@ which returns to Info mode for browsing. (buffer-enable-undo (current-buffer)) (run-mode-hooks 'Info-edit-mode-hook)) +(make-obsolete 'Info-edit-mode + "editing Info nodes by hand is not recommended." "24.4") + (defun Info-edit () - "Edit the contents of this Info node. -Allowed only if variable `Info-enable-edit' is non-nil." + "Edit the contents of this Info node." (interactive) - (or Info-enable-edit - (error "Editing Info nodes is not enabled")) (Info-edit-mode) (message "%s" (substitute-command-keys "Editing: Type \\<Info-edit-map>\\[Info-cease-edit] to return to info"))) +(put 'Info-edit 'disabled "Editing Info nodes by hand is not recommended. +This feature will be removed in future.") + +(make-obsolete 'Info-edit + "editing Info nodes by hand is not recommended." "24.4") + (defun Info-cease-edit () "Finish editing Info node; switch back to Info proper." (interactive) @@ -4232,6 +4359,9 @@ Allowed only if variable `Info-enable-edit' is non-nil." (and (marker-position Info-tag-table-marker) (buffer-modified-p) (message "Tags may have changed. Use Info-tagify if necessary"))) + +(make-obsolete 'Info-cease-edit + "editing Info nodes by hand is not recommended." "24.4") (defvar Info-file-list-for-emacs '("ediff" "eudc" "forms" "gnus" "info" ("Info" . "info") ("mh" . "mh-e") @@ -4253,7 +4383,8 @@ Allowed only if variable `Info-enable-edit' is non-nil." ("ietf-drums" . "emacs-mime") ("quoted-printable" . "emacs-mime") ("binhex" . "emacs-mime") ("uudecode" . "emacs-mime") ("mailcap" . "emacs-mime") ("mm" . "emacs-mime") - ("mml" . "emacs-mime")) + ("mml" . "emacs-mime") + "tramp" "dbus") "List of Info files that describe Emacs commands. An element can be a file name, or a list of the form (PREFIX . FILE) where PREFIX is a name prefix and FILE is the file to look in. @@ -4264,7 +4395,7 @@ If the element is just a file name, the file name also serves as the prefix.") The `info-file' property of COMMAND says which Info manual to search. If COMMAND has no property, the variable `Info-file-list-for-emacs' defines heuristics for which Info manual to try. -The locations are of the format used in `Info-history', i.e. +The locations are of the format used in the variable `Info-history', i.e. \(FILENAME NODENAME BUFFERPOS), where BUFFERPOS is the line number in the first element of the returned list (which is treated specially in `Info-goto-emacs-command-node'), and 0 for the rest elements of a list." @@ -4419,7 +4550,8 @@ first line or header line, and for breadcrumb links.") (if (not (equal node "Top")) node (format "(%s)Top" (if (stringp Info-current-file) - (file-name-nondirectory Info-current-file) + (file-name-sans-extension + (file-name-nondirectory Info-current-file)) ;; Some legacy code can still use a symbol. Info-current-file))))) (setq line (concat @@ -4531,7 +4663,8 @@ first line or header line, and for breadcrumb links.") (if (re-search-forward (format "File: %s\\([^,\n\t]+\\)," (if (stringp Info-current-file) - (file-name-nondirectory Info-current-file) + (file-name-sans-extension + (file-name-nondirectory Info-current-file)) Info-current-file)) header-end t) (put-text-property (match-beginning 1) (match-end 1) @@ -4823,6 +4956,21 @@ first line or header line, and for breadcrumb links.") mouse-face highlight help-echo "mouse-2: go to this URL")))) + ;; Fontify footnotes + (goto-char (point-min)) + (when (and not-fontified-p (re-search-forward "^[ \t]*-+ Footnotes -+$" nil t)) + (let ((limit (point))) + (goto-char (point-min)) + (while (re-search-forward "\\(([0-9]+)\\)" nil t) + (add-text-properties (match-beginning 0) (match-end 0) + `(font-lock-face info-xref + link t + mouse-face highlight + help-echo + ,(if (< (point) limit) + "mouse-2: go to footnote definition" + "mouse-2: go to footnote reference")))))) + ;; Hide empty lines at the end of the node. (goto-char (point-max)) (skip-chars-backward "\n") @@ -4836,6 +4984,17 @@ first line or header line, and for breadcrumb links.") ;; current Info node. (eval-when-compile (require 'speedbar)) +(declare-function speedbar-add-expansion-list "speedbar" (new-list)) +(declare-function speedbar-center-buffer-smartly "speedbar" ()) +(declare-function speedbar-change-expand-button-char "speedbar" (char)) +(declare-function speedbar-change-initial-expansion-list "speedbar" (new-default)) +(declare-function speedbar-delete-subblock "speedbar" (indent)) +(declare-function speedbar-make-specialized-keymap "speedbar" ()) +(declare-function speedbar-make-tag-line "speedbar" + (exp-button-type exp-button-char exp-button-function + exp-button-data tag-button tag-button-function + tag-button-data tag-button-face depth)) + (defvar Info-speedbar-key-map nil "Keymap used when in the Info display mode.") @@ -5058,7 +5217,8 @@ BUFFER is the buffer speedbar is requesting buttons for." "This implements the `bookmark-make-record-function' type (which see) for Info nodes." (let* ((file (and (stringp Info-current-file) - (file-name-nondirectory Info-current-file))) + (file-name-sans-extension + (file-name-nondirectory Info-current-file)))) (bookmark-name (if file (concat "(" file ") " Info-current-node) Info-current-node)) @@ -5086,8 +5246,16 @@ type returned by `Info-bookmark-make-record', which see." ;;;###autoload (defun info-display-manual (manual) - "Go to Info buffer that displays MANUAL, creating it if none already exists." - (interactive "sManual name: ") + "Display an Info buffer displaying MANUAL. +If there is an existing Info buffer for MANUAL, display it. +Otherwise, visit the manual in a new Info buffer." + (interactive + (list + (progn + (info-initialize) + (completing-read "Manual name: " + (info--manual-names) + nil t)))) (let ((blist (buffer-list)) (manual-re (concat "\\(/\\|\\`\\)" manual "\\(\\.\\|\\'\\)")) (case-fold-search t) @@ -5102,7 +5270,25 @@ type returned by `Info-bookmark-make-record', which see." (if found (switch-to-buffer found) (info-initialize) - (info (Info-find-file manual))))) + (info (Info-find-file manual) + (generate-new-buffer-name "*info*"))))) + +(defun info--manual-names () + (let (names) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (and (eq major-mode 'Info-mode) + (stringp Info-current-file) + (not (string= (substring (buffer-name) 0 1) " ")) + (push (file-name-sans-extension + (file-name-nondirectory Info-current-file)) + names)))) + (delete-dups (append (nreverse names) + (all-completions + "" + (apply-partially 'Info-read-node-name-2 + Info-directory-list + (mapcar 'car Info-suffix-list))))))) (provide 'info) |