X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/858aab4c0267bd9c3760f337e03916b238d712c1..bb8097b96b074e55ff05dc379b394dbdbdc82f3e:/lisp/info.el diff --git a/lisp/info.el b/lisp/info.el index 4c7bb981af..9344268931 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 @@ -59,15 +59,6 @@ to the user." :group 'info :version "24.1") -(defcustom Info-enable-edit nil - "Non-nil means the \\\\[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' @@ -781,7 +790,7 @@ See a list of available Info commands in `Info-mode'." (defun info-setup (file-or-node buffer) "Display Info node FILE-OR-NODE in BUFFER." - (if (and buffer (not (eq major-mode 'Info-mode))) + (if (and buffer (not (derived-mode-p 'Info-mode))) (Info-mode)) (if file-or-node ;; If argument already contains parentheses, don't add another set @@ -911,20 +920,24 @@ just return nil (no error)." (error "Info file %s does not exist" filename))) filename)))) -(defun Info-find-node (filename nodename &optional no-going-back) +(defun Info-find-node (filename nodename &optional no-going-back strict-case) "Go to an Info node specified as separate FILENAME and NODENAME. NO-GOING-BACK is non-nil if recovering from an error in this function; -it says do not attempt further (recursive) error recovery." +it says do not attempt further (recursive) error recovery. + +This function first looks for a case-sensitive match for NODENAME; +if none is found it then tries a case-insensitive match (unless +STRICT-CASE is non-nil)." (info-initialize) (setq filename (Info-find-file filename)) ;; Go into Info buffer. - (or (eq major-mode 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) ;; Record the node we are leaving, if we were in one. (and (not no-going-back) Info-current-file (push (list Info-current-file Info-current-node (point)) Info-history)) - (Info-find-node-2 filename nodename no-going-back)) + (Info-find-node-2 filename nodename no-going-back strict-case)) ;;;###autoload (defun Info-on-current-buffer (&optional nodename) @@ -948,7 +961,7 @@ otherwise, that defaults to `Top'." "Go to an Info node FILENAME and NODENAME, re-reading disk contents. When *info* is already displaying FILENAME and NODENAME, the window position is preserved, if possible." - (or (eq major-mode 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) (let ((old-filename Info-current-file) (old-nodename Info-current-node) (window-selected (eq (selected-window) (get-buffer-window))) @@ -1001,7 +1014,7 @@ which the match was found." (+ (point-min) (read (current-buffer))) major-mode))))) -(defun Info-find-in-tag-table (marker regexp) +(defun Info-find-in-tag-table (marker regexp &optional strict-case) "Find a node in a tag table. MARKER specifies the buffer and position to start searching at. REGEXP is a regular expression matching nodes or references. Its first @@ -1011,10 +1024,11 @@ FOUND-ANCHOR is non-nil if a `Ref:' was matched, POS is the position where the match was found, and MODE is `major-mode' of the buffer in which the match was found. This function tries to find a case-sensitive match first, then a -case-insensitive match is tried." +case-insensitive match is tried (unless optional argument STRICT-CASE +is non-nil)." (let ((result (Info-find-in-tag-table-1 marker regexp nil))) - (when (null (car result)) - (setq result (Info-find-in-tag-table-1 marker regexp t))) + (or strict-case (car result) + (setq result (Info-find-in-tag-table-1 marker regexp t))) result)) (defun Info-find-node-in-buffer-1 (regexp case-fold) @@ -1037,19 +1051,21 @@ Value is the position at which a match was found, or nil if not found." (setq found (line-beginning-position))))))) found)) -(defun Info-find-node-in-buffer (regexp) +(defun Info-find-node-in-buffer (regexp &optional strict-case) "Find a node or anchor in the current buffer. REGEXP is a regular expression matching nodes or references. Its first group should match `Node:' or `Ref:'. Value is the position at which a match was found, or nil if not found. This function looks for a case-sensitive match first. If none is found, -a case-insensitive match is tried." +a case-insensitive match is tried (unless optional argument STRICT-CASE +is non-nil)." (or (Info-find-node-in-buffer-1 regexp nil) - (Info-find-node-in-buffer-1 regexp t))) + (and (not strict-case) + (Info-find-node-in-buffer-1 regexp t)))) -(defun Info-find-node-2 (filename nodename &optional no-going-back) +(defun Info-find-node-2 (filename nodename &optional no-going-back strict-case) (buffer-disable-undo (current-buffer)) - (or (eq major-mode 'Info-mode) + (or (derived-mode-p 'Info-mode) (Info-mode)) (widen) (setq Info-current-node nil) @@ -1158,7 +1174,7 @@ a case-insensitive match is tried." ;; First, search a tag table, if any (when (marker-position Info-tag-table-marker) (let* ((m Info-tag-table-marker) - (found (Info-find-in-tag-table m regexp))) + (found (Info-find-in-tag-table m regexp strict-case))) (when found ;; FOUND is (ANCHOR POS MODE). @@ -1185,7 +1201,7 @@ a case-insensitive match is tried." ;; buffer) to find the actual node. First, check ;; whether the node is right where we are, in case the ;; buffer begins with a node. - (let ((pos (Info-find-node-in-buffer regexp))) + (let ((pos (Info-find-node-in-buffer regexp strict-case))) (when pos (goto-char pos) (throw 'foo t))) @@ -1521,11 +1537,14 @@ a case-insensitive match is tried." ;; Widen in case we are in the same subfile as before. (widen) (goto-char (point-min)) + ;; Skip the summary segment for `Info-search'. (if (looking-at "\^_") (forward-char 1) (search-forward "\n\^_")) + ;; Don't add the length of the skipped summary segment to + ;; the value returned to `Info-find-node-2'. (Bug#14125) (if (numberp nodepos) - (+ (- nodepos lastfilepos) (point))))) + (+ (- nodepos lastfilepos) (point-min))))) (defun Info-unescape-quotes (value) "Unescape double quotes and backslashes in VALUE." @@ -1576,17 +1595,20 @@ escaped (\\\",\\\\)." "")) (image (if (file-exists-p image-file) (create-image image-file) - "[broken image]"))) + (or (cdr (assoc-string "text" parameter-alist)) + (and src (concat "[broken image:" src "]")) + "[broken image]")))) (if (not (get-text-property start 'display)) (add-text-properties - start (point) `(display ,image rear-nonsticky (display))))) + start (point) + `(display ,image rear-nonsticky (display) + help-echo ,(cdr (assoc-string "alt" parameter-alist)))))) ;; text-only display, show alternative text if provided, or ;; otherwise a clue that there's meant to be a picture (delete-region start (point)) (insert (or (cdr (assoc-string "text" parameter-alist)) (cdr (assoc-string "alt" parameter-alist)) - (and src - (concat "[image:" src "]")) + (and src (concat "[image:" src "]")) "[image]")))))) (set-buffer-modified-p nil))) @@ -1689,15 +1711,21 @@ escaped (\\\",\\\\)." ;; Don't autoload this function: the correct entry point for other packages ;; to use is `info'. --Stef ;; ;;;###autoload -(defun Info-goto-node (nodename &optional fork) +(defun Info-goto-node (nodename &optional fork strict-case) "Go to Info node named NODENAME. Give just NODENAME or (FILENAME)NODENAME. 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." +If FORK is a string, it is the name to use for the new buffer. + +This function first looks for a case-sensitive match for the node part +of NODENAME; if none is found it then tries a case-insensitive match +\(unless STRICT-CASE is non-nil)." (interactive (list (Info-read-node-name "Go to node: ") current-prefix-arg)) (info-initialize) (if fork @@ -1716,7 +1744,7 @@ If FORK is a string, it is the name to use for the new buffer." (if trim (setq nodename (substring nodename 0 trim)))) (if transient-mark-mode (deactivate-mark)) (Info-find-node (if (equal filename "") nil filename) - (if (equal nodename "") "Top" nodename)))) + (if (equal nodename "") "Top" nodename) nil strict-case))) (defvar Info-read-node-completion-table) @@ -1731,6 +1759,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) @@ -1753,7 +1782,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) @@ -1771,12 +1807,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)))) @@ -1785,7 +1832,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))) @@ -1793,41 +1842,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." @@ -1846,6 +1908,30 @@ in the current Info file, or \"(FILENAME)NODENAME\"." (defvar Info-search-case-fold nil "The value of `case-fold-search' from previous `Info-search' command.") +(defun Info--search-loop (regexp bound backward) + (when backward + ;; Hide Info file header for backward search. + (narrow-to-region (save-excursion + (goto-char (point-min)) + (search-forward "\n\^_") + (1- (point))) + (point-max))) + (let ((give-up nil) + (found nil) + (beg-found nil)) + (while (not (or give-up + (and found + (funcall isearch-filter-predicate + beg-found found)))) + (let ((search-spaces-regexp Info-search-whitespace-regexp)) + (if (funcall + (if backward #'re-search-backward #'re-search-forward) + regexp bound t) + (setq found (point) beg-found (if backward (match-end 0) + (match-beginning 0))) + (setq give-up t found nil)))) + found)) + (defun Info-search (regexp &optional bound _noerror _count direction) "Search for REGEXP, starting from point, and select node it's found in. If DIRECTION is `backward', search in the reverse direction." @@ -1861,54 +1947,35 @@ If DIRECTION is `backward', search in the reverse direction." (when (equal regexp "") (setq regexp (car Info-search-history))) (when regexp - (let (found beg-found give-up - (backward (eq direction 'backward)) - (onode Info-current-node) - (ofile Info-current-file) - (opoint (point)) - (opoint-min (point-min)) - (opoint-max (point-max)) - (ostart (window-start)) - (osubfile Info-current-subfile)) - (setq Info-search-case-fold case-fold-search) - (save-excursion - (save-restriction - (widen) - (when backward - ;; Hide Info file header for backward search - (narrow-to-region (save-excursion - (goto-char (point-min)) - (search-forward "\n\^_") - (1- (point))) - (point-max))) - (while (and (not give-up) - (or (null found) - (not (funcall isearch-filter-predicate beg-found found)))) - (let ((search-spaces-regexp Info-search-whitespace-regexp)) - (if (if backward - (re-search-backward regexp bound t) - (re-search-forward regexp bound t)) - (setq found (point) beg-found (if backward (match-end 0) - (match-beginning 0))) - (setq give-up t)))))) - - (when (and isearch-mode Info-isearch-search - (not Info-isearch-initial-node) - (not bound) - (or give-up (and found (not (and (> found opoint-min) - (< found opoint-max)))))) + (setq Info-search-case-fold case-fold-search) + (let* ((backward (eq direction 'backward)) + (onode Info-current-node) + (ofile Info-current-file) + (opoint (point)) + (opoint-min (point-min)) + (opoint-max (point-max)) + (ostart (window-start)) + (osubfile Info-current-subfile) + (found + (save-excursion + (save-restriction + (widen) + (Info--search-loop regexp bound backward))))) + + (unless (or (not isearch-mode) (not Info-isearch-search) + Info-isearch-initial-node + bound + (and found (> found opoint-min) (< found opoint-max))) (signal 'search-failed (list regexp "end of node"))) ;; If no subfiles, give error now. - (if give-up - (if (null Info-current-subfile) - (if isearch-mode - (signal 'search-failed (list regexp "end of manual")) - (let ((search-spaces-regexp Info-search-whitespace-regexp)) - (if backward - (re-search-backward regexp) - (re-search-forward regexp)))) - (setq found nil))) + (unless (or found Info-current-subfile) + (if isearch-mode + (signal 'search-failed (list regexp "end of manual")) + (let ((search-spaces-regexp Info-search-whitespace-regexp)) + (if backward + (re-search-backward regexp) + (re-search-forward regexp))))) (if (and bound (not found)) (signal 'search-failed (list regexp))) @@ -1949,28 +2016,9 @@ If DIRECTION is `backward', search in the reverse direction." (while list (message "Searching subfile %s..." (cdr (car list))) (Info-read-subfile (car (car list))) - (when backward - ;; Hide Info file header for backward search - (narrow-to-region (save-excursion - (goto-char (point-min)) - (search-forward "\n\^_") - (1- (point))) - (point-max)) - (goto-char (point-max))) + (when backward (goto-char (point-max))) (setq list (cdr list)) - (setq give-up nil found nil) - (while (and (not give-up) - (or (null found) - (not (funcall isearch-filter-predicate beg-found found)))) - (let ((search-spaces-regexp Info-search-whitespace-regexp)) - (if (if backward - (re-search-backward regexp nil t) - (re-search-forward regexp nil t)) - (setq found (point) beg-found (if backward (match-end 0) - (match-beginning 0))) - (setq give-up t)))) - (if give-up - (setq found nil)) + (setq found (Info--search-loop regexp nil backward)) (if found (setq list nil))) (if found @@ -2106,7 +2154,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)) @@ -2164,7 +2212,7 @@ End of submatch 0, 1, and 3 are the same, so you can safely concat." (interactive) ;; In case another window is currently selected (save-window-excursion - (or (eq major-mode 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) (Info-goto-node (Info-extract-pointer "next")))) (defun Info-prev () @@ -2172,7 +2220,7 @@ End of submatch 0, 1, and 3 are the same, so you can safely concat." (interactive) ;; In case another window is currently selected (save-window-excursion - (or (eq major-mode 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")))) (defun Info-up (&optional same-file) @@ -2181,7 +2229,7 @@ If SAME-FILE is non-nil, do not move to a different Info file." (interactive) ;; In case another window is currently selected (save-window-excursion - (or (eq major-mode 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) (let ((old-node Info-current-node) (old-file Info-current-file) (node (Info-extract-pointer "up")) p) @@ -3018,48 +3066,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. @@ -3193,7 +3285,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) @@ -3212,12 +3304,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 @@ -3443,7 +3537,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) @@ -3476,12 +3570,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 @@ -3771,6 +3868,24 @@ If FORK is non-nil, it is passed to `Info-goto-node'." ((setq node (Info-get-token (point) "\\*note[ \n\t]+" "\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?")) (Info-follow-reference 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)))) ;; menu item: node name ((setq node (Info-get-token (point) "\\* +" "\\* +\\([^:]*\\)::")) (Info-goto-node node fork)) @@ -3815,6 +3930,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) @@ -3966,7 +4082,7 @@ If FORK is non-nil, it is passed to `Info-goto-node'." (defun Info-menu-update () "Update the Info menu for the current node." (condition-case nil - (if (or (not (eq major-mode 'Info-mode)) + (if (or (not (derived-mode-p 'Info-mode)) (equal (list Info-current-file Info-current-node) Info-menu-last-node)) () @@ -4159,8 +4275,7 @@ Advanced commands: 'Info-isearch-wrap) (set (make-local-variable 'isearch-push-state-function) 'Info-isearch-push-state) - (set (make-local-variable 'isearch-filter-predicate) - 'Info-isearch-filter) + (set (make-local-variable 'isearch-filter-predicate) #'Info-isearch-filter) (set (make-local-variable 'revert-buffer-function) 'Info-revert-buffer-function) (Info-set-mode-line) @@ -4170,7 +4285,7 @@ Advanced commands: ;; When an Info buffer is killed, make sure the associated tags buffer ;; is killed too. (defun Info-kill-buffer () - (and (eq major-mode 'Info-mode) + (and (derived-mode-p 'Info-mode) Info-tag-table-buffer (kill-buffer Info-tag-table-buffer))) @@ -4187,39 +4302,45 @@ Advanced commands: (copy-marker (marker-position m))) (make-marker)))))) -(defvar Info-edit-map (let ((map (make-sparse-keymap))) - (set-keymap-parent map text-mode-map) - (define-key map "\C-c\C-c" 'Info-cease-edit) - map) +(define-obsolete-variable-alias 'Info-edit-map 'Info-edit-mode-map "24.1") +(defvar Info-edit-mode-map (let ((map (make-sparse-keymap))) + (set-keymap-parent map text-mode-map) + (define-key map "\C-c\C-c" 'Info-cease-edit) + 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) -(defun Info-edit-mode () +(define-derived-mode Info-edit-mode text-mode "Info Edit" "Major mode for editing the contents of an Info node. Like text mode with the addition of `Info-cease-edit' which returns to Info mode for browsing. \\{Info-edit-map}" - (use-local-map Info-edit-map) - (setq major-mode 'Info-edit-mode) - (setq mode-name "Info Edit") - (kill-local-variable 'mode-line-buffer-identification) (setq buffer-read-only nil) (force-mode-line-update) - (buffer-enable-undo (current-buffer)) - (run-mode-hooks 'Info-edit-mode-hook)) + (buffer-enable-undo (current-buffer))) + +(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-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) @@ -4227,15 +4348,14 @@ Allowed only if variable `Info-enable-edit' is non-nil." (and (buffer-modified-p) (y-or-n-p "Save the file? ") (save-buffer)) - (use-local-map Info-mode-map) - (setq major-mode 'Info-mode) - (setq mode-name "Info") - (Info-set-mode-line) - (setq buffer-read-only t) + (Info-mode) (force-mode-line-update) (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") @@ -4257,7 +4377,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. @@ -4268,7 +4389,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." @@ -4340,7 +4461,7 @@ COMMAND must be a symbol or string." ;; Get Info running, and pop to it in another window. (save-window-excursion (info)) - (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (pop-to-buffer "*info*")) ;; Bind Info-history to nil, to prevent the last Index node ;; visited by Info-find-emacs-command-nodes from being ;; pushed onto the history. @@ -4829,6 +4950,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") @@ -4840,7 +4976,7 @@ first line or header line, and for breadcrumb links.") ;;; Speedbar support: ;; These functions permit speedbar to display the "tags" in the ;; current Info node. -(eval-when-compile (require 'speedbar)) +(eval-when-compile (require 'speedbar)) ; for speedbar-with-writable (declare-function speedbar-add-expansion-list "speedbar" (new-list)) (declare-function speedbar-center-buffer-smartly "speedbar" ()) @@ -4902,6 +5038,10 @@ This will add a speedbar major display mode." (speedbar-change-initial-expansion-list "Info") ) +;; speedbar loads dframe at runtime. +(declare-function dframe-select-attached-frame "dframe" (&optional frame)) +(declare-function dframe-current-frame "dframe" (frame-var desired-major-mode)) + (defun Info-speedbar-hierarchy-buttons (_directory depth &optional node) "Display an Info directory hierarchy in speedbar. DIRECTORY is the current directory in the attached frame. @@ -4985,7 +5125,7 @@ INDENT is the current indentation depth." NODESPEC is a string of the form: (file)node." ;; Set up a buffer we can use to fake-out Info. (with-current-buffer (get-buffer-create " *info-browse-tmp*") - (if (not (equal major-mode 'Info-mode)) + (if (not (derived-mode-p 'Info-mode)) (Info-mode)) ;; Get the node into this buffer (if (not (string-match "^(\\([^)]+\\))\\([^.]+\\)$" nodespec)) @@ -5137,13 +5277,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)