]> code.delx.au - gnu-emacs/blobdiff - lisp/info.el
(Info-directory-list): Doc fix.
[gnu-emacs] / lisp / info.el
index 043a4402ce019c91e7e67d362df4022dbf2491c2..993a31c963ecea705331598de93792e952ab75a2 100644 (file)
@@ -1,6 +1,6 @@
 ;;; info.el --- info package for Emacs.
 
-;; Copyright (C) 1985, 1986, 1992 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1992, 1993, 1994 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: help
@@ -47,8 +47,12 @@ The Lisp code is executed when the node is selected.")
 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)
@@ -58,11 +62,18 @@ in paths.el.")
                  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'
-if there is no INFOPATH variable in the environment.")
+if there is no INFOPATH variable in the environment.
+The last element of `Info-default-directory-list' is the directory
+where Emacs's installs the Info files that come with it.
+If Emacs finds it has been installed elsewhere, or not yet installed,
+it replaces that element with the directory that its Info files are in.")
 
 (defvar Info-current-file nil
   "Info file that Info is now looking at, or nil.")
@@ -78,42 +89,62 @@ or nil if current info file is not split into subfiles.")
   "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)
+(defvar Info-standalone nil
+  "Non-nil if Emacs was started solely as an Info browser.")
+
+(defvar Info-suffix-list '( (".info.Z"  . "uncompress")
+                           (".info.Y"  . "unyabba")
+                           (".info.gz" . "gunzip")
+                           (".info.z"  . "gunzip")
                            (".info"    . nil)
                            (".Z"       . "uncompress")
                            (".Y"       . "unyabba")
                            (".gz"      . "gunzip")
                            (".z"       . "gunzip")
-                           (".info.Z"  . "uncompress")
-                           (".info.Y"  . "unyabba")
-                           (".info.gz" . "gunzip")
-                           (".info.z"  . "gunzip"))
+                           (""         . nil))
   "List of file name suffixes and associated decoding commands.
 Each entry should be (SUFFIX . STRING); the file is given to
-the command as standard input.  If STRING is nil, no decoding is done.")
+the command as standard input.  If STRING is nil, no decoding is done.
+Because the SUFFIXes are tried in order, the empty string should
+be last in the list.")
 
 (defun info-insert-file-contents (filename &optional visit)
   "Insert the contents of an info file in the current buffer.
 Do the right thing if the file has been compressed or zipped."
-  (if (null (catch 'ok
-             (mapcar
-              (function
-               (lambda (x)
-                 (let ((compressed (concat filename (car x))))
-                   (if (file-exists-p compressed)
-                       (progn
-                         (insert-file-contents compressed visit)
-                         (if (cdr x)
-                             (let ((buffer-read-only nil))
-                               (shell-command-on-region
-                                (point-min) (point-max) (cdr x) t)))
-                         (throw 'ok t))))))
-              Info-suffix-list)
-             nil))
-      (error "Can't find %s or any compressed version of it!" filename)))
+  (let ((tail Info-suffix-list)
+       fullname decoder)
+    (if (file-exists-p filename)
+       (progn
+         (while (and tail
+                     (not (string-match
+                           (concat (regexp-quote (car (car tail))) "$")
+                           filename)))
+           (setq tail (cdr tail)))
+         (setq fullname filename
+               decoder (cdr (car tail))))
+      (while (and tail
+                 (not (file-exists-p (concat filename (car (car tail))))))
+       (setq tail (cdr tail)))
+      (setq fullname (concat filename (car (car tail)))
+           decoder (cdr (car tail)))
+      ;; check for conflict with jka-compr
+      (if (and (featurep 'jka-compr)
+              (jka-compr-installed-p)
+              (jka-compr-get-compression-info (concat filename
+                                                      (car (car tail)))))
+         (setq decoder nil))
+      (or tail
+         (error "Can't find %s or any compressed version of it!" filename)))
+    (insert-file-contents fullname visit)
+    (if decoder
+       (let ((buffer-read-only nil))
+         (shell-command-on-region (point-min) (point-max) decoder t)))))
 
 ;;;###autoload
 (defun info (&optional file)
@@ -131,6 +162,24 @@ to read a file name from the minibuffer."
        (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.
@@ -190,6 +239,7 @@ to read a file name from the minibuffer."
            (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)
@@ -264,7 +314,7 @@ to read a file name from the minibuffer."
          (Info-select-node)))
     ;; If we did not finish finding the specified node,
     ;; go back to the previous one.
-    (or Info-current-node no-going-back
+    (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)
@@ -279,46 +329,62 @@ to read a file name from the minibuffer."
 ;; 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)))
 
+      (or buffers
+         (error "Can't find the info directory node"))
       ;; Distinguish the dir file that comes with Emacs from all the
       ;; others.  Yes, that is really what this is supposed to do.
       ;; If it doesn't work, fix it.
@@ -345,10 +411,10 @@ to read a file name from the minibuffer."
              (let (beg nodename end)
                (forward-line 1)
                (setq beg (point))
-               (search-backward "\n\1f")
+               (search-backward "\n\^_")
                (search-forward "Node: ")
                (setq nodename (Info-following-node-name))
-               (search-forward "\n\1f" nil 'move)
+               (search-forward "\n\^_" nil 'move)
                (beginning-of-line)
                (setq end (point))
                (setq nodes (cons (list nodename other beg end) nodes))))))
@@ -359,15 +425,18 @@ to read a file name from the minibuffer."
       (let ((menu-items '("top"))
            (nodes nodes)
            (case-fold-search t)
-           (end (save-excursion (search-forward "\1f" nil t) (point))))
+           (end (save-excursion (search-forward "\^_" nil t) (point))))
        (while nodes
          (let ((nodename (car (car nodes))))
-           (or (member (downcase nodename) menu-items)
-               (re-search-forward (concat "^\\* " (regexp-quote nodename) ":")
-                                  end t)
-               (progn
-                 (insert "* " nodename "\n")
-                 (setq menu-items (cons nodename menu-items)))))
+           (save-excursion
+             (or (member (downcase nodename) menu-items)
+                 (re-search-forward (concat "^\\* "
+                                            (regexp-quote nodename)
+                                            "::")
+                                    end t)
+                 (progn
+                   (insert "* " nodename "::" "\n")
+                   (setq menu-items (cons nodename menu-items))))))
          (setq nodes (cdr nodes))))
       ;; Now take each node of each of the other buffers
       ;; and merge it into the main buffer.
@@ -375,16 +444,16 @@ to read a file name from the minibuffer."
        (let ((nodename (car (car nodes))))
          (goto-char (point-min))
          ;; Find the like-named node in the main buffer.
-         (if (re-search-forward (concat "\n\1f.*\n.*Node: "
+         (if (re-search-forward (concat "\n\^_.*\n.*Node: "
                                         (regexp-quote nodename)
                                         "[,\n\t]")
                                 nil t)
              (progn
-               (search-forward "\n\1f" nil 'move)
+               (search-forward "\n\^_" nil 'move)
                (beginning-of-line))
            ;; If none exists, add one.
            (goto-char (point-max))
-           (insert "\1f\nFile: dir\tnode: " nodename "\n\n* Menu:\n\n"))
+           (insert "\^_\nFile: dir\tnode: " nodename "\n\n* Menu:\n\n"))
          ;; Merge the text from the other buffer's menu
          ;; into the menu in the like-named node in the main buffer.
          (apply 'insert-buffer-substring (cdr (car nodes)))
@@ -460,7 +529,9 @@ to read a file name from the minibuffer."
                                  (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
@@ -477,7 +548,7 @@ to read a file name from the minibuffer."
 
 (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)
@@ -491,14 +562,53 @@ to read a file name from the minibuffer."
       (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))
+                 (widen)
+                 (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."
-  (if hl
-      (if (and (equal (nth 0 (car hl)) Info-current-file)
-              (equal (nth 1 (car hl)) Info-current-node))
+  (while hl
+    (if (and (equal (nth 0 (car hl)) Info-current-file)
+            (equal (nth 1 (car hl)) Info-current-node))
+       (progn
          (goto-char (nth 2 (car hl)))
-       (Info-restore-point (cdr hl)))))
+         (setq hl nil))                ;terminate the while at next iter
+      (setq hl (cdr hl)))))
 \f
 (defvar Info-last-search nil
   "Default regexp for \\<Info-mode-map>\\[Info-search] command to search for.")
@@ -638,8 +748,14 @@ to read a file name from the minibuffer."
 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
@@ -647,9 +763,14 @@ NAME may be an abbreviation of the reference name."
                    (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) " "
@@ -657,15 +778,24 @@ NAME may be an abbreviation of the reference name."
           (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))
@@ -761,8 +891,8 @@ Completion is allowed, and the menu item point is on is the default."
     (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)
@@ -861,8 +991,10 @@ N is the digit argument used to invoke this command."
 (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)
@@ -882,21 +1014,21 @@ N is the digit argument used to invoke this command."
        (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 ((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 ()
@@ -904,16 +1036,47 @@ N is the digit argument used to invoke this command."
   (interactive)
   (if (pos-visible-in-window-p (point-max))
       (Info-next-preorder)
-      (scroll-up))
-  )
+    (scroll-up)))
 
 (defun Info-scroll-down ()
   "Read the previous screen.  If start of buffer is visible, go to last entry."
   (interactive)
   (if (pos-visible-in-window-p (point-min))
       (Info-last-preorder)
-      (scroll-down))
-  )
+    (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.
@@ -1036,7 +1199,8 @@ Give a blank topic name to go to the Index node itself."
                    (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,
@@ -1069,19 +1233,35 @@ SIG optional fourth argument, controls action on no match
              (t
               (error "No %s around position %d" errorstring pos)))))))
 
-(defun Info-follow-nearest-node (click)
+(defun Info-mouse-follow-nearest-node (click)
   "\\<Info-mode-map>Follow a node reference near point.
 Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where you click.
-At end of the node's text, moves to the next node."
+At end of the node's text, moves to the next node, or up if none."
   (interactive "e")
   (let* ((start (event-start click))
         (window (car start))
         (pos (car (cdr start))))
     (select-window window)
     (goto-char pos))
+  (and (not (Info-try-follow-nearest-node))
+       (save-excursion (forward-line 1) (eobp))
+       (Info-next-preorder)))
+
+(defun Info-follow-nearest-node ()
+  "\\<Info-mode-map>Follow a node reference near point.
+Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where point is.
+If no reference to follow, moves to the next node, or up if none."
+  (interactive)
+  (or (Info-try-follow-nearest-node)
+      (Info-next-preorder)))
+
+;; Common subroutine.
+(defun Info-try-follow-nearest-node ()
+  "Follow a node reference near point.  Return non-nil if successful."
   (let (node)
     (cond
-     ((setq node (Info-get-token (point) "\\*note[ \n]" "\\*note[ \n]\\([^:]*\\):"))
+     ((setq node (Info-get-token (point) "\\*note[ \n]"
+                                "\\*note[ \n]\\([^:]*\\):"))
       (Info-follow-reference node))
      ((setq node (Info-get-token (point) "\\* " "\\* \\([^:]*\\)::"))
       (Info-goto-node node))
@@ -1094,10 +1274,8 @@ At end of the node's text, moves to the next node."
      ((setq node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)"))
       (Info-goto-node "Top"))
      ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)"))
-      (Info-goto-node node))
-     ((save-excursion (forward-line 1) (eobp))
-      (Info-next)))
-    ))
+      (Info-goto-node node)))
+    node))
 \f
 (defvar Info-mode-map nil
   "Keymap containing Info commands.")
@@ -1107,7 +1285,9 @@ At end of the node's text, moves to the next node."
   (suppress-keymap Info-mode-map)
   (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 "\C-m" 'Info-follow-nearest-node)
+  (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)
@@ -1140,7 +1320,7 @@ At end of the node's text, moves to the next node."
   (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-2] 'Info-follow-nearest-node)
+  (define-key Info-mode-map [mouse-2] 'Info-mouse-follow-nearest-node)
   )
 \f
 ;; Info mode is suitable only for specially formatted data.
@@ -1168,9 +1348,9 @@ Selecting other nodes:
 \\[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.  
 
@@ -1181,11 +1361,14 @@ Advanced commands:
 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")
@@ -1200,6 +1383,20 @@ Advanced commands:
   (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))
 
@@ -1234,7 +1431,7 @@ Allowed only if variable `Info-enable-edit' is non-nil."
   ;; 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."
