(custom-face-value-create): If face name doesn't end with "face", add
[bpt/emacs.git] / lisp / info.el
index 8641761..e02ae97 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)
@@ -73,7 +74,8 @@ The Lisp code is executed when the node is selected.")
   :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,40 @@ 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', 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.  (If
+you want to override that, 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.
@@ -343,7 +367,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 +377,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?
@@ -867,6 +891,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."
@@ -889,6 +916,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
@@ -901,26 +929,42 @@ 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))
+  (forward-line 1)
+  (set (make-local-variable 'Info-header-line)
+       (buffer-substring (point-min) (1- (point))))
+  (setq header-line-format 'Info-header-line)
+  (narrow-to-region (point) (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
@@ -1095,15 +1139,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.
@@ -1319,7 +1368,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
@@ -1686,13 +1738,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))
@@ -1827,11 +1878,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)))
@@ -1938,6 +1985,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
@@ -1946,16 +1995,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.
@@ -2028,7 +2094,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.
@@ -2103,6 +2169,7 @@ 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)
@@ -2120,15 +2187,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.
@@ -2173,7 +2239,13 @@ Allowed only if variable `Info-enable-edit' is non-nil."
 \f
 (defvar Info-file-list-for-emacs
   '("ediff" "forms" "gnus" ("mh" . "mh-e") "sc" "message"
-    ("dired" . "dired-x") ("c" . "ccmode") "viper")
+    ("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.
@@ -2188,7 +2260,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)
@@ -2207,22 +2279,30 @@ 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)
@@ -2242,13 +2322,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)
@@ -2276,20 +2360,29 @@ 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)
+
 (defun Info-fontify-node ()
   (save-excursion
     (let ((buffer-read-only nil)
@@ -2297,42 +2390,54 @@ 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))
+       (if (memq (framep (selected-frame)) '(x pc w32 mac))
            (add-text-properties (match-end 1) (match-end 2)
                                 '(invisible t 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))
@@ -2345,10 +2450,10 @@ 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")))))
       (set-buffer-modified-p nil))))
 \f
 
@@ -2415,6 +2520,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.
@@ -2452,7 +2559,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*")