2000-05-31 Karl M. Hegbloom <karlheg@debian.org>
[bpt/emacs.git] / lisp / info.el
index 80d1c3a..dc9a7b7 100644 (file)
@@ -61,7 +61,8 @@ The Lisp code is executed when the node is selected.")
   :group 'info)
 
 (defface info-node
-  '((((class color)) (:foreground "brown" :bold t :italic t))
+  '((((class color) (background light)) (:foreground "brown" :bold t :italic t))
+    (((class color) (background dark)) (:foreground "white" :bold t :italic t))
     (t (:bold t :italic t)))
   "Face for Info node names."
   :group 'info)
@@ -69,11 +70,12 @@ The Lisp code is executed when the node is selected.")
 (defface info-menu-5
   '((((class color)) (:foreground "red1"))
     (t (:underline t)))
-  "Face for the fifth and tenth `*' in an Info menu."
+  "Face for the fifth and nineth `*' in an Info menu."
   :group 'info)
 
 (defface info-xref
-  '((((class color)) (:foreground "magenta4" :bold t))
+  '((((class color) (background light)) (:foreground "magenta4" :bold t))
+    (((class color) (background dark)) (:foreground "cyan" :bold t))
     (t (:bold t)))
   "Face for Info cross-references."
   :group 'info)
@@ -83,18 +85,50 @@ The Lisp code is executed when the node is selected.")
   :type 'integer
   :group 'info)
 
