X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/d8ad4d3ff9dcea9c581d72e1e9ec292ea18673b1..0ccdf61ed6527800856123189c799beca78218e3:/lisp/info.el diff --git a/lisp/info.el b/lisp/info.el index 6624c0d61b..c276420fb7 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -1,6 +1,6 @@ ;; info.el --- info package for Emacs -;; Copyright (C) 1985-1986, 1992-2012 Free Software Foundation, Inc. +;; Copyright (C) 1985-1986, 1992-2013 Free Software Foundation, Inc. ;; Maintainer: FSF ;; Keywords: help @@ -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." @@ -3016,48 +3063,62 @@ 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 (&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 (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 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 (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-index-nodes (&optional file) "Return a list of names of all index nodes in Info FILE. @@ -4032,7 +4093,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 "\")"))) @@ -4264,7 +4327,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 +4482,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 +4595,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) @@ -5069,7 +5134,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)) @@ -5130,13 +5196,16 @@ Otherwise, visit the manual in a new Info buffer." (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) - (apply-partially 'Info-read-node-name-2 - Info-directory-list - (mapcar 'car Info-suffix-list)))))) + (all-completions + "" + (apply-partially 'Info-read-node-name-2 + Info-directory-list + (mapcar 'car Info-suffix-list))))))) (provide 'info)