(rmail-next-same-subject): Ignore leading and
[bpt/emacs.git] / lisp / info.el
index be26cd4..a50b0c0 100644 (file)
@@ -1,6 +1,7 @@
 ;;; info.el --- info package for Emacs.
 
-;; Copyright (C) 1985-1986, 1992-1997 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 97 Free Software
+;; Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: help
 
 ;;; Code:
 
+(defgroup info nil
+  "Info subsystem"
+  :group 'help
+  :group 'docs)
+
+
 (defvar Info-history nil
   "List of info nodes user has visited.
 Each element of list is a list (FILENAME NODENAME BUFFERPOS).")
 
-(defvar Info-enable-edit nil
+(defcustom Info-enable-edit nil
   "*Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node.
 This is convenient if you want to write info files by hand.
 However, we recommend that you not do this.
 It is better to write a Texinfo file and generate the Info file from that,
-because that gives you a printed manual as well.")
+because that gives you a printed manual as well."
+  :type 'boolean
+  :group 'info)
 
 (defvar Info-enable-active-nodes nil
   "Non-nil allows Info to execute Lisp code associated with nodes.
 The Lisp code is executed when the node is selected.")
 (put 'Info-enable-active-nodes 'risky-local-variable t)
 
-(defvar Info-fontify t
-  "*Non-nil enables highlighting and fonts in Info nodes.")
+(defcustom Info-fontify t
+  "*Non-nil enables highlighting and fonts in Info nodes."
+  :type 'boolean
+  :group 'info)
+
+(defface info-node
+  '((t (:bold t :italic t)))
+  "Face for Info node names."
+  :group 'info)
+
+(defface info-menu-5
+  '((t (:underline t)))
+  "Face for the fifth and tenth `*' in an Info menu."
+  :group 'info)
+
+(defface info-xref
+  '((t (:bold t)))
+  "Face for Info cross-references."
+  :group 'info)
 
-(defvar Info-fontify-maximum-menu-size 30000
-  "*Maximum size of menu to fontify if `Info-fontify' is non-nil.")
+(defcustom Info-fontify-maximum-menu-size 30000
+  "*Maximum size of menu to fontify if `Info-fontify' is non-nil."
+  :type 'integer
+  :group 'info)
 
 (defvar Info-directory-list
   (let ((path (getenv "INFOPATH"))
@@ -98,9 +126,11 @@ 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.")
 
-(defvar Info-additional-directory-list nil
+(defcustom Info-additional-directory-list nil
   "List of additional directories to search for Info documentation files.
-These directories are not searched for merging the `dir' file.")
+These directories are not searched for merging the `dir' file."
+  :type '(repeat directory)
+  :group 'info)
 
 (defvar Info-current-file nil
   "Info file that Info is now looking at, or nil.
@@ -131,21 +161,44 @@ Marker points nowhere if file has no tag table.")
   "Non-nil if Emacs was started solely as an Info browser.")
 
 (defvar Info-suffix-list
+  ;; The MS-DOS list should work both when long file names are
+  ;; supported (Windows 9X), and when only 8+3 file names are available.
   (if (eq system-type 'ms-dos)
       '( (".gz"      . "gunzip")
         (".z"       . "gunzip")
+        (".inz"     . "gunzip")
+        (".igz"     . "gunzip")
+        (".info.Z"  . "gunzip")
+        (".info.gz" . "gunzip")
+        ("-info.Z"  . "gunzip")
+        ("-info.gz" . "gunzip")
+        ("/index.gz". "gunzip")
+        ("/index.z" . "gunzip")
         (".inf"     . nil)
+        (".info"    . nil)
+        ("-info"    . nil)
+        ("/index"   . nil)
         (""         . nil))
-    '( (".info.Z"  . "uncompress")
-       (".info.Y"  . "unyabba")
-       (".info.gz" . "gunzip")
-       (".info.z"  . "gunzip")
-       (".info"    . nil)
-       (".Z"       . "uncompress")
-       (".Y"       . "unyabba")
-       (".gz"      . "gunzip")
-       (".z"       . "gunzip")
-       (""         . nil)))
+    '( (".info.Z".    "uncompress")
+       (".info.Y".    "unyabba")
+       (".info.gz".   "gunzip")
+       (".info.z".    "gunzip")
+       (".info".      nil)
+       ("-info.Z".   "uncompress")
+       ("-info.Y".   "unyabba")
+       ("-info.gz".  "gunzip")
+       ("-info.z".   "gunzip")
+       ("-info".     nil)
+       ("/index.Z".   "uncompress")
+       ("/index.Y".   "unyabba")
+       ("/index.gz".  "gunzip")
+       ("/index.z".   "gunzip")
+       ("/index".     nil)
+       (".Z".         "uncompress")
+       (".Y".         "unyabba")
+       (".gz".        "gunzip")
+       (".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.
@@ -207,12 +260,15 @@ Do the right thing if the file has been compressed or zipped."
             (jka-compr-installed-p)
             (jka-compr-get-compression-info fullname))
        (setq decoder nil))
-    (insert-file-contents fullname visit)
     (if decoder
-       (let ((buffer-read-only nil)
-             (default-directory (or (file-name-directory fullname)
-                                    default-directory)))
-         (call-process-region (point-min) (point-max) decoder t t)))))
+       (progn
+         (insert-file-contents-literally fullname visit)
+         (let ((buffer-read-only nil)
+               (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)))
+      (insert-file-contents fullname visit))))
 
 ;;;###autoload (add-hook 'same-window-buffer-names "*info*")
 
@@ -230,11 +286,10 @@ The top-level Info directory is made by combining all the files named `dir'
 in all the directories in that path."
   (interactive (if current-prefix-arg
                   (list (read-file-name "Info file name: " nil nil t))))
+  (pop-to-buffer "*info*")
   (if file
       (Info-goto-node (concat "(" file ")"))
-    (if (get-buffer "*info*")
-       (pop-to-buffer "*info*")
-      (Info-directory))))
+    (Info-directory)))
 
 ;;;###autoload
 (defun info-standalone ()
@@ -475,7 +530,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
                    (save-excursion
                      (or buffers
                          (message "Composing main Info directory..."))
-                     (set-buffer (generate-new-buffer "info dir"))
+                     (set-buffer (generate-new-buffer " info dir"))
                      (insert-file-contents file)
                      (setq buffers (cons (current-buffer) buffers)
                            Info-dir-file-attributes
@@ -646,7 +701,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
 (defun Info-set-mode-line ()
   (setq mode-line-buffer-identification
        (concat
-        "Info:  ("
+        "  Info:  ("
         (if Info-current-file
             (file-name-nondirectory Info-current-file)
           "")
@@ -1188,8 +1243,7 @@ N is the digit argument used to invoke this command."
   (interactive)
   (if Info-standalone
       (save-buffers-kill-emacs)
-    (switch-to-buffer (prog1 (other-buffer (current-buffer))
-                       (bury-buffer (current-buffer))))))
+    (bury-buffer)))
 
 (defun Info-next-menu-item ()
   (interactive)
@@ -1463,6 +1517,7 @@ Give a blank topic name to go to the Index node itself."
   (interactive)
   (save-window-excursion
     (switch-to-buffer "*Help*")
+    (setq buffer-read-only nil)
     (erase-buffer)
     (insert (documentation 'Info-mode))
     (help-mode)
@@ -1724,9 +1779,12 @@ Selecting other nodes:
 \\[Info-index-next]    (comma) Move to the next match from a previous `i' command.
 
 Moving within a node:
-\\[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.
-\\[Info-scroll-down]  Normally, scroll backward.  If the beginning of the buffer is
+\\[Info-scroll-up]     Normally, scroll forward a full screen.
+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.
+When after all menu items (or if their is no menu), move up to
+the parent node.
+\\[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.  
 
@@ -1762,20 +1820,9 @@ Advanced commands:
   (setq Info-tag-table-buffer nil)
   (make-local-variable 'Info-history)
   (make-local-variable 'Info-index-alternatives)
-  (if (memq (framep (selected-frame)) '(x pc w32))
-      (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))
+  ;; 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)
   (Info-set-mode-line)
   (run-hooks 'Info-mode-hook))
 
@@ -1935,12 +1982,14 @@ the variable `Info-file-list-for-emacs'."
          (t
           (Info-goto-emacs-command-node command)))))
 \f
-(defvar Info-title-face-alist
+(defcustom Info-title-face-alist
   '((?* bold underline)
     (?= bold-italic underline)
     (?- italic underline))
   "*Alist of face or list of faces to use for pseudo-underlined titles.
-The alist key is the character the title is underlined with (?*, ?= or ?-).")
+The alist key is the character the title is underlined with (?*, ?= or ?-)."
+  :type '(repeat (list character face face))
+  :group 'info)
 
 (defun Info-fontify-node ()
   (save-excursion
@@ -1962,8 +2011,12 @@ The alist key is the character the title is underlined with (?*, ?= or ?-).")
        (put-text-property (match-beginning 1) (match-end 1)
                           'face
                           (cdr (assq (preceding-char) Info-title-face-alist)))
-       (put-text-property (match-end 1) (match-end 2)
-                          'invisible t))
+       ;; 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))
+           (put-text-property (match-end 1) (match-end 2)
+                              'invisible t)))
       (goto-char (point-min))
       (while (re-search-forward "\\*Note[ \n\t]+\\([^:]*\\):" nil t)
        (if (= (char-after (1- (match-beginning 0))) ?\") ; hack
@@ -1999,7 +2052,83 @@ The alist key is the character the title is underlined with (?*, ?= or ?-).")
        (kill-buffer Info-tag-table-buffer)))
 
 (add-hook 'kill-buffer-hook 'Info-kill-buffer)
-  
+
+;;; Speedbar support:
+;; These functions permit speedbar to display the "tags" in the
+;; current info node.
+
+(eval-when-compile (require 'speedbspec))
+
+(defvar Info-last-speedbar-node nil
+  "Last node viewed with speedbar in the form '(NODE FILE).")
+
+(defvar Info-speedbar-menu-items
+  '(["Browse Item On Line" speedbar-edit-line t])
+  "Additional menu-items to add to speedbar frame.")
+
+(defun Info-speedbar-buttons (buffer)
+  "Create a speedbar display to help navigation in an Info file.
+BUFFER is the buffer speedbar is requesting buttons for."
+  (goto-char (point-min))
+  (if (and (looking-at "<Directory>")
+          (save-excursion
+            (set-buffer buffer)
+            (and (equal (car Info-last-speedbar-node) Info-current-node)
+                 (equal (cdr Info-last-speedbar-node) Info-current-file))))
+      nil
+    (erase-buffer)
+    (speedbar-insert-button "<Directory>" 'info-xref 'highlight
+                           'Info-speedbar-button
+                           'Info-directory)
+    (speedbar-insert-button "<Top>" 'info-xref 'highlight
+                           'Info-speedbar-button
+                           'Info-top-node)
+    (speedbar-insert-button "<Last>" 'info-xref 'highlight
+                           'Info-speedbar-button
+                           'Info-last)
+    (speedbar-insert-button "<Up>" 'info-xref 'highlight
+                           'Info-speedbar-button
+                           'Info-up)
+    (speedbar-insert-button "<Next>" 'info-xref 'highlight
+                           'Info-speedbar-button
+                           'Info-next)
+    (speedbar-insert-button "<Prev>" 'info-xref 'highlight
+                           'Info-speedbar-button
+                           'Info-prev)
+    (let ((completions nil))
+      (save-excursion
+       (set-buffer buffer)
+       (setq Info-last-speedbar-node
+             (cons Info-current-node Info-current-file))
+       (goto-char (point-min))
+       ;; Always skip the first one...
+       (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
+       (while (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
+         (setq completions (cons (buffer-substring (match-beginning 1)
+                                                   (match-end 1))
+                                 completions))))
+      (setq completions (nreverse completions))
+      (while completions
+       (speedbar-make-tag-line nil nil nil nil
+                               (car completions) 'Info-speedbar-menu
+                               nil 'info-node 0)
+       (setq completions (cdr completions))))))
+
+(defun Info-speedbar-button (text token indent)
+  "Called when user clicks <Directory> from speedbar.
+TEXT, TOKEN, and INDENT are unused."
+  (speedbar-with-attached-buffer
+   (funcall token)
+   (setq Info-last-speedbar-node nil)
+   (speedbar-update-contents)))
+
+(defun Info-speedbar-menu (text token indent)
+  "Goto the menu node specified in TEXT.
+TOKEN and INDENT are not used."
+  (speedbar-with-attached-buffer
+   (Info-menu text)
+   (setq Info-last-speedbar-node nil)
+   (speedbar-update-contents)))
 
 (provide 'info)