;;; Code:
+(eval-when-compile (require 'jka-compr))
+
(defgroup info nil
"Info subsystem"
:group 'help
:group 'info)
(defface info-node
- '((t (:bold t :italic t)))
+ '((((class color)) (:foreground "brown" :bold t :italic t))
+ (t (:bold t :italic t)))
"Face for Info node names."
:group 'info)
(defface info-menu-5
- '((t (:underline t)))
+ '((((class color)) (:foreground "red1"))
+ (t (:underline t)))
"Face for the fifth and tenth `*' in an Info menu."
:group 'info)
(defface info-xref
- '((t (:bold t)))
+ '((((class color)) (:foreground "magenta4" :bold t))
+ (t (:bold t)))
"Face for Info cross-references."
:group 'info)
:type 'integer
:group 'info)
-(defvar Info-directory-list
- (let ((path (getenv "INFOPATH"))
- (source (expand-file-name "info/" source-directory))
- (sibling (if installation-directory
- (expand-file-name "info/" installation-directory)))
- alternative)
- (if path
- (split-string path (regexp-quote path-separator))
- (if (and sibling (file-exists-p sibling))
- (setq alternative sibling) ; uninstalled, Emacs builddir != srcdir
- (setq alternative source)) ; uninstalled, builddir != srcdir
- (if (or (member alternative Info-default-directory-list)
- (not (file-exists-p alternative))
- ;; On DOS/NT, we use movable executables always,
- ;; and we must always find the Info dir at run time.
- (if (or (eq system-type 'ms-dos) (eq system-type 'windows-nt))
- nil
- ;; Use invocation-directory for Info only if we used it for
- ;; exec-directory also.
- (not (string= exec-directory
- (expand-file-name "lib-src/"
- installation-directory)))))
- Info-default-directory-list
- ;; `alternative' contains the Info files that came with this
- ;; version, so we should look there first. `Info-insert-dir'
- ;; currently expects to find `alternative' first on the list.
- (cons alternative
- (reverse (cdr (reverse Info-default-directory-list)))))))
+(defvar Info-directory-list nil
"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'
(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.
-It doesn't contain directory names or file name extensions added by Info.")
+It doesn't contain directory names or file name extensions added by Info.
+Can also be t when using `Info-on-current-buffer'.")
(defvar Info-current-subfile nil
"Info subfile that is actually in the *info* buffer now,
(defvar Info-standalone nil
"Non-nil if Emacs was started solely as an Info browser.")
-
+\f
(defvar Info-suffix-list
;; The MS-DOS list should work both when long file names are
;; supported (Windows 9X), and when only 8+3 file names are available.
(if (eq system-type 'ms-dos)
'( (".gz" . "gunzip")
(".z" . "gunzip")
+ (".bz2" . "bzip2 -dc")
(".inz" . "gunzip")
(".igz" . "gunzip")
(".info.Z" . "gunzip")
(".info.Y". "unyabba")
(".info.gz". "gunzip")
(".info.z". "gunzip")
+ (".info.bz2" . "bzip2 -dc")
(".info". nil)
("-info.Z". "uncompress")
("-info.Y". "unyabba")
("-info.gz". "gunzip")
+ ("-info.bz2" . "bzip2 -dc")
("-info.z". "gunzip")
("-info". nil)
("/index.Z". "uncompress")
("/index.Y". "unyabba")
("/index.gz". "gunzip")
("/index.z". "gunzip")
+ ("/index.bz2". "bzip2 -dc")
("/index". nil)
(".Z". "uncompress")
(".Y". "unyabba")
(".gz". "gunzip")
(".z". "gunzip")
+ (".bz2" . "bzip2 -dc")
("". nil)))
"List of file name suffixes and associated decoding commands.
Each entry should be (SUFFIX . STRING); the file is given to
default-directory)))
(call-process-region (point-min) (point-max) decoder t t)))
(insert-file-contents fullname visit))))
+\f
+;; Initialize Info-directory-list, if that hasn't been done yet.
+(defun info-initialize ()
+ (unless Info-directory-list
+ (let ((path (getenv "INFOPATH"))
+ (source (expand-file-name "info/" source-directory))
+ (sibling (if installation-directory
+ (expand-file-name "info/" installation-directory)))
+ alternative)
+ (setq Info-directory-list
+ (if path
+ (split-string path (regexp-quote path-separator))
+ (if (and sibling (file-exists-p sibling))
+ ;; Uninstalled, Emacs builddir != srcdir.
+ (setq alternative sibling)
+ ;; Uninstalled, builddir == srcdir
+ (setq alternative source))
+ (if (or (member alternative Info-default-directory-list)
+ ;; On DOS/NT, we use movable executables always,
+ ;; and we must always find the Info dir at run time.
+ (if (memq system-type '(ms-dos windows-nt))
+ nil
+ ;; Use invocation-directory for Info
+ ;; only if we used it for exec-directory also.
+ (not (string= exec-directory
+ (expand-file-name "lib-src/"
+ installation-directory))))
+ (not (file-exists-p alternative)))
+ Info-default-directory-list
+ ;; `alternative' contains the Info files that came with this
+ ;; version, so we should look there first. `Info-insert-dir'
+ ;; currently expects to find `alternative' first on the list.
+ (cons alternative
+ (reverse (cdr (reverse Info-default-directory-list))))))))))
;;;###autoload
(defun info-other-window (&optional file)
"Enter Info, the documentation browser.
Optional argument FILE specifies the file to examine;
the default is the top-level directory of Info.
+Called from a program, FILE may specify an Info node of the form
+`(FILENAME)NODENAME'.
In interactive use, a prefix argument directs this command
to read a file name from the minibuffer.
(nth 1 err) err)))
(save-buffers-kill-emacs)))
(info)))
-
+\f
;; See if the the accessible portion of the buffer begins with a node
;; delimiter, and the node header line which follows matches REGEXP.
;; Typically, this test will be followed by a loop that examines the
;; no-going-back is non-nil if recovering from an error in this function;
;; it says do not attempt further (recursive) error recovery.
(defun Info-find-node (filename nodename &optional no-going-back)
+ (info-initialize)
;; Convert filename to lower case if not found as specified.
;; Expand it.
- (if filename
+ (if (stringp filename)
(let (temp temp-downcase found)
(setq filename (substitute-in-file-name filename))
(if (string= (downcase filename) "dir")
Info-history)))
;; Go into info buffer.
(or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
+ (Info-find-node-2 filename nodename no-going-back))
+
+(defun Info-on-current-buffer (&optional nodename)
+ "Use the `Info-mode' to browse the current info buffer.
+If a prefix arg is provided, it queries for the NODENAME which
+else defaults to `Top'."
+ (interactive
+ (list (if current-prefix-arg
+ (completing-read "Node name: " (Info-build-node-completions)
+ nil t "Top")
+ "Top")))
+ (Info-mode)
+ (set (make-local-variable 'Info-current-file) t)
+ (Info-find-node-2 nil nodename))
+
+(defun Info-find-node-2 (filename nodename &optional no-going-back)
(buffer-disable-undo (current-buffer))
(or (eq major-mode 'Info-mode)
(Info-mode))
(goto-char (setq anchorpos guesspos))
;; Else we may have a node, which we search for:
- (goto-char (max (point-min)
- (- (byte-to-position guesspos) 1000)))
+ (let ((guesschar
+ (or (byte-to-position guesspos)
+ (if (< (position-bytes (point-max)) guesspos)
+ (point-max)
+ (point-min)))))
+ (goto-char (max (point-min)
+ (- guesschar 1000))))
;; Now search from our advised position
;; (or from beg of buffer)
;; to find the actual node.
(concat
" Info: ("
(if Info-current-file
- (file-name-nondirectory Info-current-file)
+ (file-name-nondirectory (if (stringp Info-current-file)
+ Info-current-file
+ (or buffer-file-name "")))
"")
")"
(or Info-current-node ""))))
;; Go to an info node specified with a filename-and-nodename string
;; of the sort that is found in pointers in nodes.
-(defun Info-goto-node (nodename)
- "Go to info node named NAME. Give just NODENAME or (FILENAME)NODENAME."
- (interactive (list (Info-read-node-name "Goto node: ")))
+(defun Info-goto-node (nodename &optional fork)
+ "Go to info node named NAME. Give just NODENAME or (FILENAME)NODENAME.
+If FORK is non-nil, show the node in a new info buffer.
+If FORK is a string, it is the name to use for the new buffer."
+ (interactive (list (Info-read-node-name "Goto node: ") current-prefix-arg))
+ (info-initialize)
+ (if fork
+ (set-buffer
+ (clone-buffer (concat "*info-" (if (stringp fork) fork nodename) "*") t)))
(let (filename)
(string-match "\\s *\\((\\s *\\([^\t)]*\\)\\s *)\\s *\\|\\)\\(.*\\)"
nodename)
(goto-char marker)
(while (re-search-forward "\nNode: \\(.*\\)\177" nil t)
(setq compl
- (cons (list (buffer-substring (match-beginning 1)
- (match-end 1)))
+ (cons (list (match-string-no-properties 1))
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 (buffer-substring (match-beginning 1)
- (match-end 1)))))
+ (setq compl (list (match-string-no-properties 1))))
;; Now for the rest of the nodes.
(while (search-forward "\n\^_" nil t)
(forward-line 1)
(forward-line 1)
(if (re-search-backward node-regexp beg t)
(setq compl
- (cons (list (buffer-substring (match-beginning 1)
- (match-end 1)))
+ (cons (list (match-string-no-properties 1))
compl))))))))
+ (setq compl (cons '("*") compl))
(setq Info-current-file-completions compl))))
\f
(defun Info-restore-point (hl)
(re-search-forward "\\(^.*\\): [0-9]+$")
(goto-char (+ (match-end 1) 2))
(setq list (cons (cons (read (current-buffer))
- (buffer-substring
- (match-beginning 1) (match-end 1)))
+ (match-string-no-properties 1))
list))
(goto-char (1+ (match-end 0))))
(setq list (nreverse list)
If SAME-FILE is non-nil, do not move to a different Info file."
(interactive)
(let ((node (Info-extract-pointer "up")))
- (and same-file
+ (and (or same-file (not (stringp Info-current-file)))
(string-match "^(" node)
(error "Up node is in another Info file"))
(Info-goto-node node))
(goto-char (point-min))
(while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
- (setq str (buffer-substring
+ (setq str (buffer-substring-no-properties
(match-beginning 1)
(1- (point))))
;; See if this one should be the default.
(goto-char (point-min))
(search-forward "\n* Menu:")
(while (re-search-forward pattern nil t)
- (setq completions (cons (cons (format "%s"
- (buffer-substring
- (match-beginning 1)
- (match-end 1)))
- (match-beginning 1))
- completions))))
+ (setq completions
+ (cons (cons (match-string-no-properties 1)
+ (match-beginning 1))
+ completions))))
(try-completion string completions predicate)))
((eq action t)
(let (completions
(goto-char (point-min))
(search-forward "\n* Menu:")
(while (re-search-forward pattern nil t)
- (setq completions (cons (cons (format "%s"
- (buffer-substring
- (match-beginning 1)
- (match-end 1)))
- (match-beginning 1))
+ (setq completions (cons (cons
+ (match-string-no-properties 1)
+ (match-beginning 1))
completions))))
(all-completions string completions predicate)))
(t
nil t))))))
-(defun Info-menu (menu-item)
+(defun Info-menu (menu-item &optional fork)
"Go to node for menu item named (or abbreviated) NAME.
Completion is allowed, and the menu item point is on is the default."
(interactive
(goto-char p)
(end-of-line)
(if (re-search-backward "\n\\* +\\([^:\t\n]*\\):" beg t)
- (setq default (format "%s" (buffer-substring
- (match-beginning 1)
- (match-end 1))))))))
+ (setq default (match-string-no-properties 1))))))
(let ((item nil))
(while (null item)
(setq item (let ((completion-ignore-case t)
(setq item default)
;; ask again
(setq item nil))))
- (list item))))
+ (list item current-prefix-arg))))
;; there is a problem here in that if several menu items have the same
;; name you can only go to the node of the first with this command.
- (Info-goto-node (Info-extract-menu-item menu-item)))
+ (Info-goto-node (Info-extract-menu-item menu-item) (if fork menu-item)))
(defun Info-extract-menu-item (menu-item)
(setq menu-item (regexp-quote menu-item))
(goto-char (point-min))
(while (re-search-forward pattern nil t)
(setq matches
- (cons (list (buffer-substring (match-beginning 1)
- (match-end 1))
- (buffer-substring (match-beginning 2)
- (match-end 2))
+ (cons (list (match-string-no-properties 1)
+ (match-string-no-properties 2)
Info-current-node
(string-to-int (concat "0"
- (buffer-substring
- (match-beginning 3)
- (match-end 3)))))
+ (match-string 3))))
matches)))
(and (setq node (Info-extract-pointer "next" t))
(string-match "\\<Index\\>" node)))
(> (match-end 0) pos))))))
(if (and found (<= (match-beginning 0) pos)
(> (match-end 0) pos))
- (buffer-substring (match-beginning 1) (match-end 1))
+ (match-string-no-properties 1)
(cond ((null errorstring)
nil)
((eq errorstring t)
(define-key Info-mode-map "s" 'Info-search)
;; For consistency with Rmail.
(define-key Info-mode-map "\M-s" 'Info-search)
+ (define-key Info-mode-map "\M-n" 'clone-buffer)
(define-key Info-mode-map "t" 'Info-top-node)
(define-key Info-mode-map "u" 'Info-up)
(define-key Info-mode-map "," 'Info-index-next)
;; This is for the sake of the invisible text we use handling titles.
(make-local-variable 'line-move-ignore-invisible)
(setq line-move-ignore-invisible t)
+ (add-hook (make-local-hook 'clone-buffer-hook) 'Info-clone-buffer-hook nil t)
(Info-set-mode-line)
(run-hooks 'Info-mode-hook))
+(defun Info-clone-buffer-hook ()
+ (when (bufferp Info-tag-table-buffer)
+ (setq Info-tag-table-buffer
+ (with-current-buffer Info-tag-table-buffer (clone-buffer)))
+ (let ((m Info-tag-table-marker))
+ (when (and (markerp m) (marker-position m))
+ (setq Info-tag-table-marker
+ (with-current-buffer Info-tag-table-buffer
+ (copy-marker (marker-position m))))))))
+
(defvar Info-edit-map nil
"Local keymap used within `e' command of Info.")
(if Info-edit-map
(message "Tags may have changed. Use Info-tagify if necessary")))
\f
(defvar Info-file-list-for-emacs
- '("ediff" "forms" "gnus" "info" ("mh" . "mh-e") "sc")
+ '("ediff" "forms" "gnus" "info" ("mh" . "mh-e") "sc" "message"
+ ("dired" . "dired-x") ("c" . "ccmode") "viper")
"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.
(goto-char (point-max))
(while (re-search-backward cmd-desc nil t)
(setq where (cons (list Info-current-file
- (buffer-substring
- (match-beginning 1)
- (match-end 1))
+ (match-string-no-properties 1)
0)
where)))
where)))