+(defcustom Info-use-header-line t
+  "*Non-nil means to put the beginning-of-node links in an emacs header-line.
+A header-line does not scroll with the rest of the buffer."
+  :type 'boolean
+  :group 'info)
+
+(defface info-header-xref
+  '((t (:inherit info-xref)))
+  "Face for Info cross-references in a node header."
+  :group 'info)
+
+(defface info-header-node
+  '((t (:inherit info-node)))
+  "Face for Info nodes in a node header."
+  :group 'info)
+
 (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'
 if there is no INFOPATH variable in the environment.
-The last element of `Info-default-directory-list' is the directory
-where Emacs installs the Info files that come with it.
+
+When `Info-directory-list' is initialized from the value of
+`Info-default-directory-list', and Emacs is installed in one of the
+standard directories, the directory of Info files that come with Emacs
+is put last (so that local Info files override standard ones).
+
+When `Info-directory-list' is initialized from the value of
+`Info-default-directory-list', and Emacs is not installed in one
+of the standard directories, the first element of the resulting
+list is the directory where Emacs installs the Info files that
+come with it.  This is so that Emacs's own manual, which suits the
+version of Emacs you are using, will always be found first.  This
+is useful when you install an experimental version of Emacs without
+removing the standard installation.
+
+If you want to override the order of directories in
+`Info-default-directory-list', set INFOPATH in the environment.
 
 If you run the Emacs executable from the `src' directory in the Emacs
-source tree, the `info' directory in the source tree is used as the last
-element, in place of the installation Info directory.  This is useful
-when you run a version of Emacs without installing it.")
+source tree, and INFOPATH is not defined, the `info' directory in the
+source tree is used as the first element of `Info-directory-list', in
+place of the installation Info directory.  This is useful when you run
+a version of Emacs without installing it.")
 
 (defcustom Info-additional-directory-list nil
   "List of additional directories to search for Info documentation files.
@@ -102,6 +136,19 @@ These directories are not searched for merging the `dir' file."
   :type '(repeat directory)
   :group 'info)
 
+(defcustom Info-scroll-prefer-subnodes t
+  "*If non-nil, \\<Info-mode-map>\\[Info-scroll-up] in a menu visits subnodes.
+If this is non-nil, and you scroll far enough in a node that its menu
+appears on the screen, the next \\<Info-mode-map>\\[Info-scroll-up]
+moves to a subnode indicated by the following menu item.  This means
+that you visit a subnode before getting to the end of the menu.
+
+Setting this option to nil results in behavior similar to the stand-alone
+Info reader program, which visits the first subnode from the menu only
+when you hit the end of the current node."
+  :type 'boolean
+  :group 'info)
+
 (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.
@@ -137,7 +184,7 @@ Marker points nowhere if file has no tag table.")
   (if (eq system-type 'ms-dos)
       '( (".gz"      . "gunzip")
         (".z"       . "gunzip")
-        (".bz2"     . "bzip2 -dc")
+        (".bz2"     . ("bzip2" "-dc"))
         (".inz"     . "gunzip")
         (".igz"     . "gunzip")
         (".info.Z"  . "gunzip")
@@ -155,29 +202,34 @@ Marker points nowhere if file has no tag table.")
        (".info.Y".    "unyabba")
        (".info.gz".   "gunzip")
        (".info.z".    "gunzip")
-       (".info.bz2" . "bzip2 -dc")
+       (".info.bz2" . ("bzip2" "-dc"))
        (".info".      nil)
        ("-info.Z".   "uncompress")
        ("-info.Y".   "unyabba")
        ("-info.gz".  "gunzip")
-       ("-info.bz2" . "bzip2 -dc")
+       ("-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.bz2". ("bzip2" "-dc"))
        ("/index".     nil)
        (".Z".         "uncompress")
        (".Y".         "unyabba")
        (".gz".        "gunzip")
        (".z".         "gunzip")
-       (".bz2" .      "bzip2 -dc")
+       (".bz2" .      ("bzip2" "-dc"))
        ("".           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.
+
+STRING may be a list of strings.  In that case, the first element is
+the command name, and the rest are arguments to that command.
+
+If STRING is nil, no decoding is done.
 Because the SUFFIXes are tried in order, the empty string should
 be last in the list.")
 
@@ -247,7 +299,10 @@ Do the right thing if the file has been compressed or zipped."
                (coding-system-for-write 'no-conversion)
                (default-directory (or (file-name-directory fullname)
                                       default-directory)))
-           (call-process-region (point-min) (point-max) decoder t t)))
+           (or (consp decoder)
+               (setq decoder (list decoder)))
+           (apply 'call-process-region (point-min) (point-max)
+                  (car decoder) t t nil (cdr decoder))))
       (insert-file-contents fullname visit))))
 \f
 (defun info-initialize ()
@@ -343,7 +398,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
               (save-buffers-kill-emacs)))
     (info)))
 \f
-;; See if the the accessible portion of the buffer begins with a node
+;; See if 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
 ;; rest of the buffer with (search-forward "\n\^_"), and it's a pity
@@ -353,7 +408,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
 ;; want to use the results of re-search-backward.
 
 ;; The return value is the value of point at the beginning of matching
-;; REGERXP, if the function succeeds, nil otherwise.
+;; REGEXP, if the function succeeds, nil otherwise.
 (defun Info-node-at-bob-matching (regexp)
   (and (bobp)                          ; are we at beginning of buffer?
        (looking-at "\^_")              ; does it begin with node delimiter?
@@ -576,7 +631,9 @@ a case-insensitive match is tried."
           ;; Search file for a suitable node.
          (let ((guesspos (point-min))
                (regexp (concat "\\(Node:\\|Ref:\\) *\\("
-                               (regexp-quote nodename)
+                               (if (stringp nodename) 
+                                   (regexp-quote nodename)
+                                 "")
                                "\\) *[,\t\n\177]"))
                (nodepos nil))
 
@@ -665,7 +722,10 @@ a case-insensitive match is tried."
       (progn
        (insert Info-dir-contents)
        (goto-char (point-min)))
-    (let ((dirs Info-directory-list)
+    (let ((dirs (if Info-additional-directory-list
+                              (append Info-directory-list
+                                      Info-additional-directory-list)
+                            Info-directory-list))
          ;; Bind this in case the user sets it to nil.
          (case-fold-search t)
          ;; This is set non-nil if we find a problem in some input files.
@@ -743,6 +803,8 @@ a case-insensitive match is tried."
            (while (re-search-forward "^\\* Menu:" nil t)
              (let (beg nodename end)
                (forward-line 1)
+               (while (and (eolp) (not (eobp)))
+                 (forward-line 1))
                (setq beg (point))
                (or (search-backward "\n\^_" nil 'move)
                    (looking-at "\^_")
@@ -806,6 +868,7 @@ a case-insensitive match is tried."
       (while buffers
        (kill-buffer (car buffers))
        (setq buffers (cdr buffers)))
+      (if Info-fontify (Info-fontify-menu-headers))
       (goto-char (point-min))
       (if problems
          (message "Composing main Info directory...problems encountered, see `*Messages*'")
@@ -862,6 +925,9 @@ a case-insensitive match is tried."
     (if (numberp nodepos)
        (+ (- nodepos lastfilepos) (point)))))
 
+(defvar Info-header-line nil
+  "If the info node header is hidden, the text of the header.")
+
 (defun Info-select-node ()
 "Select the info node that point is in.
 Bind this in case the user sets it to nil."
@@ -884,6 +950,7 @@ Bind this in case the user sets it to nil."
       ;; Find the end of it, and narrow.
       (beginning-of-line)
       (let (active-expression)
+       ;; Narrow to the node contents
        (narrow-to-region (point)
                          (if (re-search-forward "\n[\^_\f]" nil t)
                              (prog1
@@ -896,26 +963,52 @@ Bind this in case the user sets it to nil."
                            (point-max)))
        (if Info-enable-active-nodes (eval active-expression))
        (if Info-fontify (Info-fontify-node))
+       (if Info-use-header-line
+           (Info-setup-header-line)
+         (setq Info-header-line nil))
        (run-hooks 'Info-selection-hook)))))
 
 (defun Info-set-mode-line ()
   (setq mode-line-buffer-identification
-       (concat
-        "  *Info* ("
-        (file-name-nondirectory (if (stringp Info-current-file)
-                                    Info-current-file
-                                  (or buffer-file-name "")))
-        ") "
-        (or Info-current-node ""))))
+       (nconc (propertized-buffer-identification "%b")
+              (list
+               (concat " ("
+                       (file-name-nondirectory
+                        (if (stringp Info-current-file)
+                            Info-current-file
+                          (or buffer-file-name "")))
+                       ") "
+                       (or Info-current-node ""))))))
+\f
+;; Skip the node header and make it into a header-line.  This function
+;; should be called when the node is already narrowed.
+(defun Info-setup-header-line ()
+  (goto-char (point-min))
+  (let* ((case-fold-search t)
+        (header-end (save-excursion (forward-line 1) (1- (point))))
+        ;; If we find neither Next: nor Prev: link, show the entire
+        ;; node header.  Otherwise, don't show the File: and Node:
+        ;; parts, to avoid wasting precious space on information that
+        ;; is available in the mode line.
+        (header-beg (if (re-search-forward
+                         "\\(next\\|prev[ious]*\\): "
+                         header-end t)
+                        (match-beginning 1)
+                      (point))))
+    (set (make-local-variable 'Info-header-line)
+        (buffer-substring header-beg header-end))
+    (setq header-line-format 'Info-header-line)
+    (narrow-to-region (1+ header-end) (point-max))))
 \f
 ;; 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 &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 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."
-  (interactive (list (Info-read-node-name "Goto node: ") current-prefix-arg))
+  (interactive (list (Info-read-node-name "Go to node: ") current-prefix-arg))
   (info-initialize)
   (if fork
       (set-buffer
@@ -1014,8 +1107,12 @@ If FORK is a string, it is the name to use for the new buffer."
 
 (defun Info-search (regexp)
   "Search for REGEXP, starting from point, and select node it's found in."
-  (interactive (list (read-string "Regexp search: "
-                                 nil 'Info-search-history)))
+  (interactive (list (read-string
+                     (if Info-search-history
+                         (format "Regexp search (default `%s'): "
+                                 (car Info-search-history))
+                       "Regexp search: ")
+                     nil 'Info-search-history)))
   (when transient-mark-mode
     (deactivate-mark))
   (when (equal regexp "")
@@ -1090,15 +1187,20 @@ if ERRORNAME is nil, just return nil.
 Bind this in case the user sets it to nil."
   (let ((case-fold-search t))
     (save-excursion
-      (goto-char (point-min))
-      (forward-line 1)
-      (if (re-search-backward (concat name ":") nil t)
-         (progn
-           (goto-char (match-end 0))
-           (Info-following-node-name))
-       (if (eq errorname t)
-           nil
-         (error "Node has no %s" (capitalize (or errorname name))))))))
+      (save-restriction
+       (goto-char (point-min))
+       (when Info-header-line
+         ;; expose the header line in the buffer
+         (widen)
+         (forward-line -1))
+       (let ((bound (point)))
+         (forward-line 1)
+         (cond ((re-search-backward (concat name ":") bound t)
+                (goto-char (match-end 0))
+                (Info-following-node-name))
+               ((not (eq errorname t))
+                (error "Node has no %s"
+                       (capitalize (or errorname name))))))))))
 
 (defun Info-following-node-name (&optional allowedchars)
   "Return the node name in the buffer following point.
@@ -1314,7 +1416,10 @@ FOOTNOTENAME may be an abbreviation of the reference name."
 
 (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."
+Completion is allowed, and the menu item point is on is the default.
+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."
   (interactive
    (let ((completions '())
         ;; If point is within a menu item, use that item as the default
@@ -1430,10 +1535,28 @@ N is the digit argument used to invoke this command."
                (not (string-match "\\<index\\>" Info-current-node)))
           (Info-goto-node (Info-extract-menu-counting 1))
           t)
-         ((save-excursion (search-backward "next:" nil t))
+         ((save-excursion
+            (save-restriction
+              (let (limit)
+                (when Info-header-line
+                  (goto-char (point-min))
+                  (widen)
+                  (forward-line -1)
+                  (setq limit (point))
+                  (forward-line 1))
+                (search-backward "next:" limit t))))
           (Info-next)
           t)
-         ((and (save-excursion (search-backward "up:" nil t))
+         ((and (save-excursion
+                 (save-restriction
+                   (let (limit)
+                     (when Info-header-line
+                       (goto-char (point-min))
+                       (widen)
+                       (forward-line -1)
+                       (setq limit (point))
+                       (forward-line 1))
+                     (search-backward "up:" limit t))))
                ;; Use string-equal, not equal, to ignore text props.
                (not (string-equal (downcase (Info-extract-pointer "up"))
                                   "top")))
@@ -1539,7 +1662,8 @@ N is the digit argument used to invoke this command."
                 ;; so we can scroll back through it.
                 (goto-char (point-max))))
         (recenter -1))
-       ((and (not (equal (Info-extract-pointer "up")
+       ((and (Info-no-error (Info-extract-pointer "prev"))
+             (not (equal (Info-extract-pointer "up")
                          (Info-extract-pointer "prev"))))
         (Info-no-error (Info-prev))
         (goto-char (point-max))
@@ -1559,13 +1683,16 @@ N is the digit argument used to invoke this command."
 (defun Info-scroll-up ()
   "Scroll one screenful forward in Info, considering all nodes as one sequence.
 Once you scroll far enough in a node that its menu appears on the screen
-but after point, the next scroll moves into its first subnode.
+but after point, the next scroll moves into its first subnode, unless
+`Info-scroll-prefer-subnodes' is nil.
 
-When you scroll past the end of a node, that goes to the next node; if
-this node has no successor, it moves to the parent node's successor,
-and so on.  If point is inside the menu of a node, it moves to
-subnode indicated by the following menu item.  (That case won't
-normally result from this command, but can happen in other ways.)"
+When you scroll past the end of a node, that goes to the next node if
+`Info-scroll-prefer-subnodes' is non-nil and to the first subnode otherwise;
+if this node has no successor, it moves to the parent node's successor,
+and so on.  If `Info-scroll-prefer-subnodes' is non-nil and point is inside
+the menu of a node, it moves to subnode indicated by the following menu
+item.  (That case won't normally result from this command, but can happen
+in other ways.)"
 
   (interactive)
   (if (or (< (window-start) (point-min))
@@ -1574,32 +1701,39 @@ normally result from this command, but can happen in other ways.)"
   (let* ((case-fold-search t)
         (virtual-end (save-excursion
                        (goto-char (point-min))
-                       (if (search-forward "\n* Menu:" nil t)
+                       (if (and Info-scroll-prefer-subnodes
+                                (search-forward "\n* Menu:" nil t))
                            (point)
                          (point-max)))))
     (if (or (< virtual-end (window-start))
            (pos-visible-in-window-p virtual-end))
-       (Info-next-preorder)
+       (cond
+        (Info-scroll-prefer-subnodes (Info-next-preorder))
+        ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1))))
+        (t (Info-next-preorder)))
       (scroll-up))))
 
 (defun Info-scroll-down ()
   "Scroll one screenful back in Info, considering all nodes as one sequence.
-Within the menu of a node, this goes to its last subnode.
-When you scroll past the beginning of a node, that goes to the
-previous node or back up to the parent node."
+If point is within the menu of a node, and `Info-scroll-prefer-subnodes'
+is non-nil, this goes to its last subnode.  When you scroll past the
+beginning of a node, that goes to the previous node or back up to the
+parent node."
   (interactive)
   (if (or (< (window-start) (point-min))
          (> (window-start) (point-max)))
       (set-window-start (selected-window) (point)))
   (let* ((case-fold-search t)
         (current-point (point))
-        (virtual-end (save-excursion
-                       (beginning-of-line)
-                       (setq current-point (point))
-                       (goto-char (point-min))
-                       (search-forward "\n* Menu:"
-                                       current-point
-                                       t))))
+        (virtual-end
+         (and Info-scroll-prefer-subnodes
+              (save-excursion
+                (beginning-of-line)
+                (setq current-point (point))
+                (goto-char (point-min))
+                (search-forward "\n* Menu:"
+                                current-point
+                                t)))))
     (if (or virtual-end (pos-visible-in-window-p (point-min)))
        (Info-last-preorder)
       (scroll-down))))
@@ -1681,13 +1815,12 @@ Give a blank topic name to go to the Index node itself."
              (progn
                (goto-char (point-min))
                (while (re-search-forward pattern nil t)
-                 (setq matches
-                       (cons (list (match-string-no-properties 1)
-                                   (match-string-no-properties 2)
-                                   Info-current-node
-                                   (string-to-int (concat "0"
-                                                          (match-string 3))))
-                             matches)))
+                 (push (list (match-string-no-properties 1)
+                             (match-string-no-properties 2)
+                             Info-current-node
+                             (string-to-int (concat "0"
+                                                    (match-string 3))))
+                       matches))
                (and (setq node (Info-extract-pointer "next" t))
                     (string-match "\\<Index\\>" node)))
            (Info-goto-node node))
@@ -1822,11 +1955,7 @@ ERRORSTRING optional fourth argument, controls action on no match
 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, or up if none."
   (interactive "e")
-  (let* ((start (event-start click))
-        (window (car start))
-        (pos (car (cdr start))))
-    (select-window window)
-    (goto-char pos))
+  (mouse-set-point click)
   (and (not (Info-try-follow-nearest-node))
        (save-excursion (forward-line 1) (eobp))
        (Info-next-preorder)))
@@ -1933,6 +2062,8 @@ If no reference to follow, moves to the next node, or up if none."
     :help "Go backward one node, considering all as a sequence"]
    ["Forward" Info-forward-node
     :help "Go forward one node, considering all as a sequence"]
+   ["Beginning" beginning-of-buffer
+    :help "Go to beginning of this node"]
    ["Top" Info-top-node
     :help "Go to top node of file"]
    ["Final Node" Info-final-node
@@ -1941,16 +2072,33 @@ If no reference to follow, moves to the next node, or up if none."
    ("Reference" ["You should never see this" report-emacs-bug t])
    ["Search..." Info-search
     :help "Search for regular expression in this Info file"]
-   ["Goto Node..." Info-goto-node
+   ["Go to Node..." Info-goto-node
     :help "Go to a named node"]
-   ["Last" Info-last Info-history
+   ["Last" Info-last :active Info-history
     :help "Go to the last node you were at"]
    ("Index..."
     ["Lookup a String" Info-index
      :help "Look for a string in the index items"]
     ["Next Matching Item" Info-index-next
      :help "Look for another occurrence of previous item"])
-   ["Exit" Info-exit t]))
+   ["Edit" Info-edit :help "Edit contents of this node"
+    :active Info-enable-edit]
+   ["Exit" Info-exit :help "Stop reading Info"]))
+
+
+(defvar info-tool-bar-map
+  (if (display-graphic-p)
+      (let ((tool-bar-map (make-sparse-keymap)))
+       (tool-bar-add-item-from-menu 'Info-exit "close" Info-mode-map)
+       (tool-bar-add-item-from-menu 'Info-prev "left_arrow" Info-mode-map)
+       (tool-bar-add-item-from-menu 'Info-next "right_arrow" Info-mode-map)
+       (tool-bar-add-item-from-menu 'Info-up "up_arrow" Info-mode-map)
+       (tool-bar-add-item-from-menu 'Info-last "undo" Info-mode-map)
+       (tool-bar-add-item-from-menu 'Info-top-node "home" Info-mode-map)
+       (tool-bar-add-item-from-menu 'Info-index "index" Info-mode-map)
+       (tool-bar-add-item-from-menu 'Info-goto-node "jump_to" Info-mode-map)
+       (tool-bar-add-item-from-menu 'Info-search "search" Info-mode-map)
+       tool-bar-map)))
 
 (defvar Info-menu-last-node nil)
 ;; Last node the menu was created for.
@@ -2023,7 +2171,7 @@ If no reference to follow, moves to the next node, or up if none."
 
 \f
 ;; Info mode is suitable only for specially formatted data.
-(put 'info-mode 'mode-class 'special)
+(put 'Info-mode 'mode-class 'special)
 
 (defun Info-mode ()
   "Info mode provides commands for browsing through the Info documentation tree.
@@ -2083,7 +2231,6 @@ Advanced commands:
   (setq mode-name "Info")
   (setq tab-width 8)
   (use-local-map Info-mode-map)
-  (make-local-hook 'activate-menubar-hook)
   (add-hook 'activate-menubar-hook 'Info-menu-update nil t)
   (set-syntax-table text-mode-syntax-table)
   (setq local-abbrev-table text-mode-abbrev-table)
@@ -2098,10 +2245,11 @@ Advanced commands:
   (setq Info-tag-table-buffer nil)
   (make-local-variable 'Info-history)
   (make-local-variable 'Info-index-alternatives)
+  (set (make-local-variable 'tool-bar-map) info-tool-bar-map)
   ;; 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)
+  (add-hook 'clone-buffer-hook 'Info-clone-buffer-hook nil t)
   (Info-set-mode-line)
   (run-hooks 'Info-mode-hook))
 
@@ -2115,15 +2263,14 @@ Advanced commands:
              (with-current-buffer Info-tag-table-buffer
                (copy-marker (marker-position m))))))))
 
-(defvar Info-edit-map nil
+(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)
   "Local keymap used within `e' command of Info.")
-(if Info-edit-map
-    nil
-  (setq Info-edit-map (nconc (make-sparse-keymap) text-mode-map))
-  (define-key Info-edit-map "\C-c\C-c" 'Info-cease-edit))
 
 ;; Info-edit mode is suitable only for specially formatted data.
-(put 'info-edit-mode 'mode-class 'special)
+(put 'Info-edit-mode 'mode-class 'special)
 
 (defun Info-edit-mode ()
   "Major mode for editing the contents of an Info node.
@@ -2167,8 +2314,14 @@ Allowed only if variable `Info-enable-edit' is non-nil."
        (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" "message"
-    ("dired" . "dired-x") ("c" . "ccmode") "viper")
+  '("ediff" "forms" "gnus" ("mh" . "mh-e") "sc" "message"
+    ("dired" . "dired-x") ("c" . "ccmode") "viper" "vip"
+    ("skeleton" . "autotype") ("auto-insert" . "autotype")
+    ("copyright" . "autotype") ("executable" . "autotype")
+    ("time-stamp" . "autotype") ("quickurl" . "autotype")
+    ("tempo" . "autotype") ("hippie-expand" . "autotype")
+    ("cvs" . "pcl-cvs")
+    "ebrowse" "eshell" "cl" "idlwave" "reftex" "speedbar" "widget" "woman")
   "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.
@@ -2183,7 +2336,7 @@ The locations are of the format used in `Info-history', i.e.
 \(FILENAME NODENAME BUFFERPOS\)."
   (let ((where '())
        (cmd-desc (concat "^\\* +" (regexp-quote (symbol-name command))
-                         ":\\s *\\(.*\\)\\.$"))
+                         "\\( <[0-9]+>\\)?:\\s *\\(.*\\)\\.$"))
        (info-file "emacs"))            ;default
     ;; Determine which info file this command is documented in.
     (if (get command 'info-file)
@@ -2202,27 +2355,35 @@ The locations are of the format used in `Info-history', i.e.
            (if (string-match regexp (symbol-name command))
                (setq info-file file file-list nil))
            (setq file-list (cdr file-list))))))
-    (save-excursion
-      (condition-case nil
-         (Info-find-node info-file "Command Index")
-       ;; Some manuals may not have a separate Command Index node,
-       ;; so try just Index instead.
-       (error
-        (Info-find-node info-file "Index")))
-      ;; Take the index node off the Info history.
-      (setq Info-history (cdr Info-history))
-      (goto-char (point-max))
-      (while (re-search-backward cmd-desc nil t)
-       (setq where (cons (list Info-current-file
-                               (match-string-no-properties 1)
+    (Info-find-node info-file "Top")
+    (or (and (search-forward "\n* menu:" nil t)
+            (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t))
+       (error "Info file `%s' appears to lack an index" info-file))
+    (goto-char (match-beginning 1))
+    ;; Bind Info-history to nil, to prevent the index nodes from
+    ;; getting into the node history.
+    (let ((Info-history nil)
+         (exact nil)
+         node found)
+      (Info-goto-node (Info-extract-menu-node-name))
+      (while
+         (progn
+           (goto-char (point-min))
+           (while (re-search-forward cmd-desc nil t)
+             (setq where
+                   (cons (list Info-current-file
+                               (match-string-no-properties 2)
                                0)
                          where)))
-      where)))
+           (and (setq node (Info-extract-pointer "next" t))
+                (string-match "\\<Index\\>" node)))
+       (Info-goto-node node)))
+    where))
 
 ;;;###autoload
 (defun Info-goto-emacs-command-node (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
+The command is found by looking up in Emacs manual's indices
 or in another manual found via COMMAND's `info-file' property or
 the variable `Info-file-list-for-emacs'."
   (interactive "CFind documentation for command: ")
@@ -2237,13 +2398,17 @@ the variable `Info-file-list-for-emacs'."
          ;; FIXME It would be cool if this could use a buffer other
          ;; than *info*.
          (pop-to-buffer "*info*")
-         (Info-find-node (car (car where))
-                         (car (cdr (car where))))
+         ;; Bind Info-history to nil, to prevent the last Index node
+         ;; visited by Info-find-emacs-command-nodes from being
+         ;; pushed onto the history.
+         (let ((Info-history nil))
+           (Info-find-node (car (car where))
+                           (car (cdr (car where)))))
          (if (> num-matches 1)
              (progn
-               ;; Info-find-node already pushed (car where) onto
-               ;; Info-history.  Put the other nodes that were found on
-               ;; the history.
+               ;; (car where) will be pushed onto Info-history
+               ;; when/if they go to another node.  Put the other
+               ;; nodes that were found on the history.
                (setq Info-history (nconc (cdr where) Info-history))
                (message "Found %d other entr%s.  Use %s to see %s."
                         (1- num-matches)
@@ -2254,12 +2419,13 @@ the variable `Info-file-list-for-emacs'."
 
 ;;;###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.
+  "Go to the node in the Emacs manual which describes the command bound to KEY.
+KEY is a string.
 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
+The command is found by looking up in Emacs manual's indices
 or in another manual found via COMMAND's `info-file' property or
 the variable `Info-file-list-for-emacs'."
-  (interactive "kFind documentation for key:")
+  (interactive "kFind documentation for key: ")
   (let ((command (key-binding key)))
     (cond ((null command)
           (message "%s is undefined" (key-description key)))
@@ -2271,20 +2437,50 @@ the variable `Info-file-list-for-emacs'."
           (Info-goto-emacs-command-node command)))))
 \f
 (defface Info-title-1-face
-  '((t (:family "helv" :height 240 :weight bold)))
+  '((((type tty pc) (class color)) (:foreground "yellow" :weight bold))
+    (t (:height 1.2 :inherit Info-title-2-face)))
   "Face for Info titles at level 1."
   :group 'info)
 
 (defface Info-title-2-face
-  '((t (:family "helv" :height 180 :weight bold)))
+  '((((type tty pc) (class color)) (:foreground "lightblue" :weight bold))
+    (t (:height 1.2 :inherit Info-title-3-face)))
   "Face for Info titles at level 2."
   :group 'info)
 
 (defface Info-title-3-face
-  '((t (:family "helv" :height 160 :weight bold)))
+  '((((type tty pc) (class color)) (:weight bold))
+    (t (:height 1.2 :inherit Info-title-4-face)))
   "Face for Info titles at level 3."
   :group 'info)
 
+(defface Info-title-4-face
+  '((((type tty pc) (class color)) (:weight bold))
+    (t (:weight bold :inherit variable-pitch)))
+  "Face for Info titles at level 4."
+  :group 'info)
+
+(defface info-menu-header
+  '((((type tty pc))
+     :underline t
+     :weight bold)
+    (t
+     :inherit variable-pitch
+     :weight bold))
+  "Face for headers in Info menus."
+  :group 'info)
+
+(defun Info-fontify-menu-headers ()
+  "Add the face `info-menu-header' to any header before a menu entry."
+  (save-excursion
+    (goto-char (point-min))
+    (when (re-search-forward "\\* Menu:" nil t)
+      (put-text-property (match-beginning 0) (match-end 0)
+                        'face 'info-menu-header)
+      (while (re-search-forward "\n\n\\([^*\n ].*\\)\n\n?[*]" nil t)
+       (put-text-property (match-beginning 1) (match-end 1)
+                          'face 'info-menu-header)))))
+
 (defun Info-fontify-node ()
   (save-excursion
     (let ((buffer-read-only nil)
@@ -2292,42 +2488,56 @@ the variable `Info-file-list-for-emacs'."
       (goto-char (point-min))
       (when (looking-at "^File: [^,: \t]+,?[ \t]+")
        (goto-char (match-end 0))
-       (while
-           (looking-at "[ \t]*\\([^:, \t\n]+\\):[ \t]+\\([^:,\t\n]+\\),?")
+       (while (looking-at "[ \t]*\\([^:, \t\n]+\\):[ \t]+\\([^:,\t\n]+\\),?")
          (goto-char (match-end 0))
-         (if (save-excursion
-               (goto-char (match-beginning 1))
-               (save-match-data (looking-at "Node:")))
-             (put-text-property (match-beginning 2) (match-end 2)
-                                'face 'info-node)
-           (put-text-property (match-beginning 2) (match-end 2)
-                              'face 'info-xref)
-           (put-text-property (match-beginning 2) (match-end 2)
-                              'mouse-face 'highlight))))
+         (let* ((nbeg (match-beginning 2))
+                (nend (match-end 2))
+                (tbeg (match-beginning 1))
+                (tag (buffer-substring tbeg (match-end 1))))
+           (if (string-equal tag "Node")
+               (put-text-property nbeg nend 'face 'info-header-node)
+             (put-text-property nbeg nend 'face 'info-header-xref)
+             (put-text-property nbeg nend 'mouse-face 'highlight)
+             (put-text-property tbeg nend
+                                'help-echo
+                                (concat "Go to node "
+                                        (buffer-substring nbeg nend)))
+             (let ((fun (cdr (assoc tag '(("Prev" . Info-prev)
+                                          ("Next" . Info-next)
+                                          ("Up" . Info-up))))))
+               (when fun
+                 (let ((keymap (make-sparse-keymap)))
+                   (define-key keymap [header-line mouse-1] fun)
+                   (define-key keymap [header-line mouse-2] fun)
+                   (put-text-property tbeg nend 'local-map keymap))))
+             ))))
       (goto-char (point-min))
-      (while (re-search-forward "\n\\([^ \t\n].+\\)\n\\(\\*+\\|=+\\|-+\\)$"
+      (while (re-search-forward "\n\\([^ \t\n].+\\)\n\\(\\*+\\|=+\\|-+\\|\\.+\\)$"
                                nil t)
        (let ((c (preceding-char))
              face)
          (cond ((= c ?*) (setq face 'Info-title-1-face))
                ((= c ?=) (setq face 'Info-title-2-face))
-               (t        (setq face 'Info-title-3-face)))
+               ((= c ?-) (setq face 'Info-title-3-face))
+               (t        (setq face 'Info-title-4-face)))
          (put-text-property (match-beginning 1) (match-end 1)
                             'face face))
        ;; This is a serious problem for trying to handle multiple
        ;; frame types at once.  We want this text to be invisible
        ;; on frames that can display the font above.
-       (if (memq (framep (selected-frame)) '(x pc w32))
-           (add-text-properties (match-end 1) (match-end 2)
-                                '(invisible t intangible t))))
+       (when (memq (framep (selected-frame)) '(x pc w32 mac))
+         (add-text-properties (match-end 1) (match-end 2)
+                              '(invisible t intangible t))
+         (add-text-properties (1- (match-end 1)) (match-end 2)
+                              '(intangible t))))
       (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)))
+         (add-text-properties (match-beginning 1) (match-end 1)
+                              '(face info-xref
+                                mouse-face highlight
+                                help-echo "mouse-2: go to this node"))))
       (goto-char (point-min))
       (if (and (search-forward "\n* Menu:" nil t)
               (not (string-match "\\<Index\\>" Info-current-node))
@@ -2340,10 +2550,11 @@ the variable `Info-file-list-for-emacs'."
                  (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-xref)
-             (put-text-property (match-beginning 1) (match-end 1)
-                                'mouse-face 'highlight))))
+             (add-text-properties (match-beginning 1) (match-end 1)
+                                  '(face info-xref
+                                    mouse-face highlight
+                                    help-echo "mouse-2: go to this node")))))
+      (Info-fontify-menu-headers)
       (set-buffer-modified-p nil))))
 \f
 
@@ -2410,6 +2621,8 @@ This will add a speedbar major display mode."
   (speedbar-change-initial-expansion-list "Info")
   )
 
+(eval-when-compile (defvar speedbar-attached-frame))
+
 (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.
@@ -2447,7 +2660,7 @@ specific node to expand."
        nil))))
 
 (defun Info-speedbar-goto-node (text node indent)
-  "When user clicks on TEXT, goto an info NODE.
+  "When user clicks on TEXT, go to an info NODE.
 The INDENT level is ignored."
   (select-frame speedbar-attached-frame)
   (let* ((buff (or (get-buffer "*info*")