Refill some copyright headers.
[bpt/emacs.git] / lisp / info.el
index e9e2b42..185d504 100644 (file)
@@ -1,8 +1,8 @@
-;;; info.el --- info package for Emacs
+;; info.el --- info package for Emacs
 
-;; Copyright (C) 1985, 1986, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-;;   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
-;;   Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+;;   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+;;   2010, 2011  Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: help
@@ -83,32 +83,28 @@ The Lisp code is executed when the node is selected.")
     (t :height 1.2 :inherit info-title-2))
   "Face for info titles at level 1."
   :group 'info)
-;; backward-compatibility alias
-(put 'Info-title-1-face 'face-alias 'info-title-1)
+(define-obsolete-face-alias 'Info-title-1-face 'info-title-1 "22.1")
 
 (defface info-title-2
   '((((type tty pc) (class color)) :foreground "lightblue" :weight bold)
     (t :height 1.2 :inherit info-title-3))
   "Face for info titles at level 2."
   :group 'info)
-;; backward-compatibility alias
-(put 'Info-title-2-face 'face-alias 'info-title-2)
+(define-obsolete-face-alias 'Info-title-2-face 'info-title-2 "22.1")
 
 (defface info-title-3
   '((((type tty pc) (class color)) :weight bold)
     (t :height 1.2 :inherit info-title-4))
   "Face for info titles at level 3."
   :group 'info)
-;; backward-compatibility alias
-(put 'Info-title-3-face 'face-alias 'info-title-3)
+(define-obsolete-face-alias 'Info-title-3-face 'info-title-3 "22.1")
 
 (defface info-title-4
   '((((type tty pc) (class color)) :weight bold)
     (t :weight bold :inherit variable-pitch))
   "Face for info titles at level 4."
   :group 'info)
-;; backward-compatibility alias
-(put 'Info-title-4-face 'face-alias 'info-title-4)
+(define-obsolete-face-alias 'Info-title-4-face 'info-title-4 "22.1")
 
 (defface info-menu-header
   '((((type tty pc))
@@ -125,7 +121,7 @@ The Lisp code is executed when the node is selected.")
     (t :underline t))
   "Face for every third `*' in an Info menu."
   :group 'info)
-(put 'info-menu-5 'face-alias 'info-menu-star)
+(define-obsolete-face-alias 'info-menu-5 'info-menu-star "22.1")
 
 (defface info-xref
   '((t :inherit link))
@@ -242,7 +238,9 @@ This only has an effect if `Info-hide-note-references' is non-nil."
 (defcustom Info-breadcrumbs-depth 4
   "Depth of breadcrumbs to display.
 0 means do not display breadcrumbs."
-  :type 'integer)
+  :version "23.1"
+  :type 'integer
+  :group 'info)
 
 (defcustom Info-search-whitespace-regexp "\\s-+"
   "If non-nil, regular expression to match a sequence of whitespace chars.
@@ -270,6 +268,8 @@ with wrapping around the current Info node."
   :group 'info)
 
 (defvar Info-isearch-initial-node nil)
+(defvar Info-isearch-initial-history nil)
+(defvar Info-isearch-initial-history-list nil)
 
 (defcustom Info-mode-hook
   ;; Try to obey obsolete Info-fontify settings.
@@ -328,6 +328,54 @@ If number, the point is moved to the corresponding line.")
 
 (defvar Info-standalone nil
   "Non-nil if Emacs was started solely as an Info browser.")
+
+(defvar Info-virtual-files nil
+  "List of definitions of virtual Info files.
+Each element of the list has the format (FILENAME (OPERATION . HANDLER) ...)
+where FILENAME is a regexp that matches a class of virtual Info file names.
+It should be carefully chosen to not cause file name clashes with
+existing file names.  OPERATION is one of the following operation
+symbols `find-file', `find-node', `toc-nodes' that define what HANDLER
+function to call instead of calling the default corresponding function
+to override it.")
+
+(defvar Info-virtual-nodes nil
+  "List of definitions of virtual Info nodes.
+Each element of the list has the format (NODENAME (OPERATION . HANDLER) ...)
+where NODENAME is a regexp that matches a class of virtual Info node names.
+It should be carefully chosen to not cause node name clashes with
+existing node names.  OPERATION is one of the following operation
+symbols `find-node' that define what HANDLER
+function to call instead of calling the default corresponding function
+to override it.")
+
+(defvar Info-current-node-virtual nil
+  "Non-nil if the current Info node is virtual.")
+
+(defun Info-virtual-file-p (filename)
+  "Check if Info file FILENAME is virtual."
+  (Info-virtual-fun 'find-file filename nil))
+
+(defun Info-virtual-fun (op filename nodename)
+  "Find a function that handles operations on virtual manuals.
+OP is an operation symbol (`find-file', `find-node' or `toc-nodes'),
+FILENAME is a virtual Info file name, NODENAME is a virtual Info
+node name.  Return a function found either in `Info-virtual-files'
+or `Info-virtual-nodes'."
+  (or (and (stringp filename) ; some legacy code can still use a symbol
+          (cdr-safe (assoc op (assoc-default filename
+                                             Info-virtual-files
+                                             'string-match))))
+      (and (stringp nodename) ; some legacy code can still use a symbol
+          (cdr-safe (assoc op (assoc-default nodename
+                                             Info-virtual-nodes
+                                             'string-match))))))
+
+(defun Info-virtual-call (virtual-fun &rest args)
+  "Call a function that handles operations on virtual manuals."
+  (when (functionp virtual-fun)
+    (or (apply virtual-fun args) t)))
+
 \f
 (defvar Info-suffix-list
   ;; The MS-DOS list should work both when long file names are
@@ -354,24 +402,28 @@ If number, the point is moved to the corresponding line.")
        (".info.gz".   "gunzip")
        (".info.z".    "gunzip")
        (".info.bz2" . ("bzip2" "-dc"))
+       (".info.xz".   "unxz")
        (".info".      nil)
        ("-info.Z".   "uncompress")
        ("-info.Y".   "unyabba")
        ("-info.gz".  "gunzip")
        ("-info.bz2" . ("bzip2" "-dc"))
        ("-info.z".   "gunzip")
+       ("-info.xz".  "unxz")
        ("-info".     nil)
        ("/index.Z".   "uncompress")
        ("/index.Y".   "unyabba")
        ("/index.gz".  "gunzip")
        ("/index.z".   "gunzip")
        ("/index.bz2". ("bzip2" "-dc"))
+       ("/index.xz".  "unxz")
        ("/index".     nil)
        (".Z".         "uncompress")
        (".Y".         "unyabba")
        (".gz".        "gunzip")
        (".z".         "gunzip")
        (".bz2" .      ("bzip2" "-dc"))
+       (".xz".        "unxz")
        ("".           nil)))
   "List of file name suffixes and associated decoding commands.
 Each entry should be (SUFFIX . STRING); the file is given to
@@ -481,7 +533,7 @@ in `Info-file-supports-index-cookies-list'."
   (or (assoc file Info-file-supports-index-cookies-list)
       ;; Skip virtual Info files
       (and (or (not (stringp file))
-              (member file '("dir" apropos history toc)))
+              (Info-virtual-file-p file))
            (setq Info-file-supports-index-cookies-list
                 (cons (cons file nil) Info-file-supports-index-cookies-list)))
       (save-excursion
@@ -563,9 +615,9 @@ in `Info-file-supports-index-cookies-list'."
   (let (same-window-buffer-names same-window-regexps)
     (info file-or-node)))
 
-;;;###autoload (add-hook 'same-window-regexps "\\*info\\*\\(\\|<[0-9]+>\\)")
+;;;###autoload (add-hook 'same-window-regexps (purecopy "\\*info\\*\\(\\|<[0-9]+>\\)"))
 
-;;;###autoload (put 'info 'info-file "emacs")
+;;;###autoload (put 'info 'info-file (purecopy "emacs"))
 ;;;###autoload
 (defun info (&optional file-or-node buffer)
   "Enter Info, the documentation browser.
@@ -660,59 +712,58 @@ Optional second argument NOERROR, if t, means if file is not found
 just return nil (no error)."
   ;; Convert filename to lower case if not found as specified.
   ;; Expand it.
-  (if (stringp filename)
-      (let (temp temp-downcase found)
-        (setq filename (substitute-in-file-name filename))
-       (cond
-        ((string= (downcase filename) "dir")
-         (setq found t))
-        (t
-         (let ((dirs (if (string-match "^\\./" filename)
-                          ;; If specified name starts with `./'
-                          ;; then just try current directory.
-                          '("./")
-                        (if (file-name-absolute-p filename)
-                            ;; No point in searching for an
-                            ;; absolute file name
-                            '(nil)
-                          (if Info-additional-directory-list
-                              (append Info-directory-list
-                                      Info-additional-directory-list)
-                            Info-directory-list)))))
-            ;; Search the directory list for file FILENAME.
-            (while (and dirs (not found))
-              (setq temp (expand-file-name filename (car dirs)))
-              (setq temp-downcase
-                    (expand-file-name (downcase filename) (car dirs)))
-              ;; Try several variants of specified name.
-              (let ((suffix-list Info-suffix-list)
-                   (lfn (if (fboundp 'msdos-long-file-names)
-                            (msdos-long-file-names)
-                          t)))
-                (while (and suffix-list (not found))
-                  (cond ((info-file-exists-p
-                          (info-insert-file-contents-1
-                           temp (car (car suffix-list)) lfn))
-                         (setq found temp))
-                        ((info-file-exists-p
-                          (info-insert-file-contents-1
-                           temp-downcase (car (car suffix-list)) lfn))
-                         (setq found temp-downcase))
-                       ((and (fboundp 'msdos-long-file-names)
-                             lfn
-                             (info-file-exists-p
-                              (info-insert-file-contents-1
-                               temp (car (car suffix-list)) nil)))
-                        (setq found temp)))
-                  (setq suffix-list (cdr suffix-list))))
-              (setq dirs (cdr dirs))))))
-        (if found
-            (setq filename found)
-          (if noerror
-              (setq filename nil)
-            (error "Info file %s does not exist" filename)))
-        filename)
-    (and (member filename '(apropos history toc)) filename)))
+  (cond
+   ((Info-virtual-call
+     (Info-virtual-fun 'find-file filename nil)
+     filename noerror))
+   ((stringp filename)
+    (let (temp temp-downcase found)
+      (setq filename (substitute-in-file-name filename))
+      (let ((dirs (if (string-match "^\\./" filename)
+                     ;; If specified name starts with `./'
+                     ;; then just try current directory.
+                     '("./")
+                   (if (file-name-absolute-p filename)
+                       ;; No point in searching for an
+                       ;; absolute file name
+                       '(nil)
+                     (if Info-additional-directory-list
+                         (append Info-directory-list
+                                 Info-additional-directory-list)
+                       Info-directory-list)))))
+       ;; Search the directory list for file FILENAME.
+       (while (and dirs (not found))
+         (setq temp (expand-file-name filename (car dirs)))
+         (setq temp-downcase
+               (expand-file-name (downcase filename) (car dirs)))
+         ;; Try several variants of specified name.
+         (let ((suffix-list Info-suffix-list)
+               (lfn (if (fboundp 'msdos-long-file-names)
+                        (msdos-long-file-names)
+                      t)))
+           (while (and suffix-list (not found))
+             (cond ((info-file-exists-p
+                     (info-insert-file-contents-1
+                      temp (car (car suffix-list)) lfn))
+                    (setq found temp))
+                   ((info-file-exists-p
+                     (info-insert-file-contents-1
+                      temp-downcase (car (car suffix-list)) lfn))
+                    (setq found temp-downcase))
+                   ((and (fboundp 'msdos-long-file-names)
+                         lfn
+                         (info-file-exists-p
+                          (info-insert-file-contents-1
+                           temp (car (car suffix-list)) nil)))
+                    (setq found temp)))
+             (setq suffix-list (cdr suffix-list))))
+         (setq dirs (cdr dirs))))
+      (if found
+         (setq filename found)
+       (if noerror
+           (setq filename nil)
+         (error "Info file %s does not exist" filename)))
+      filename))))
 
 (defun Info-find-node (filename nodename &optional no-going-back)
   "Go to an Info node specified as separate FILENAME and NODENAME.
@@ -755,17 +806,22 @@ otherwise, that defaults to `Top'."
   "Go to an Info node FILENAME and NODENAME, re-reading disk contents.
 When *info* is already displaying FILENAME and NODENAME, the window position
 is preserved, if possible."
-  (pop-to-buffer "*info*")
+  (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
   (let ((old-filename Info-current-file)
        (old-nodename Info-current-node)
+       (old-buffer-name (buffer-name))
        (pcolumn      (current-column))
        (pline        (count-lines (point-min) (line-beginning-position)))
        (wline        (count-lines (point-min) (window-start)))
+       (old-history-forward Info-history-forward)
        (old-history  Info-history)
        (new-history  (and Info-current-file
                           (list Info-current-file Info-current-node (point)))))
     (kill-buffer (current-buffer))
+    (pop-to-buffer (or old-buffer-name "*info*"))
+    (Info-mode)
     (Info-find-node filename nodename)
+    (setq Info-history-forward old-history-forward)
     (setq Info-history old-history)
     (if (and (equal old-filename Info-current-file)
             (equal old-nodename Info-current-node))
@@ -832,17 +888,16 @@ Value is the position at which a match was found, or nil if not found."
   (let ((case-fold-search case-fold)
        found)
     (save-excursion
-      (when (Info-node-at-bob-matching regexp)
-       (setq found (point)))
-      (while (and (not found)
-                 (search-forward "\n\^_" nil t))
-       (forward-line 1)
-       (let ((beg (point)))
-         (forward-line 1)
-         (when (re-search-backward regexp beg t)
-           (beginning-of-line)
-           (setq found (point)))))
-      found)))
+      (if (Info-node-at-bob-matching regexp)
+          (setq found (point))
+        (while (and (not found)
+                    (search-forward "\n\^_" nil t))
+          (forward-line 1)
+          (let ((beg (point)))
+            (forward-line 1)
+            (if (re-search-backward regexp beg t)
+                (setq found (line-beginning-position)))))))
+    found))
 
 (defun Info-find-node-in-buffer (regexp)
   "Find a node or anchor in the current buffer.
@@ -862,68 +917,81 @@ a case-insensitive match is tried."
   (setq Info-current-node nil)
   (unwind-protect
       (let ((case-fold-search t)
+           (virtual-fun (Info-virtual-fun 'find-node
+                                          (or filename Info-current-file)
+                                          nodename))
            anchorpos)
-        ;; Switch files if necessary
-        (or (null filename)
-            (equal Info-current-file filename)
-            (let ((inhibit-read-only t))
-              (setq Info-current-file nil
-                    Info-current-subfile nil
-                    Info-current-file-completions nil
-                    buffer-file-name nil)
-              (erase-buffer)
-             (cond
-              ((eq filename t)
-               (Info-insert-dir))
-              ((eq filename 'apropos)
-               (insert-buffer-substring " *info-apropos*"))
-              ((eq filename 'history)
-               (insert-buffer-substring " *info-history*"))
-              ((eq filename 'toc)
-               (insert-buffer-substring " *info-toc*"))
-              (t
-                (info-insert-file-contents filename nil)
-                (setq default-directory (file-name-directory filename))))
-              (set-buffer-modified-p nil)
-             (set (make-local-variable 'Info-file-supports-index-cookies)
-                  (Info-file-supports-index-cookies filename))
-
-              ;; See whether file has a tag table.  Record the location if yes.
-              (goto-char (point-max))
-              (forward-line -8)
-              ;; Use string-equal, not equal, to ignore text props.
-              (if (not (or (string-equal nodename "*")
-                           (not
-                            (search-forward "\^_\nEnd tag table\n" nil t))))
-                  (let (pos)
-                    ;; We have a tag table.  Find its beginning.
-                    ;; Is this an indirect file?
-                    (search-backward "\nTag table:\n")
-                    (setq pos (point))
-                    (if (save-excursion
-                          (forward-line 2)
-                          (looking-at "(Indirect)\n"))
-                        ;; It is indirect.  Copy it to another buffer
-                        ;; and record that the tag table is in that buffer.
-                        (let ((buf (current-buffer))
-                              (tagbuf
-                               (or Info-tag-table-buffer
-                                   (generate-new-buffer " *info tag table*"))))
-                          (setq Info-tag-table-buffer tagbuf)
-                          (with-current-buffer tagbuf
-                            (buffer-disable-undo (current-buffer))
-                            (setq case-fold-search t)
-                            (erase-buffer)
-                            (insert-buffer-substring buf))
-                          (set-marker Info-tag-table-marker
-                                      (match-end 0) tagbuf))
-                      (set-marker Info-tag-table-marker pos)))
-                (set-marker Info-tag-table-marker nil))
-              (setq Info-current-file
-                   (cond
-                    ((eq filename t) "dir")
-                    (t filename)))
-             ))
+       (cond
+        ((functionp virtual-fun)
+         (let ((filename (or filename Info-current-file)))
+           (setq buffer-read-only nil)
+           (setq Info-current-file filename
+                 Info-current-subfile nil
+                 Info-current-file-completions nil
+                 buffer-file-name nil)
+           (erase-buffer)
+           (Info-virtual-call virtual-fun filename nodename no-going-back)
+           (set-marker Info-tag-table-marker nil)
+           (setq buffer-read-only t)
+           (set-buffer-modified-p nil)
+           (set (make-local-variable 'Info-current-node-virtual) t)))
+        ((not (and
+               ;; Reread a file when moving from a virtual node.
+               (not Info-current-node-virtual)
+               (or (null filename)
+                   (equal Info-current-file filename))))
+         ;; Switch files if necessary
+         (let ((inhibit-read-only t))
+           (when Info-current-node-virtual
+             ;; When moving from a virtual node.
+             (set (make-local-variable 'Info-current-node-virtual) nil)
+             (if (null filename)
+                 (setq filename Info-current-file)))
+           (setq Info-current-file nil
+                 Info-current-subfile nil
+                 Info-current-file-completions nil
+                 buffer-file-name nil)
+           (erase-buffer)
+           (info-insert-file-contents filename nil)
+           (setq default-directory (file-name-directory filename))
+           (set-buffer-modified-p nil)
+           (set (make-local-variable 'Info-file-supports-index-cookies)
+                (Info-file-supports-index-cookies filename))
+
+           ;; See whether file has a tag table.  Record the location if yes.
+           (goto-char (point-max))
+           (forward-line -8)
+           ;; Use string-equal, not equal, to ignore text props.
+           (if (not (or (string-equal nodename "*")
+                        (not
+                         (search-forward "\^_\nEnd tag table\n" nil t))))
+               (let (pos)
+                 ;; We have a tag table.  Find its beginning.
+                 ;; Is this an indirect file?
+                 (search-backward "\nTag table:\n")
+                 (setq pos (point))
+                 (if (save-excursion
+                       (forward-line 2)
+                       (looking-at "(Indirect)\n"))
+                     ;; It is indirect.  Copy it to another buffer
+                     ;; and record that the tag table is in that buffer.
+                     (let ((buf (current-buffer))
+                           (tagbuf
+                            (or Info-tag-table-buffer
+                                (generate-new-buffer " *info tag table*"))))
+                       (setq Info-tag-table-buffer tagbuf)
+                       (with-current-buffer tagbuf
+                         (buffer-disable-undo (current-buffer))
+                         (setq case-fold-search t)
+                         (erase-buffer)
+                         (insert-buffer-substring buf))
+                       (set-marker Info-tag-table-marker
+                                   (match-end 0) tagbuf))
+                   (set-marker Info-tag-table-marker pos)))
+             (set-marker Info-tag-table-marker nil))
+           (setq Info-current-file filename)
+           )))
+
         ;; Use string-equal, not equal, to ignore text props.
         (if (string-equal nodename "*")
             (progn (setq Info-current-node nodename)
@@ -997,8 +1065,8 @@ a case-insensitive match is tried."
            (Info-select-node)
            (goto-char (point-min))
            (forward-line 1)                   ; skip header line
-           (when (> Info-breadcrumbs-depth 0) ; skip breadcrumbs line
-             (forward-line 1))
+           ;; (when (> Info-breadcrumbs-depth 0) ; skip breadcrumbs line
+           ;;   (forward-line 1))
 
            (cond (anchorpos
                    (let ((new-history (list Info-current-file
@@ -1390,7 +1458,8 @@ any double quotes or backslashes must be escaped (\\\",\\\\)."
              "\\(\0[\0-\37][[][^\0]*\0[\0-\37][]]\n?\\)"
              nil t)
        (let* ((start (match-beginning 1)))
-         (if (not (get-text-property start 'invisible))
+         (if (and (not (get-text-property start 'invisible))
+                  (not (get-text-property start 'display)))
              (put-text-property start (point) 'invisible t)))))
     (set-buffer-modified-p nil)))
 
@@ -1638,8 +1707,7 @@ If DIRECTION is `backward', search in the reverse direction."
                        (format "Regexp search%s: "
                                (if case-fold-search "" " case-sensitively")))
                      nil 'Info-search-history)))
-  (when transient-mark-mode
-    (deactivate-mark))
+  (deactivate-mark)
   (when (equal regexp "")
     (setq regexp (car Info-search-history)))
   (when regexp
@@ -1858,7 +1926,27 @@ If DIRECTION is `backward', search in the reverse direction."
   (setq Info-isearch-initial-node
        ;; Don't stop at initial node for nonincremental search.
        ;; Otherwise this variable is set after first search failure.
-       (and isearch-nonincremental Info-current-node)))
+       (and isearch-nonincremental Info-current-node))
+  (setq Info-isearch-initial-history      Info-history
+       Info-isearch-initial-history-list Info-history-list)
+  (add-hook 'isearch-mode-end-hook 'Info-isearch-end nil t))
+
+(defun Info-isearch-end ()
+  ;; Remove intermediate nodes (visited while searching)
+  ;; from the history.  Add only the last node (where Isearch ended).
+  (if (> (length Info-history)
+        (length Info-isearch-initial-history))
+      (setq Info-history
+           (nthcdr (- (length Info-history)
+                      (length Info-isearch-initial-history)
+                      1)
+                   Info-history)))
+  (if (> (length Info-history-list)
+        (length Info-isearch-initial-history-list))
+      (setq Info-history-list
+           (cons (car Info-history-list)
+                 Info-isearch-initial-history-list)))
+  (remove-hook 'isearch-mode-end-hook  'Info-isearch-end t))
 
 (defun Info-isearch-filter (beg-found found)
   "Test whether the current search hit is a visible useful text.
@@ -1998,6 +2086,26 @@ If SAME-FILE is non-nil, do not move to a different Info file."
     (Info-find-node filename nodename)
     (setq Info-history-forward history-forward)
     (goto-char opoint)))
+\f
+(add-to-list 'Info-virtual-files
+            '("\\`dir\\'"
+              (toc-nodes . Info-directory-toc-nodes)
+              (find-file . Info-directory-find-file)
+              (find-node . Info-directory-find-node)
+              ))
+
+(defun Info-directory-toc-nodes (filename)
+  "Directory-specific implementation of Info-directory-toc-nodes."
+  `(,filename
+    ("Top" nil nil nil)))
+
+(defun Info-directory-find-file (filename &optional noerror)
+  "Directory-specific implementation of Info-find-file."
+  filename)
+
+(defun Info-directory-find-node (filename nodename &optional no-going-back)
+  "Directory-specific implementation of Info-find-node-2."
+  (Info-insert-dir))
 
 ;;;###autoload
 (defun Info-directory ()
@@ -2005,72 +2113,88 @@ If SAME-FILE is non-nil, do not move to a different Info file."
   (interactive)
   (Info-find-node "dir" "top"))
 \f
+(add-to-list 'Info-virtual-files
+            '("\\`\\*History\\*\\'"
+              (toc-nodes . Info-history-toc-nodes)
+              (find-file . Info-history-find-file)
+              (find-node . Info-history-find-node)
+              ))
+
+(defun Info-history-toc-nodes (filename)
+  "History-specific implementation of Info-history-toc-nodes."
+  `(,filename
+    ("Top" nil nil nil)))
+
+(defun Info-history-find-file (filename &optional noerror)
+  "History-specific implementation of Info-find-file."
+  filename)
+
+(defun Info-history-find-node (filename nodename &optional no-going-back)
+  "History-specific implementation of Info-find-node-2."
+  (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: (dir)\n\n"
+                 (or filename Info-current-file) nodename))
+  (insert "Recently Visited Nodes\n")
+  (insert "**********************\n\n")
+  (insert "* Menu:\n\n")
+  (let ((hl (delete '("*History*" "Top") Info-history-list)))
+    (while hl
+      (let ((file (nth 0 (car hl)))
+           (node (nth 1 (car hl))))
+       (if (stringp file)
+           (insert "* " node ": ("
+                   (propertize (or (file-name-directory file) "") 'invisible t)
+                   (file-name-nondirectory file)
+                   ")" node ".\n")))
+      (setq hl (cdr hl)))))
+
 (defun Info-history ()
   "Go to a node with a menu of visited nodes."
   (interactive)
-  (let ((curr-file Info-current-file)
-        (curr-node Info-current-node)
-        p)
-    (with-current-buffer (get-buffer-create " *info-history*")
-      (let ((inhibit-read-only t))
-        (erase-buffer)
-        (goto-char (point-min))
-        (insert "\n\^_\nFile: history,  Node: Top,  Up: (dir)\n\n")
-        (insert "Recently Visited Nodes\n**********************\n\n")
-        (insert "* Menu:\n\n")
-        (let ((hl (delete '("history" "Top") Info-history-list)))
-          (while hl
-            (let ((file (nth 0 (car hl)))
-                  (node (nth 1 (car hl))))
-              (if (and (equal file curr-file)
-                       (equal node curr-node))
-                  (setq p (point)))
-              (if (stringp file)
-                 (insert "* " node ": ("
-                         (propertize (or (file-name-directory file) "") 'invisible t)
-                         (file-name-nondirectory file)
-                         ")" node ".\n")))
-            (setq hl (cdr hl))))))
-    (Info-find-node 'history "Top")
-    (goto-char (or p (point-min)))))
+  (Info-find-node "*History*" "Top")
+  (Info-next-reference)
+  (Info-next-reference))
+\f
+(add-to-list 'Info-virtual-nodes
+            '("\\`\\*TOC\\*\\'"
+              (find-node . Info-toc-find-node)
+              ))
+
+(defun Info-toc-find-node (filename nodename &optional no-going-back)
+  "Toc-specific implementation of Info-find-node-2."
+  (let* ((curr-file (substring-no-properties (or filename Info-current-file)))
+        (curr-node (substring-no-properties (or nodename Info-current-node)))
+        (node-list (Info-toc-nodes curr-file)))
+    (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                   curr-file curr-node))
+    (insert "Table of Contents\n")
+    (insert "*****************\n\n")
+    (insert "*Note Top::\n")
+    (Info-toc-insert
+     (nth 3 (assoc "Top" node-list))   ; get Top nodes
+     node-list 0 curr-file)
+    (unless (bobp)
+      (let ((Info-hide-note-references 'hide)
+           (Info-fontify-visited-nodes nil))
+       (setq Info-current-file filename Info-current-node "*TOC*")
+       (goto-char (point-min))
+       (narrow-to-region (or (re-search-forward "\n[\^_\f]\n" nil t)
+                             (point-min))
+                         (point-max))
+       (Info-fontify-node)
+       (widen)))))
 
 (defun Info-toc ()
   "Go to a node with table of contents of the current Info file.
 Table of contents is created from the tree structure of menus."
   (interactive)
-  (if (stringp Info-current-file)
-      (let ((curr-file (substring-no-properties Info-current-file))
-           (curr-node (substring-no-properties Info-current-node))
-           p)
-       (with-current-buffer (get-buffer-create " *info-toc*")
-         (let ((inhibit-read-only t)
-               (node-list (Info-toc-nodes curr-file)))
-           (erase-buffer)
-           (goto-char (point-min))
-           (insert "\n\^_\nFile: toc,  Node: Top,  Up: (dir)\n\n")
-           (insert "Table of Contents\n*****************\n\n")
-           (insert "*Note Top: (" curr-file ")Top.\n")
-           (Info-insert-toc
-            (nth 3 (assoc "Top" node-list)) ; get Top nodes
-            node-list 0 curr-file))
-         (if (not (bobp))
-             (let ((Info-hide-note-references 'hide)
-                   (Info-fontify-visited-nodes nil))
-               (Info-mode)
-               (setq Info-current-file 'toc Info-current-node "Top")
-               (goto-char (point-min))
-               (narrow-to-region (or (re-search-forward "\n[\^_\f]\n" nil t)
-                                     (point-min))
-                                 (point-max))
-               (Info-fontify-node)
-               (widen)))
-         (goto-char (point-min))
-         (if (setq p (search-forward (concat "*Note " curr-node ":") nil t))
-             (setq p (- p (length curr-node) 2))))
-       (Info-find-node 'toc "Top")
-       (goto-char (or p (point-min))))))
+  (Info-find-node Info-current-file "*TOC*")
+  (let ((prev-node (nth 1 (car Info-history))) p)
+    (goto-char (point-min))
+    (if (setq p (search-forward (concat "*Note " prev-node ":") nil t))
+       (setq p (- p (length prev-node) 2)))
+    (goto-char (or p (point-min)))))
 
-(defun Info-insert-toc (nodes node-list level curr-file)
+(defun Info-toc-insert (nodes node-list level curr-file)
   "Insert table of contents with references to nodes."
   (let ((section "Top"))
     (while nodes
@@ -2078,11 +2202,11 @@ Table of contents is created from the tree structure of menus."
         (unless (member (nth 2 node) (list nil section))
           (insert (setq section (nth 2 node)) "\n"))
         (insert (make-string level ?\t))
-        (insert "*Note " (car nodes) ": (" curr-file ")" (car nodes) ".\n")
-        (Info-insert-toc (nth 3 node) node-list (1+ level) curr-file)
+        (insert "*Note " (car nodes) ":\n")
+        (Info-toc-insert (nth 3 node) node-list (1+ level) curr-file)
         (setq nodes (cdr nodes))))))
 
-(defun Info-build-toc (file)
+(defun Info-toc-build (file)
   "Build table of contents from menus of Info FILE and its subfiles."
   (with-temp-buffer
     (let* ((file (and (stringp file) (Info-find-file file)))
@@ -2162,23 +2286,28 @@ where PARENT is the parent node extracted from the Up pointer,
 SECTION is the section name in the Top node where this node is placed,
 CHILDREN is a list of child nodes extracted from the node menu.")
 
-(defun Info-toc-nodes (file)
-  "Return a node list of Info FILE with parent-children information.
+(defun Info-toc-nodes (filename)
+  "Return a node list of Info FILENAME with parent-children information.
 This information is cached in the variable `Info-toc-nodes' with the help
-of the function `Info-build-toc'."
-  (or file (setq file Info-current-file))
-  (or (assoc file Info-toc-nodes)
-      ;; Skip virtual Info files
-      (and (or (not (stringp file))
-              (member file '("dir" apropos history toc)))
-           (push (cons file nil) Info-toc-nodes))
-      ;; Scan the entire manual and cache the result in Info-toc-nodes
-      (let ((nodes (Info-build-toc file)))
-       (push (cons file nodes) Info-toc-nodes)
-       nodes)
-      ;; If there is an error, still add nil to the cache
-      (push (cons file nil) Info-toc-nodes))
-  (cdr (assoc file Info-toc-nodes)))
+of the function `Info-toc-build'."
+  (cond
+   ((Info-virtual-call
+     (Info-virtual-fun 'toc-nodes (or filename Info-current-file) nil)
+     filename))
+   (t
+    (or filename (setq filename Info-current-file))
+    (or (assoc filename Info-toc-nodes)
+       ;; Skip virtual Info files
+       (and (or (not (stringp filename))
+                (Info-virtual-file-p filename))
+            (push (cons filename nil) Info-toc-nodes))
+       ;; Scan the entire manual and cache the result in Info-toc-nodes
+       (let ((nodes (Info-toc-build filename)))
+         (push (cons filename nodes) Info-toc-nodes)
+         nodes)
+       ;; If there is an error, still add nil to the cache
+       (push (cons filename nil) Info-toc-nodes))
+    (cdr (assoc filename Info-toc-nodes)))))
 
 \f
 (defun Info-follow-reference (footnotename &optional fork)
@@ -2193,11 +2322,8 @@ new buffer."
         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))
-
+       (setq eol (line-end-position)
+             bol (line-beginning-position))
        (goto-char (point-min))
        (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t)
         (setq str (match-string-no-properties 1))
@@ -2349,12 +2475,16 @@ Because of ambiguities, this should be concatenated with something like
             nextnode)
         (goto-char (point-min))
         (search-forward "\n* Menu:")
-        (if (not (memq action '(nil t)))
-            (re-search-forward
-             (concat "\n\\* +" (regexp-quote string) ":") nil t)
+        (cond
+         ((eq (car-safe action) 'boundaries) nil)
+         ((eq action 'lambda)
+          (re-search-forward
+           (concat "\n\\* +" (regexp-quote string) ":") nil t))
+         (t
           (let ((pattern (concat "\n\\* +\\("
                                  (regexp-quote string)
-                                 Info-menu-entry-name-re "\\):" Info-node-spec-re))
+                                 Info-menu-entry-name-re "\\):"
+                                 Info-node-spec-re))
                 completions
                 (complete-nodes Info-complete-nodes))
             ;; Check the cache.
@@ -2389,9 +2519,7 @@ Because of ambiguities, this should be concatenated with something like
                   (list Info-current-file Info-current-node
                         Info-complete-next-re string completions
                         Info-complete-nodes)))
-           (if action
-               (all-completions string completions predicate)
-             (try-completion string completions predicate))))))))
+            (complete-with-action action completions string predicate))))))))
 
 
 (defun Info-menu (menu-item &optional fork)
@@ -2402,12 +2530,10 @@ 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
+   (let (;; If point is within a menu item, use that item as the default
         (default nil)
         (p (point))
         beg
-        (last nil)
         (case-fold-search t))
      (save-excursion
        (goto-char (point-min))
@@ -2713,12 +2839,9 @@ parent node."
         (virtual-end
          (and Info-scroll-prefer-subnodes
               (save-excursion
-                (beginning-of-line)
-                (setq current-point (point))
+                (setq current-point (line-beginning-position))
                 (goto-char (point-min))
-                (search-forward "\n* Menu:"
-                                current-point
-                                t)))))
+                (search-forward "\n* Menu:" current-point t)))))
     (if (or virtual-end
            (pos-visible-in-window-p (point-min) nil t))
        (Info-last-preorder)
@@ -2792,7 +2915,7 @@ following nodes whose names also contain the word \"Index\"."
   (or (assoc file Info-index-nodes)
       ;; Skip virtual Info files
       (and (or (not (stringp file))
-              (member file '("dir" apropos history toc)))
+              (Info-virtual-file-p file))
            (setq Info-index-nodes (cons (cons file nil) Info-index-nodes)))
       (if (Info-file-supports-index-cookies file)
          ;; Find nodes with index cookie
@@ -2801,7 +2924,7 @@ following nodes whose names also contain the word \"Index\"."
                                              (setq file (Info-find-file file))))
                                        default-directory))
                 Info-history Info-history-list Info-fontify-maximum-menu-size
-                (main-file file) subfiles nodes node)
+                (main-file file) subfiles nodes)
            (condition-case nil
                (with-temp-buffer
                  (while (or main-file subfiles)
@@ -2844,7 +2967,7 @@ following nodes whose names also contain the word \"Index\"."
                  (Info-goto-node (car nodes))
                  (while (and (setq node (Info-extract-pointer "next" t))
                              (string-match "\\<Index\\>" node))
-                   (setq nodes (cons node nodes))
+                   (push node nodes)
                    (Info-goto-node node))))
            (error nil))
          (if nodes
@@ -2860,21 +2983,22 @@ following nodes whose names also contain the word \"Index\"."
 If NODE is nil, check the current Info node.
 If FILE is nil, check the current Info file."
   (or file (setq file Info-current-file))
-  (if (or (and node (not (equal node Info-current-node)))
-          (assoc file Info-index-nodes))
+  (if (and (or (and node (not (equal node Info-current-node)))
+              (assoc file Info-index-nodes))
+          (not Info-current-node-virtual))
       (member (or node Info-current-node) (Info-index-nodes file))
     ;; Don't search all index nodes if request is only for the current node
     ;; and file is not in the cache of index nodes
-    (if (Info-file-supports-index-cookies file)
-       (save-excursion
-         (goto-char (+ (or (save-excursion
-                             (search-backward "\n\^_" nil t))
-                           (point-min)) 2))
-         (search-forward "\0\b[index\0\b]"
-                         (or (save-excursion
-                               (search-forward "\n\^_" nil t))
-                             (point-max)) t))
-      (save-match-data
+    (save-match-data
+      (if (Info-file-supports-index-cookies file)
+         (save-excursion
+           (goto-char (+ (or (save-excursion
+                               (search-backward "\n\^_" nil t))
+                             (point-min)) 2))
+           (search-forward "\0\b[index\0\b]"
+                           (or (save-excursion
+                                 (search-forward "\n\^_" nil t))
+                               (point-max)) t))
        (string-match "\\<Index\\>" (or node Info-current-node ""))))))
 
 (defun Info-goto-index ()
@@ -2964,7 +3088,9 @@ Give an empty topic name to go to the Index node itself."
          num (1- num)))
   (Info-goto-node (nth 1 (car Info-index-alternatives)))
   (if (> (nth 3 (car Info-index-alternatives)) 0)
-      (forward-line (1- (nth 3 (car Info-index-alternatives))))
+      ;; Forward 2 lines less because `Info-find-node-2' initially
+      ;; puts point to the 2nd line.
+      (forward-line (- (nth 3 (car Info-index-alternatives)) 2))
     (forward-line 3)                   ; don't search in headers
     (let ((name (car (car Info-index-alternatives))))
       (Info-find-index-name name)))
@@ -3000,11 +3126,165 @@ Give an empty topic name to go to the Index node itself."
                  (Info-find-index-name (match-string 1 name))))
        (progn (beginning-of-line) t)  ;; non-nil for recursive call
       (goto-char (point-min)))))
-
-;;;###autoload
-(defun info-apropos (string)
-  "Grovel indices of all known Info files on your system for STRING.
-Build a menu of the possible matches."
+\f
+(add-to-list 'Info-virtual-nodes
+            '("\\`\\*Index.*\\*\\'"
+              (find-node . Info-virtual-index-find-node)
+              (slow . t)
+              ))
+
+(defvar Info-virtual-index-nodes nil
+  "Alist of cached matched index search nodes.
+Each element is ((FILENAME . TOPIC) MATCHES) where
+FILENAME is the file name of the manual,
+TOPIC is the search string given as an argument to `Info-virtual-index',
+MATCHES is a list of index matches found by `Info-index'.")
+
+(defun Info-virtual-index-find-node (filename nodename &optional no-going-back)
+  "Index-specific implementation of Info-find-node-2."
+  ;; Generate Index-like menu of matches
+  (if (string-match "^\\*Index for `\\(.+\\)'\\*$" nodename)
+      ;; Generate Index-like menu of matches
+      (let* ((topic (match-string 1 nodename))
+            (matches (cdr (assoc (cons (or filename Info-current-file) topic)
+                                 Info-virtual-index-nodes))))
+       (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: *Index*\n\n"
+                       (or filename Info-current-file) nodename))
+       (insert "Info Virtual Index\n")
+       (insert "******************\n\n")
+       (insert "Index entries that match `" topic "':\n\n")
+       (insert "\0\b[index\0\b]\n")
+       (if (null matches)
+           (insert "No matches found.\n")
+         (insert "* Menu:\n\n")
+         (dolist (entry matches)
+           (insert (format "* %-38s %s.%s\n"
+                           (format "%s [%s]:" (nth 0 entry) (nth 2 entry))
+                           (nth 1 entry)
+                           (if (nth 3 entry)
+                               (format " (line %s)" (nth 3 entry))
+                             ""))))))
+    ;; Else, Generate a list of previous search results
+    (let ((nodes (reverse Info-virtual-index-nodes)))
+      (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                     (or filename Info-current-file) nodename))
+      (insert "Info Virtual Index\n")
+      (insert "******************\n\n")
+      (insert "This is a list of search results produced by\n"
+             "`Info-virtual-index' for the current manual.\n\n")
+      (insert "* Menu:\n\n")
+      (dolist (nodeinfo nodes)
+       (when (equal (car (nth 0 nodeinfo)) (or filename Info-current-file))
+         (insert
+          (format "* %-20s %s.\n"
+                  (format "*Index for `%s'*::" (cdr (nth 0 nodeinfo)))
+                  (cdr (nth 0 nodeinfo)))))))))
+
+(defun Info-virtual-index (topic)
+  "Show a node with all lines in the index containing a string TOPIC.
+Like `Info-index' but displays a node with index search results.
+Give an empty topic name to go to the node with links to previous
+search results."
+  ;; `interactive' is a copy from `Info-index'
+  (interactive
+   (list
+    (let ((completion-ignore-case t)
+         (Info-complete-menu-buffer (clone-buffer))
+         (Info-complete-nodes (Info-index-nodes))
+         (Info-history-list nil))
+      (if (equal Info-current-file "dir")
+         (error "The Info directory node has no index; use m to select a manual"))
+      (unwind-protect
+         (with-current-buffer Info-complete-menu-buffer
+           (Info-goto-index)
+           (completing-read "Index topic: " 'Info-complete-menu-item))
+       (kill-buffer Info-complete-menu-buffer)))))
+  (if (equal topic "")
+      (Info-find-node Info-current-file "*Index*")
+    (unless (assoc (cons Info-current-file topic) Info-virtual-index-nodes)
+      (let ((orignode Info-current-node)
+           (ohist-list Info-history-list)
+           nodename)
+       ;; Reuse `Info-index' to set `Info-index-alternatives'.
+       (Info-index topic)
+       (push (cons (cons Info-current-file topic) Info-index-alternatives)
+             Info-virtual-index-nodes)
+       ;; Clean up unneccessary side-effects of `Info-index'.
+       (setq Info-history-list ohist-list)
+       (Info-goto-node orignode)
+       (message "")))
+    (Info-find-node Info-current-file (format "*Index for `%s'*" topic))))
+\f
+(add-to-list 'Info-virtual-files
+            '("\\`\\*Apropos\\*\\'"
+              (toc-nodes . Info-apropos-toc-nodes)
+              (find-file . Info-apropos-find-file)
+              (find-node . Info-apropos-find-node)
+              (slow . t)
+              ))
+
+(defvar Info-apropos-file "*Apropos*"
+  "Info file name of the virtual manual for matches of `info-apropos'.")
+
+(defvar Info-apropos-nodes nil
+  "Alist of cached apropos matched nodes.
+Each element is (NODENAME STRING MATCHES) where
+NODENAME is the name of the node that holds the search result,
+STRING is the search string given as an argument to `info-apropos',
+MATCHES is a list of index matches found by `Info-apropos-matches'.")
+
+(defun Info-apropos-toc-nodes (filename)
+  "Apropos-specific implementation of Info-apropos-toc-nodes."
+  (let ((nodes (mapcar 'car (reverse Info-apropos-nodes))))
+    `(,filename
+      ("Top" nil nil ,nodes)
+      ,@(mapcar (lambda (node) `(,node "Top" nil nil)) nodes))))
+
+(defun Info-apropos-find-file (filename &optional noerror)
+  "Apropos-specific implementation of Info-find-file."
+  filename)
+
+(defun Info-apropos-find-node (filename nodename &optional no-going-back)
+  "Apropos-specific implementation of Info-find-node-2."
+  (if (equal nodename "Top")
+      ;; Generate Top menu
+      (let ((nodes (reverse Info-apropos-nodes)))
+       (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: (dir)\n\n"
+                       Info-apropos-file nodename))
+       (insert "Apropos Index\n")
+       (insert "*************\n\n")
+       (insert "This is a list of search results produced by `info-apropos'.\n\n")
+       (insert "* Menu:\n\n")
+       (dolist (nodeinfo nodes)
+         (insert (format "* %-20s %s.\n"
+                         (format "%s::" (nth 0 nodeinfo))
+                         (nth 1 nodeinfo)))))
+    ;; Else, Generate Index-like menu of matches
+    (let* ((nodeinfo (assoc nodename Info-apropos-nodes))
+          (matches (nth 2 nodeinfo)))
+      (when matches
+       (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                       Info-apropos-file nodename))
+       (insert "Apropos Index\n")
+       (insert "*************\n\n")
+       (insert "Index entries that match `" (nth 1 nodeinfo) "':\n\n")
+       (insert "\0\b[index\0\b]\n")
+       (if (eq matches t)
+           (insert "No matches found.\n")
+         (insert "* Menu:\n\n")
+         (dolist (entry matches)
+           (insert (format "* %-38s (%s)%s.%s\n"
+                           (format "%s [%s]:" (nth 1 entry) (nth 0 entry))
+                           (nth 0 entry)
+                           (nth 2 entry)
+                           (if (nth 3 entry)
+                               (format " (line %s)" (nth 3 entry))
+                             "")))))))))
+
+(defun Info-apropos-matches (string)
+  "Collect STRING matches from all known Info files on your system.
+Return a list of matches where each element is in the format
+\((FILENAME INDEXTEXT NODENAME LINENUMBER))."
   (interactive "sIndex apropos: ")
   (unless (string= string "")
     (let ((pattern (format "\n\\* +\\([^\n]*%s[^\n]*\\):[ \t]+\\([^\n]+\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
@@ -3056,24 +3336,156 @@ Build a menu of the possible matches."
       (setq Info-history ohist
            Info-history-list ohist-list)
       (message "Searching indices...done")
-      (if (null matches)
-         (message "No matches found")
-       (with-current-buffer (get-buffer-create " *info-apropos*")
-         (erase-buffer)
-         (insert "\n\^_\nFile: apropos, Node: Index, Up: (dir)\n")
-         (insert "* Menu: \nNodes whose indices contain `" string "':\n\n")
-         (dolist (entry (nreverse matches))
-           (insert
-            (format "* %-38s (%s)%s.%s\n"
-                    (concat (nth 1 entry) " [" (nth 0 entry) "]:")
-                    (nth 0 entry)
-                    (nth 2 entry)
-                    (if (nth 3 entry)
-                        (concat " (line " (nth 3 entry) ")")
-                      "")))))
-       (Info-find-node 'apropos "Index")
-       (setq Info-complete-cache nil)))))
+      (or (nreverse matches) t))))
+
+;;;###autoload
+(defun info-apropos (string)
+  "Grovel indices of all known Info files on your system for STRING.
+Build a menu of the possible matches."
+  (interactive "sIndex apropos: ")
+  (if (equal string "")
+      (Info-find-node Info-apropos-file "Top")
+    (let* ((nodes Info-apropos-nodes) nodename)
+      (while (and nodes (not (equal string (nth 1 (car nodes)))))
+       (setq nodes (cdr nodes)))
+      (if nodes
+         (Info-find-node Info-apropos-file (car (car nodes)))
+       (setq nodename (format "Index for `%s'" string))
+       (push (list nodename string (Info-apropos-matches string))
+             Info-apropos-nodes)
+       (Info-find-node Info-apropos-file nodename)))))
+\f
+(add-to-list 'Info-virtual-files
+            '("\\`\\*Finder.*\\*\\'"
+              (find-file . Info-finder-find-file)
+              (find-node . Info-finder-find-node)
+              ))
+
+(defvar Info-finder-file "*Finder*"
+  "Info file name of the virtual Info keyword finder manual.")
+
+(defun Info-finder-find-file (filename &optional noerror)
+  "Finder-specific implementation of Info-find-file."
+  filename)
+
+(defvar finder-known-keywords)
+(declare-function find-library-name "find-func" (library))
+(declare-function finder-unknown-keywords "finder" ())
+(declare-function lm-commentary "lisp-mnt" (&optional file))
+(defvar finder-keywords-hash)
+(defvar package-alist)                  ; finder requires package
+
+(defun Info-finder-find-node (filename nodename &optional no-going-back)
+  "Finder-specific implementation of Info-find-node-2."
+  (require 'finder)
+  (cond
+   ((equal nodename "Top")
+    ;; Display Top menu with descriptions of the keywords
+    (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: (dir)\n\n"
+                   Info-finder-file nodename))
+    (insert "Finder Keywords\n")
+    (insert "***************\n\n")
+    (insert "* Menu:\n\n")
+    (dolist (assoc (append '((all . "All package info")
+                            (unknown . "unknown keywords"))
+                          finder-known-keywords))
+      (let ((keyword (car assoc)))
+       (insert (format "* %s %s.\n"
+                       (concat (symbol-name keyword) ": "
+                               "kw:" (symbol-name keyword) ".")
+                       (cdr assoc))))))
+   ((equal nodename "unknown")
+    ;; Display unknown keywords
+    (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                   Info-finder-file nodename))
+    (insert "Finder Unknown Keywords\n")
+    (insert "***********************\n\n")
+    (insert "* Menu:\n\n")
+    (mapc
+     (lambda (assoc)
+       (insert (format "* %-14s %s.\n"
+                      (concat (symbol-name (car assoc)) "::")
+                      (cdr assoc))))
+     (finder-unknown-keywords)))
+   ((equal nodename "all")
+    ;; Display all package info.
+    (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                   Info-finder-file nodename))
+    (insert "Finder Package Info\n")
+    (insert "*******************\n\n")
+    (dolist (package package-alist)
+      (insert (format "%s - %s\n"
+                     (format "*Note %s::" (nth 0 package))
+                     (nth 1 package)))))
+   ((string-match "\\`kw:" nodename)
+    (setq nodename (substring nodename (match-end 0)))
+    ;; Display packages that match the keyword
+    ;; or the list of keywords separated by comma.
+    (insert (format "\n\^_\nFile: %s,  Node: kw:%s,  Up: Top\n\n"
+                   Info-finder-file nodename))
+    (insert "Finder Packages\n")
+    (insert "***************\n\n")
+    (insert
+     "The following packages match the keyword `" nodename "':\n\n")
+    (insert "* Menu:\n\n")
+    (let ((keywords
+          (mapcar 'intern (if (string-match-p "," nodename)
+                              (split-string nodename ",[ \t\n]*" t)
+                            (list nodename))))
+         hits desc)
+      (dolist (kw keywords)
+       (push (copy-tree (gethash kw finder-keywords-hash)) hits))
+      (setq hits (delete-dups (apply 'append hits)))
+      (dolist (package hits)
+       (setq desc (cdr-safe (assq package package-alist)))
+       (when (vectorp desc)
+         (insert (format "* %-16s %s.\n"
+                         (concat (symbol-name package) "::")
+                         (aref desc 2)))))))
+   (t
+    ;; Display commentary section
+    (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                   Info-finder-file nodename))
+    (insert "Finder Commentary\n")
+    (insert "*****************\n\n")
+    (insert
+     "Commentary section of the package `" nodename "':\n\n")
+    (let ((str (lm-commentary (find-library-name nodename))))
+      (if (null str)
+         (insert "Can't find any Commentary section\n\n")
+       (insert
+        (with-temp-buffer
+          (insert str)
+          (goto-char (point-min))
+          (delete-blank-lines)
+          (goto-char (point-max))
+          (delete-blank-lines)
+          (goto-char (point-min))
+          (while (re-search-forward "^;+ ?" nil t)
+            (replace-match "" nil nil))
+          (buffer-string))))))))
 
+;;;###autoload
+(defun info-finder (&optional keywords)
+  "Display descriptions of the keywords in the Finder virtual manual.
+In interactive use, a prefix argument directs this command to read
+a list of keywords separated by comma.  After that, it displays a node
+with a list packages that contain all specified keywords."
+  (interactive
+   (when current-prefix-arg
+     (require 'finder)
+     (list
+      (completing-read-multiple
+       "Keywords (separated by comma): "
+       (mapcar 'symbol-name (mapcar 'car (append finder-known-keywords
+                                                (finder-unknown-keywords))))
+       nil t))))
+  (require 'finder)
+  (if keywords
+      (Info-find-node Info-finder-file (mapconcat 'identity keywords ", "))
+    (Info-find-node Info-finder-file "Top")))
+
+\f
 (defun Info-undefined ()
   "Make command be undefined in Info."
   (interactive)
@@ -3191,9 +3603,10 @@ If FORK is a string, it is the name to use for the new buffer."
 If FORK is non-nil, it is passed to `Info-goto-node'."
   (let (node)
     (cond
-     ((Info-get-token (point) "[hf]t?tps?://" "[hf]t?tps?://\\([^ \t\n\"`({<>})']+\\)")
-      (setq node t)
-      (browse-url (browse-url-url-at-point)))
+     ((setq node (Info-get-token (point) "[hf]t?tps?://"
+                                "\\([hf]t?tps?://[^ \t\n\"`({<>})']+\\)"))
+      (browse-url node)
+      (setq node t))
      ((setq node (Info-get-token (point) "\\*note[ \n\t]+"
                                 "\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))
       (Info-follow-reference node fork))
@@ -3215,6 +3628,19 @@ If FORK is non-nil, it is passed to `Info-goto-node'."
      ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)"))
       (Info-goto-node node fork)))
     node))
+
+(defun Info-mouse-follow-link (click)
+  "Follow a link where you click."
+  (interactive "e")
+  (let* ((position (event-start click))
+        (posn-string (and position (posn-string position)))
+        (string (car-safe posn-string))
+        (string-pos (cdr-safe posn-string))
+        (link-args (and string string-pos
+                        (get-text-property string-pos 'link-args string))))
+    (when link-args
+      (Info-goto-node link-args))))
+
 \f
 (defvar Info-mode-map
   (let ((map (make-keymap)))
@@ -3248,6 +3674,7 @@ If FORK is non-nil, it is passed to `Info-goto-node'."
     (define-key map "g" 'Info-goto-node)
     (define-key map "h" 'Info-help)
     (define-key map "i" 'Info-index)
+    (define-key map "I" 'Info-virtual-index)
     (define-key map "l" 'Info-history-back)
     (define-key map "L" 'Info-history)
     (define-key map "m" 'Info-menu)
@@ -3321,6 +3748,8 @@ If FORK is non-nil, it is passed to `Info-goto-node'."
      :help "Look for a string in the index items"]
     ["Next Matching Item" Info-index-next :active Info-index-alternatives
      :help "Look for another occurrence of previous item"]
+    ["Lookup a string and display index of results..." Info-virtual-index
+     :help "Look for a string in the index items and display node with results"]
     ["Lookup a string in all indices..." info-apropos
      :help "Look for a string in the indices of all manuals"])
    ["Copy Node Name" Info-copy-current-node-name
@@ -3333,19 +3762,31 @@ If FORK is non-nil, it is passed to `Info-goto-node'."
 (defvar info-tool-bar-map
   (let ((map (make-sparse-keymap)))
     (tool-bar-local-item-from-menu 'Info-history-back "left-arrow" map Info-mode-map
-                                  :rtl "right-arrow")
+                                  :rtl "right-arrow"
+                                  :label "Back"
+                                  :vert-only t)
     (tool-bar-local-item-from-menu 'Info-history-forward "right-arrow" map Info-mode-map
-                                  :rtl "left-arrow")
+                                  :rtl "left-arrow"
+                                  :label "Forward"
+                                  :vert-only t)
+    (define-key-after map [separator-1] menu-bar-separator)
     (tool-bar-local-item-from-menu 'Info-prev "prev-node" map Info-mode-map
                                   :rtl "next-node")
     (tool-bar-local-item-from-menu 'Info-next "next-node" map Info-mode-map
                                   :rtl "prev-node")
-    (tool-bar-local-item-from-menu 'Info-up "up-node" map Info-mode-map)
-    (tool-bar-local-item-from-menu 'Info-top-node "home" map Info-mode-map)
+    (tool-bar-local-item-from-menu 'Info-up "up-node" map Info-mode-map
+                                  :vert-only t)
+    (define-key-after map [separator-2] menu-bar-separator)
+    (tool-bar-local-item-from-menu 'Info-top-node "home" map Info-mode-map
+                                  :vert-only t)
     (tool-bar-local-item-from-menu 'Info-goto-node "jump-to" map Info-mode-map)
-    (tool-bar-local-item-from-menu 'Info-index "index" map Info-mode-map)
-    (tool-bar-local-item-from-menu 'Info-search "search" map Info-mode-map)
-    (tool-bar-local-item-from-menu 'Info-exit "exit" map Info-mode-map)
+    (define-key-after map [separator-3] menu-bar-separator)
+    (tool-bar-local-item-from-menu 'Info-index "index" map Info-mode-map
+                                  :label "Index")
+    (tool-bar-local-item-from-menu 'Info-search "search" map Info-mode-map
+                                  :vert-only t)
+    (tool-bar-local-item-from-menu 'Info-exit "exit" map Info-mode-map
+                                  :vert-only t)
     map))
 
 (defvar Info-menu-last-node nil)
@@ -3443,7 +3884,7 @@ With a zero prefix arg, put the name inside a function call to `info'."
 
 ;; Autoload cookie needed by desktop.el
 ;;;###autoload
-(defun Info-mode ()
+(define-derived-mode Info-mode nil "Info"
   "Info mode provides commands for browsing through the Info documentation tree.
 Documentation in Info is divided into \"nodes\", each of which discusses
 one topic and contains references to other nodes which discuss related
@@ -3495,6 +3936,7 @@ Advanced commands:
 \\[isearch-forward], \\[isearch-forward-regexp]        Use Isearch to search through multiple Info nodes.
 \\[Info-index] Search for a topic in this manual's Index and go to index entry.
 \\[Info-index-next]    (comma) Move to the next match from a previous \\<Info-mode-map>\\[Info-index] command.
+\\[Info-virtual-index] Look for a string and display the index node with results.
 \\[info-apropos]       Look for a string in the indices of all manuals.
 \\[Info-goto-node]     Move to node specified by name.
          You may include a filename as well, as (FILENAME)NODENAME.
@@ -3504,23 +3946,17 @@ Advanced commands:
 \\[clone-buffer]       Select a new cloned Info buffer in another window.
 \\[universal-argument] \\[info]        Move to new Info file with completion.
 \\[universal-argument] N \\[info]      Select Info buffer with prefix number in the name *info*<N>."
-  (kill-all-local-variables)
-  (setq major-mode 'Info-mode)
-  (setq mode-name "Info")
+  :syntax-table text-mode-syntax-table
+  :abbrev-table text-mode-abbrev-table
   (setq tab-width 8)
-  (use-local-map Info-mode-map)
   (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)
   (setq case-fold-search t)
   (setq buffer-read-only t)
   (make-local-variable 'Info-current-file)
   (make-local-variable 'Info-current-subfile)
   (make-local-variable 'Info-current-node)
-  (make-local-variable 'Info-tag-table-marker)
-  (setq Info-tag-table-marker (make-marker))
-  (make-local-variable 'Info-tag-table-buffer)
-  (setq Info-tag-table-buffer nil)
+  (set (make-local-variable 'Info-tag-table-marker) (make-marker))
+  (set (make-local-variable 'Info-tag-table-buffer) nil)
   (make-local-variable 'Info-history)
   (make-local-variable 'Info-history-forward)
   (make-local-variable 'Info-index-alternatives)
@@ -3529,12 +3965,10 @@ Advanced commands:
            '(:eval (get-text-property (point-min) 'header-line))))
   (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)
-  (make-local-variable 'desktop-save-buffer)
-  (make-local-variable 'widen-automatically)
-  (setq widen-automatically nil)
-  (setq desktop-save-buffer 'Info-desktop-buffer-misc-data)
+  (set (make-local-variable 'line-move-ignore-invisible) t)
+  (set (make-local-variable 'desktop-save-buffer)
+       'Info-desktop-buffer-misc-data)
+  (set (make-local-variable 'widen-automatically) nil)
   (add-hook 'kill-buffer-hook 'Info-kill-buffer nil t)
   (add-hook 'clone-buffer-hook 'Info-clone-buffer nil t)
   (add-hook 'change-major-mode-hook 'font-lock-defontify nil t)
@@ -3553,8 +3987,7 @@ Advanced commands:
        'Info-revert-buffer-function)
   (Info-set-mode-line)
   (set (make-local-variable 'bookmark-make-record-function)
-       'Info-bookmark-make-record)
-  (run-mode-hooks 'Info-mode-hook))
+       'Info-bookmark-make-record))
 
 ;; When an Info buffer is killed, make sure the associated tags buffer
 ;; is killed too.
@@ -3709,7 +4142,7 @@ in the first element of the returned list (which is treated specially in
              (cdr where))
       where)))
 
-;;;###autoload (put 'Info-goto-emacs-command-node 'info-file "emacs")
+;;;###autoload (put 'Info-goto-emacs-command-node 'info-file (purecopy "emacs"))
 ;;;###autoload
 (defun Info-goto-emacs-command-node (command)
   "Go to the Info node in the Emacs manual for command COMMAND.
@@ -3751,7 +4184,7 @@ COMMAND must be a symbol or string."
                         (if (> num-matches 2) "them" "it")))))
       (error "Couldn't find documentation for %s" command))))
 
-;;;###autoload (put 'Info-goto-emacs-key-command-node 'info-file "emacs")
+;;;###autoload (put 'Info-goto-emacs-key-command-node 'info-file (purecopy "emacs"))
 ;;;###autoload
 (defun Info-goto-emacs-key-command-node (key)
   "Go to the node in the Emacs manual which describes the command bound to KEY.
@@ -3764,7 +4197,7 @@ the variable `Info-file-list-for-emacs'."
   (let ((command (key-binding key)))
     (cond ((null command)
           (message "%s is undefined" (key-description key)))
-         ((and (interactive-p)
+         ((and (called-interactively-p 'interactive)
                (eq command 'execute-extended-command))
           (Info-goto-emacs-command-node
            (read-command "Find documentation for command: ")))
@@ -3801,11 +4234,22 @@ the variable `Info-file-list-for-emacs'."
     keymap)
   "Keymap to put on the Up link in the text or the header line.")
 
-(defun Info-insert-breadcrumbs ()
+(defvar Info-link-keymap
+  (let ((keymap (make-sparse-keymap)))
+    (define-key keymap [header-line mouse-1] 'Info-mouse-follow-link)
+    (define-key keymap [header-line mouse-2] 'Info-mouse-follow-link)
+    (define-key keymap [header-line down-mouse-1] 'ignore)
+    (define-key keymap [mouse-2] 'Info-mouse-follow-link)
+    (define-key keymap [follow-link] 'mouse-face)
+    keymap)
+  "Keymap to put on the link in the text or the header line.")
+
+(defun Info-breadcrumbs ()
   (let ((nodes (Info-toc-nodes Info-current-file))
        (node Info-current-node)
         (crumbs ())
-        (depth Info-breadcrumbs-depth))
+        (depth Info-breadcrumbs-depth)
+       line)
 
     ;; Get ancestors from the cached parent-children node info
     (while (and (not (equal "Top" node)) (> depth 0))
@@ -3830,17 +4274,27 @@ the variable `Info-file-list-for-emacs'."
                 (format "(%s)Top"
                         (if (stringp Info-current-file)
                             (file-name-nondirectory Info-current-file)
-                          ;; Can be `toc', `apropos', or even `history'.
+                          ;; Some legacy code can still use a symbol.
                           Info-current-file)))))
-         (insert (if (bolp) "" " > ")
-                 (cond
-                  ((null node) "...")
-                  ((equal node Info-current-node)
-                   ;; No point linking to ourselves.
-                   (propertize text 'font-lock-face 'info-header-node))
-                  (t
-                   (concat "*Note " text "::"))))))
-      (insert "\n"))))
+         (setq line (concat
+                     line
+                     (if (null line) "" " > ")
+                     (cond
+                      ((null node) "...")
+                      ((equal node Info-current-node)
+                       ;; No point linking to ourselves.
+                       (propertize text 'font-lock-face 'info-header-node))
+                      (t
+                       (propertize text
+                                   'mouse-face 'highlight
+                                   'font-lock-face 'info-header-xref
+                                   'help-echo "mouse-2: Go to node"
+                                   'keymap Info-link-keymap
+                                   'link-args text)))))))
+      (setq line (concat line "\n")))
+    ;; (font-lock-append-text-property 0 (length line)
+    ;;                                     'font-lock-face 'header-line line)
+    line))
 
 (defun Info-fontify-node ()
   "Fontify the node."
@@ -3887,8 +4341,8 @@ the variable `Info-file-list-for-emacs'."
                ((string-equal (downcase tag) "next") Info-next-link-keymap)
                ((string-equal (downcase tag) "up"  ) Info-up-link-keymap))))))
 
-        (when (> Info-breadcrumbs-depth 0)
-          (Info-insert-breadcrumbs))
+        ;; (when (> Info-breadcrumbs-depth 0)
+        ;;   (insert (Info-breadcrumbs)))
 
         ;; Treat header line.
         (when Info-use-header-line
@@ -3920,7 +4374,9 @@ the variable `Info-file-list-for-emacs'."
             ;; that is in the header, if it is just part.
             (cond
              ((> Info-breadcrumbs-depth 0)
-              (put-text-property (point-min) (1+ header-end) 'invisible t))
+             (let ((ov (make-overlay (point-min) (1+ header-end))))
+               (overlay-put ov 'display (Info-breadcrumbs))
+               (overlay-put ov 'evaporate t)))
              ((not (bobp))
               ;; Hide the punctuation at the end, too.
               (skip-chars-backward " \t,")
@@ -4414,42 +4870,59 @@ BUFFER is the buffer speedbar is requesting buttons for."
 
 (defun Info-desktop-buffer-misc-data (desktop-dirname)
   "Auxiliary information to be saved in desktop file."
-  (unless (member Info-current-file '(apropos history toc nil))
-    (list Info-current-file Info-current-node)))
+  (list Info-current-file
+       Info-current-node
+       ;; Additional data as an association list.
+       (delq nil (list
+                  (and Info-history
+                       (cons 'history Info-history))
+                  (and (Info-virtual-fun
+                        'slow Info-current-file Info-current-node)
+                       (cons 'slow t))))))
 
 (defun Info-restore-desktop-buffer (desktop-buffer-file-name
                                     desktop-buffer-name
                                     desktop-buffer-misc)
   "Restore an Info buffer specified in a desktop file."
-  (let ((first (nth 0 desktop-buffer-misc))
-        (second (nth 1 desktop-buffer-misc)))
-  (when (and first second)
-    (when desktop-buffer-name
-      (set-buffer (get-buffer-create desktop-buffer-name))
-      (Info-mode))
-    (Info-find-node first second)
-    (current-buffer))))
+  (let* ((file (nth 0 desktop-buffer-misc))
+        (node (nth 1 desktop-buffer-misc))
+        (data (nth 2 desktop-buffer-misc))
+        (hist (assq 'history data))
+        (slow (assq 'slow data)))
+    ;; Don't restore nodes slow to regenerate.
+    (unless slow
+      (when (and file node)
+       (when desktop-buffer-name
+         (set-buffer (get-buffer-create desktop-buffer-name))
+         (Info-mode))
+       (Info-find-node file node)
+       (when hist
+         (setq Info-history (cdr hist)))
+       (current-buffer)))))
 
 (add-to-list 'desktop-buffer-mode-handlers
             '(Info-mode . Info-restore-desktop-buffer))
 
 ;;;; Bookmark support
-(declare-function bookmark-make-record-default "bookmark" (&optional pos-only))
+(declare-function bookmark-make-record-default
+                  "bookmark" (&optional no-file no-context posn))
 (declare-function bookmark-prop-get "bookmark" (bookmark prop))
 (declare-function bookmark-default-handler "bookmark" (bmk))
 (declare-function bookmark-get-bookmark-record "bookmark" (bmk))
 
 (defun Info-bookmark-make-record ()
+  "This implements the `bookmark-make-record-function' type (which see)
+for Info nodes."
   `(,Info-current-node
-    ,@(bookmark-make-record-default 'point-only)
+    ,@(bookmark-make-record-default 'no-file)
     (filename . ,Info-current-file)
     (info-node . ,Info-current-node)
     (handler . Info-bookmark-jump)))
 
 ;;;###autoload
 (defun Info-bookmark-jump (bmk)
-  ;; This implements the `handler' function interface for record type returned
-  ;; by `Info-bookmark-make-record', which see.
+  "This implements the `handler' function interface for the record
+type returned by `Info-bookmark-make-record', which see."
   (let* ((file                   (bookmark-prop-get bmk 'filename))
          (info-node              (bookmark-prop-get bmk 'info-node))
          (buf (save-window-excursion    ;FIXME: doesn't work with frames!
@@ -4457,9 +4930,8 @@ BUFFER is the buffer speedbar is requesting buttons for."
     ;; Use bookmark-default-handler to move to the appropriate location
     ;; within the node.
     (bookmark-default-handler
-     (list* "" `(buffer . ,buf) (bookmark-get-bookmark-record bmk)))))
+     `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bmk)))))
 
 (provide 'info)
 
-;; arch-tag: f2480fe2-2139-40c1-a49b-6314991164ac
 ;;; info.el ends here