;; info.el --- info package for Emacs
-;; Copyright (C) 1985-1986, 1992-2013 Free Software Foundation, Inc.
+;; Copyright (C) 1985-1986, 1992-2014 Free Software Foundation, Inc.
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
;; Keywords: help
;; This file is part of GNU Emacs.
: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.")
"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
(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.
(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.")
(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
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'
(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
(setq filename found)
(if noerror
(setq filename nil)
- (error "Info file %s does not exist" filename)))
+ ;; If there is no previous Info file, go to the directory.
+ (unless Info-current-file
+ (Info-directory))
+ (user-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)
"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)))
(+ (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
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)
(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)
;; 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).
;; 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)))
(Info-find-index-name Info-point-loc)
(setq Info-point-loc nil))))))
;; If we did not finish finding the specified node,
- ;; go back to the previous one.
- (or Info-current-node no-going-back (null Info-history)
- (let ((hist (car Info-history)))
- (setq Info-history (cdr Info-history))
- (Info-find-node (nth 0 hist) (nth 1 hist) t)
- (goto-char (nth 2 hist))))))
+ ;; go back to the previous one or to the Top node.
+ (unless (or Info-current-node no-going-back)
+ (if Info-history
+ (let ((hist (car Info-history)))
+ (setq Info-history (cdr Info-history))
+ (Info-find-node (nth 0 hist) (nth 1 hist) t)
+ (goto-char (nth 2 hist)))
+ (Info-find-node Info-current-file "Top" t)))))
;; Cache the contents of the (virtual) dir file, once we have merged
;; it for the first time, so we can save time subsequently.
;; 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."
""))
(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)))
" ("
(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
;; 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
(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)
(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)
;; 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)
(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))))
(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)))
(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))
+
\f
(defun Info-restore-point (hl)
"If this node has been visited, restore the point value when we left."
(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."
(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)))
(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
(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))
(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 ()
(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)
(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)
(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)))))))
\f
(defun Info-index-nodes (&optional file)
"Return a list of names of all index nodes in Info FILE.
(= (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)
(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
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)
(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
hits desc)
(dolist (keyword keywords)
(push (copy-tree (gethash keyword finder-keywords-hash)) hits))
- (setq hits (delete-dups (apply 'append hits)))
+ (setq hits (delete-dups (apply 'append hits))
+ ;; Not a meaningful package.
+ hits (delete 'emacs hits))
(dolist (package hits)
(setq desc (cdr-safe (assq package package--builtins)))
(when (vectorp desc)
(insert "*****************\n\n")
(insert
"Commentary section of the package `" nodename "':\n\n")
+ ;; FIXME this assumes that a file named package.el exists,
+ ;; which is not always true. E.g. for the nxml package,
+ ;; there is no "nxml.el" (it's nxml-mode.el).
(let ((str (lm-commentary (find-library-name nodename))))
(if (null str)
(insert "Can't find any Commentary section\n\n")
((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))
(suppress-keymap map)
(define-key map "." 'beginning-of-buffer)
(define-key map " " 'Info-scroll-up)
+ (define-key map [?\S-\ ] 'Info-scroll-down)
(define-key map "\C-m" 'Info-follow-nearest-node)
(define-key map "\t" 'Info-next-reference)
(define-key map "\e\t" 'Info-prev-reference)
(define-key map "f" 'Info-follow-reference)
(define-key map "g" 'Info-goto-node)
(define-key map "h" 'Info-help)
+ ;; This is for compatibility with standalone info (>~ version 5.2).
+ ;; Though for some time, standalone info had H and h reversed.
+ ;; See <http://debbugs.gnu.org/16455>.
+ (define-key map "H" 'describe-mode)
(define-key map "i" 'Info-index)
(define-key map "I" 'Info-virtual-index)
(define-key map "l" 'Info-history-back)
(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))
()
(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 "\")")))
'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)
;; 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)))
(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-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)
(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")
\f
(defvar Info-file-list-for-emacs
'("ediff" "eudc" "forms" "gnus" "info" ("Info" . "info") ("mh" . "mh-e")
("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.
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."
;; 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.
(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
(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)
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")
;;; 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" ())
+(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.")
(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.
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))
"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))
\f
;;;###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)
(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)