;;; info.el --- info package for Emacs.
-;; Copyright (C) 1985, 1986, 1992 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1992, 1993 Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: help
This value is used as the default for `Info-directory-list'. It is set
in paths.el.")
+(defvar Info-fontify t
+ "*Non-nil enables highlighting and fonts in Info nodes.")
+
(defvar Info-directory-list
- (let ((path (getenv "INFOPATH")))
+ (let ((path (getenv "INFOPATH"))
+ (sibling (expand-file-name "../info/" (invocation-directory))))
(if path
(let ((list nil)
idx)
path (substring path (min (1+ idx)
(length path)))))
(nreverse list))
- Info-default-directory-list))
+ (if (or (member sibling Info-default-directory-list)
+ (not (file-exists-p sibling)))
+ Info-default-directory-list
+ (reverse (cons sibling (cdr (reverse Info-default-directory-list)))))))
"List of directories to search for Info documentation files.
nil means not yet initialized. In this case, Info uses the environment
variable INFOPATH to initialize it, or `Info-default-directory-list'
"Marker pointing at beginning of current Info file's tag table.
Marker points nowhere if file has no tag table.")
+(defvar Info-current-file-completions nil
+ "Cached completion list for current Info file.")
+
(defvar Info-index-alternatives nil
"List of possible matches for last Info-index command.")
-(defvar Info-suffix-list '( ("" . nil)
- (".info" . nil)
+(defvar Info-standalone nil
+ "Non-nil if Emacs was started solely as an Info browser.")
+
+(defvar Info-suffix-list '( (".info" . nil)
+ ("" . nil)
(".Z" . "uncompress")
(".Y" . "unyabba")
(".gz" . "gunzip")
(switch-to-buffer "*info*")
(Info-directory))))
+;;;###autoload
+(defun info-standalone ()
+ "Run Emacs as a standalone Info reader.
+Usage: emacs -f info-standalone [filename]
+In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
+ (setq Info-standalone t)
+ (if (and command-line-args-left
+ (not (string-match "^-" (car command-line-args-left))))
+ (condition-case err
+ (progn
+ (info (car command-line-args-left))
+ (setq command-line-args-left (cdr command-line-args-left)))
+ (error (send-string-to-terminal
+ (format "%s\n" (if (eq (car-safe err) 'error)
+ (nth 1 err) err)))
+ (save-buffers-kill-emacs)))
+ (info)))
+
;; 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.
(let ((buffer-read-only nil))
(setq Info-current-file nil
Info-current-subfile nil
+ Info-current-file-completions nil
Info-index-alternatives nil
buffer-file-name nil)
(erase-buffer)
;; of the merged dir text.
(defvar Info-dir-contents-directory nil)
+;; Record the file attributes of all the files from which we
+;; constructed Info-dir-contents.
+(defvar Info-dir-file-attributes nil)
+
;; Construct the Info directory node by merging the files named `dir'
;; from various directories. Set the *info* buffer's
;; default-directory to the first directory we actually get any text
;; from.
(defun Info-insert-dir ()
- (if Info-dir-contents
+ (if (and Info-dir-contents Info-dir-file-attributes
+ ;; Verify that none of the files we used has changed
+ ;; since we used it.
+ (eval (cons 'and
+ (mapcar '(lambda (elt)
+ (equal (cdr elt)
+ (file-attributes (car elt))))
+ Info-dir-file-attributes))))
(insert Info-dir-contents)
(let ((dirs Info-directory-list)
buffers buffer others nodes dirs-done)
;; Search the directory list for the directory file.
(while dirs
- (or (member (file-truename (expand-file-name (car dirs))) dirs-done)
- (member (directory-file-name (file-truename (expand-file-name (car dirs))))
- dirs-done)
- ;; Try several variants of specified name.
- ;; Try upcasing, appending `.info', or both.
- (let* (temp
- (buffer
- (cond
- ((progn (setq temp (expand-file-name "DIR" (car dirs)))
- (file-exists-p temp))
- (find-file-noselect temp))
- ((progn (setq temp (expand-file-name "dir" (car dirs)))
- (file-exists-p temp))
- (find-file-noselect temp))
- ((progn (setq temp (expand-file-name "DIR.INFO" (car dirs)))
- (file-exists-p temp))
- (find-file-noselect temp))
- ((progn (setq temp (expand-file-name "dir.info" (car dirs)))
- (file-exists-p temp))
- (find-file-noselect temp)))))
- (setq dirs-done
- (cons (file-truename (expand-file-name (car dirs)))
- (cons (directory-file-name
- (file-truename (expand-file-name (car dirs))))
- dirs-done)))
- (if buffer (setq buffers (cons buffer buffers)))))
+ (let ((truename (file-truename (expand-file-name (car dirs)))))
+ (or (member truename dirs-done)
+ (member (directory-file-name truename) dirs-done)
+ ;; Try several variants of specified name.
+ ;; Try upcasing, appending `.info', or both.
+ (let* (temp
+ (buffer
+ (cond
+ ((progn (setq temp (expand-file-name "DIR" (car dirs)))
+ (file-exists-p temp))
+ (find-file-noselect temp))
+ ((progn (setq temp (expand-file-name "dir" (car dirs)))
+ (file-exists-p temp))
+ (find-file-noselect temp))
+ ((progn (setq temp (expand-file-name "DIR.INFO" (car dirs)))
+ (file-exists-p temp))
+ (find-file-noselect temp))
+ ((progn (setq temp (expand-file-name "dir.info" (car dirs)))
+ (file-exists-p temp))
+ (find-file-noselect temp)))))
+ (setq dirs-done
+ (cons truename
+ (cons (directory-file-name truename)
+ dirs-done)))
+ (if buffer (setq buffers (cons buffer buffers)
+ Info-dir-file-attributes
+ (cons (cons (buffer-file-name buffer)
+ (file-attributes (buffer-file-name buffer)))
+ Info-dir-file-attributes))))))
(setq dirs (cdr dirs)))
;; Distinguish the dir file that comes with Emacs from all the
(re-search-forward (concat "^\\* " (regexp-quote nodename) ":")
end t)
(progn
- (insert "* " nodename "\n")
+ (insert "* " nodename "::" "\n")
(setq menu-items (cons nodename menu-items)))))
(setq nodes (cdr nodes))))
;; Now take each node of each of the other buffers
(setq active-expression
(read (current-buffer))))))
(point-max)))
- (if Info-enable-active-nodes (eval active-expression)))))
+ (if Info-enable-active-nodes (eval active-expression))
+ (if Info-fontify (Info-fontify-node))
+ (run-hooks 'Info-selection-hook))))
(defun Info-set-mode-line ()
(setq mode-line-buffer-identification
(defun Info-goto-node (nodename)
"Go to info node named NAME. Give just NODENAME or (FILENAME)NODENAME."
- (interactive "sGoto node: ")
+ (interactive (list (Info-read-node-name "Goto node: ")))
(let (filename)
(string-match "\\s *\\((\\s *\\([^\t)]*\\)\\s *)\\s *\\|\\)\\(.*\\)"
nodename)
(if trim (setq nodename (substring nodename 0 trim))))
(Info-find-node (if (equal filename "") nil filename)
(if (equal nodename "") "Top" nodename))))
+
+(defun Info-read-node-name (prompt &optional default)
+ (let* ((completion-ignore-case t)
+ (nodename (completing-read prompt (Info-build-node-completions))))
+ (if (equal nodename "")
+ (or default
+ (Info-read-node-name prompt))
+ nodename)))
+
+(defun Info-build-node-completions ()
+ (or Info-current-file-completions
+ (let ((compl nil))
+ (save-excursion
+ (save-restriction
+ (if (marker-buffer Info-tag-table-marker)
+ (progn
+ (set-buffer (marker-buffer Info-tag-table-marker))
+ (goto-char Info-tag-table-marker)
+ (while (re-search-forward "\nNode: \\(.*\\)\177" nil t)
+ (setq compl
+ (cons (list (buffer-substring (match-beginning 1)
+ (match-end 1)))
+ compl))))
+ (widen)
+ (goto-char (point-min))
+ (while (search-forward "\n\^_" nil t)
+ (forward-line 1)
+ (let ((beg (point)))
+ (forward-line 1)
+ (if (re-search-backward "Node: *\\([^,\n]*\\) *[,\n\t]"
+ beg t)
+ (setq compl
+ (cons (list (buffer-substring (match-beginning 1)
+ (match-end 1)))
+ compl))))))))
+ (setq Info-current-file-completions compl))))
\f
(defun Info-restore-point (hl)
"If this node has been visited, restore the point value when we left."
NAME may be an abbreviation of the reference name."
(interactive
(let ((completion-ignore-case t)
- completions default (start-point (point)) str i)
+ completions default alt-default (start-point (point)) str i bol eol)
(save-excursion
+ ;; Store end and beginning of line.
+ (end-of-line)
+ (setq eol (point))
+ (beginning-of-line)
+ (setq bol (point))
+
(goto-char (point-min))
(while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
(setq str (buffer-substring
(1- (point))))
;; See if this one should be the default.
(and (null default)
- (< (match-beginning 0) start-point)
+ (<= (match-beginning 0) start-point)
(<= start-point (point))
(setq default t))
+ ;; See if this one should be the alternate default.
+ (and (null alt-default)
+ (and (<= bol (match-beginning 0))
+ (<= (point) eol))
+ (setq alt-default t))
(setq i 0)
(while (setq i (string-match "[ \n\t]+" str i))
(setq str (concat (substring str 0 i) " "
(setq i (1+ i)))
;; Record as a completion and perhaps as default.
(if (eq default t) (setq default str))
+ (if (eq alt-default t) (setq alt-default str))
(setq completions
(cons (cons str nil)
completions))))
+ ;; If no good default was found, try an alternate.
+ (or default
+ (setq default alt-default))
+ ;; If only one cross-reference found, then make it default.
+ (if (eq (length completions) 1)
+ (setq default (car (car completions))))
(if completions
- (list (completing-read (if default
- (concat "Follow reference named: ("
- default ") ")
- "Follow reference named: ")
- completions default t))
+ (let ((input (completing-read (if default
+ (concat "Follow reference named: ("
+ default ") ")
+ "Follow reference named: ")
+ completions nil t)))
+ (list (if (equal input "")
+ default input)))
(error "No cross-references in this node"))))
(let (target beg i (str (concat "\\*note " footnotename)))
(while (setq i (string-match " " str i))
(goto-char (point-min))
(or (search-forward "\n* menu:" nil t)
(error "No menu in this node"))
- (or (re-search-forward (concat "\n* " menu-item ":") nil t)
- (re-search-forward (concat "\n* " menu-item) nil t)
+ (or (re-search-forward (concat "\n\\* " menu-item ":") nil t)
+ (re-search-forward (concat "\n\\* " menu-item) nil t)
(error "No such item in menu"))
(beginning-of-line)
(forward-char 2)
(defun Info-exit ()
"Exit Info by selecting some other buffer."
(interactive)
- (switch-to-buffer (prog1 (other-buffer (current-buffer))
- (bury-buffer (current-buffer)))))
+ (if Info-standalone
+ (save-buffers-kill-emacs)
+ (switch-to-buffer (prog1 (other-buffer (current-buffer))
+ (bury-buffer (current-buffer))))))
(defun Info-next-menu-item ()
(interactive)
(error "No previous items in menu"))
(Info-goto-node (Info-extract-menu-node-name))))
-(defmacro no-error (&rest body)
+(defmacro Info-no-error (&rest body)
(list 'condition-case nil (cons 'progn (append body '(t))) '(error nil)))
(defun Info-next-preorder ()
"Go to the next node, popping up a level if there is none."
(interactive)
- (cond ((no-error (Info-next-menu-item)) )
- ((no-error (Info-up)) (forward-line 1))
+ (cond ((looking-at "\\*note[ \n]*\\([^:]*\\):")
+ (Info-follow-reference
+ (buffer-substring (match-beginning 1) (match-end 1))))
+ ((Info-no-error (Info-next-menu-item)) )
+ ((Info-no-error (Info-up)) (forward-line 1))
(t (error "No more nodes"))))
(defun Info-last-preorder ()
"Go to the last node, popping up a level if there is none."
(interactive)
- (cond ((no-error (Info-last-menu-item)) )
- ((no-error (Info-up)) (forward-line -1))
+ (cond ((Info-no-error (Info-last-menu-item)) )
+ ((Info-no-error (Info-up)) (forward-line -1))
(t (error "No previous nodes"))))
(defun Info-scroll-up ()
(scroll-down))
)
+(defun Info-next-reference ()
+ "Move cursor to the next cross-reference or menu item in the node."
+ (interactive)
+ (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
+ (old-pt (point)))
+ (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)
+ (error "No cross references in this node")))))
+ (goto-char (match-beginning 0))
+ (if (looking-at "\\* Menu:")
+ (Info-next-reference))))
+
+(defun Info-prev-reference ()
+ "Move cursor to the previous cross-reference or menu item in the node."
+ (interactive)
+ (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
+ (old-pt (point)))
+ (or (re-search-backward pat nil t)
+ (progn
+ (goto-char (point-max))
+ (or (re-search-backward pat nil t)
+ (progn
+ (goto-char old-pt)
+ (error "No cross references in this node")))))
+ (goto-char (match-beginning 0))
+ (if (looking-at "\\* Menu:")
+ (Info-prev-reference))))
+
(defun Info-index (topic)
"Look up a string in the index for this file.
The index is defined as the first node in the top-level menu whose
(if (not (eq ?\ (setq ch (read-event))))
(progn (setq unread-command-events (list ch)) nil)
flag))
- (scroll-up)))))
+ (scroll-up)))
+ (bury-buffer "*Help*")))
\f
(defun Info-get-token (pos start all &optional errorstring)
"Return the token around POS,
(define-key Info-mode-map "." 'beginning-of-buffer)
(define-key Info-mode-map " " 'Info-scroll-up)
(define-key Info-mode-map "\C-m" 'Info-next-preorder)
+ (define-key Info-mode-map "\t" 'Info-next-reference)
+ (define-key Info-mode-map "\e\t" 'Info-prev-reference)
(define-key Info-mode-map "1" 'Info-nth-menu-item)
(define-key Info-mode-map "2" 'Info-nth-menu-item)
(define-key Info-mode-map "3" 'Info-nth-menu-item)
(define-key Info-mode-map "u" 'Info-up)
(define-key Info-mode-map "," 'Info-index-next)
(define-key Info-mode-map "\177" 'Info-scroll-down)
- (define-key Info-mode-map [mouse-3] 'Info-follow-nearest-node)
+ (define-key Info-mode-map [mouse-2] 'Info-follow-nearest-node)
)
\f
;; Info mode is suitable only for specially formatted data.
\\[Info-index-next] (comma) Move to the next match from a previous `i' command.
Moving within a node:
-\\[scroll-up] Normally, scroll forward a full screen. If the end of the buffer is
+\\[Info-scroll-up] Normally, scroll forward a full screen. If the end of the buffer is
already visible, try to go to the next menu entry, or up if there is none.
-\\[scroll-down] Normally, scroll backward. If the beginning of the buffer is
+\\[Info-scroll-down] Normally, scroll backward. If the beginning of the buffer is
already visible, try to go to the previous menu entry, or up if there is none.
\\[beginning-of-buffer] Go to beginning of node.
2, 3, 4, 5 Pick second ... fifth item in node's menu.
\\[Info-goto-node] Move to node specified by name.
You may include a filename as well, as (FILENAME)NODENAME.
+\\[universal-argument] \\[info] Move to new Info file with completion.
\\[Info-search] Search through this Info file for specified regexp,
and select the node in which the next occurrence is found.
\\[Info-next-preorder] Next-preorder; that is, try to go to the next menu item,
and if that fails try to move up, and if that fails, tell user
- he/she is done reading."
+ he/she is done reading.
+\\[Info-next-reference] Move cursor to next cross-reference or menu item.
+\\[Info-prev-reference] Move cursor to previous cross-reference or menu item."
(kill-all-local-variables)
(setq major-mode 'Info-mode)
(setq mode-name "Info")
(make-local-variable 'Info-tag-table-marker)
(make-local-variable 'Info-history)
(make-local-variable 'Info-index-alternatives)
+ (if (fboundp 'make-face)
+ (progn
+ (make-face 'info-node)
+ (make-face 'info-menu-5)
+ (make-face 'info-xref)
+ (or (face-differs-from-default-p 'info-node)
+ (if (face-differs-from-default-p 'bold-italic)
+ (copy-face 'bold-italic 'info-node)
+ (copy-face 'bold 'info-node)))
+ (or (face-differs-from-default-p 'info-menu-5)
+ (set-face-underline-p 'info-menu-5 t))
+ (or (face-differs-from-default-p 'info-xref)
+ (copy-face 'bold 'info-xref)))
+ (setq Info-fontify nil))
(Info-set-mode-line)
(run-hooks 'Info-mode-hook))
;; Make mode line update.
(set-buffer-modified-p (buffer-modified-p))
(message (substitute-command-keys
- "Editing: Type \\<Info-mode-map>\\[Info-cease-edit] to return to info")))
+ "Editing: Type \\<Info-edit-map>\\[Info-cease-edit] to return to info")))
(defun Info-cease-edit ()
"Finish editing Info node; switch back to Info proper."
;;;###autoload
(defun Info-goto-emacs-command-node (command)
- "Go to the Info node in the Emacs manual for command COMMAND."
+ "Go to the Info node in the Emacs manual for command COMMAND.
+The command is found by looking up in Emacs manual's Command Index."
(interactive "CFind documentation for command: ")
(or (commandp command)
(signal 'wrong-type-argument (list 'commandp command)))
;;;###autoload
(defun Info-goto-emacs-key-command-node (key)
"Go to the Info node in the Emacs manual the command bound to KEY, a string.
-Interactively, if the binding is execute-extended-command, a command is read."
+Interactively, if the binding is execute-extended-command, a command is read.
+The command is found by looking up in Emacs manual's Command Index."
(interactive "kFind documentation for key:")
(let ((command (key-binding key)))
(cond ((null command)
(read-command "Find documentation for command: ")))
(t
(Info-goto-emacs-command-node command)))))
+\f
+(defun Info-fontify-node ()
+ (save-excursion
+ (let ((buffer-read-only nil))
+ (goto-char (point-min))
+ (if (looking-at "^File: [^,: \t]+,?[ \t]+")
+ (progn
+ (goto-char (match-end 0))
+ (while
+ (looking-at "[ \t]*[^:, \t\n]+:[ \t]+\\([^:,\t\n]+\\),?")
+ (goto-char (match-end 0))
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'face 'info-xref))))
+ (goto-char (point-min))
+ (while (re-search-forward "\\*Note[ \n\t]*\\([^:]*\\):" nil t)
+ (if (= (char-after (1- (match-beginning 0))) ?\") ; hack
+ nil
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'face 'info-xref)))
+ (goto-char (point-min))
+ (if (and (search-forward "\n* Menu:" nil t)
+ (not (string-match "\\<Index\\>" Info-current-node))
+ ;; Don't take time to annotate huge menus
+ (< (- (point-max) (point)) 10000))
+ (let ((n 0))
+ (while (re-search-forward "^\\* \\([^:\t\n]*\\):" nil t)
+ (setq n (1+ n))
+ (if (memq n '(5 9)) ; visual aids to help with 1-9 keys
+ (put-text-property (match-beginning 0)
+ (1+ (match-beginning 0))
+ 'face 'info-menu-5))
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'face 'info-node))))
+ (set-buffer-modified-p nil))))
(provide 'info)