@@ -1278,7 +1475,8 @@ The locations are of the format used in Info-history, i.e.
 
 ;;;###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)))
@@ -1298,16 +1496,17 @@ The locations are of the format used in Info-history, i.e.
                ;; the history.
                (setq Info-history (nconc (cdr where) Info-history))
                (message (substitute-command-keys
-                         "Found %d other entr%.  Use \\[Info-last] to see %s."
-                       (1- num-matches)
-                       (if (> num-matches 2) "ies" "y")
-                       (if (> num-matches 2) "them" "it"))))))
+                         "Found %d other entr%s.  Use \\[Info-last] to see %s.")
+                        (1- num-matches)
+                        (if (> num-matches 2) "ies" "y")
+                        (if (> num-matches 2) "them" "it")))))
       (error "Couldn't find documentation for %s." 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)
@@ -1318,6 +1517,46 @@ Interactively, if the binding is execute-extended-command, a command is read."
            (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)
+             (put-text-property (match-beginning 1) (match-end 1)
+                                'mouse-face 'highlight))))
+      (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)
+         (put-text-property (match-beginning 1) (match-end 1)
+                            'mouse-face 'highlight)))
+      (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)) 30000))
+         (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)
+             (put-text-property (match-beginning 1) (match-end 1)
+                                'mouse-face 'highlight))))
+      (set-buffer-modified-p nil))))
 
 (provide 'info)