From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Juri Linkov Newsgroups: gmane.emacs.devel Subject: Re: Virtual Info files and nodes Date: Tue, 30 Jun 2009 02:55:19 +0300 Organization: JURTA Message-ID: <87zlbq389k.fsf@mail.jurta.org> References: <87ws6wnj3s.fsf@mail.jurta.org> <87prcnhn4i.fsf@mail.jurta.org> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1246321580 25110 80.91.229.12 (30 Jun 2009 00:26:20 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Tue, 30 Jun 2009 00:26:20 +0000 (UTC) Cc: emacs-devel@gnu.org To: Stefan Monnier Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Jun 30 02:26:13 2009 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1MLRAe-0005RL-Dh for ged-emacs-devel@m.gmane.org; Tue, 30 Jun 2009 02:26:13 +0200 Original-Received: from localhost ([127.0.0.1]:44377 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MLRAd-0007DH-9G for ged-emacs-devel@m.gmane.org; Mon, 29 Jun 2009 20:25:59 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MLRAY-00079p-8a for emacs-devel@gnu.org; Mon, 29 Jun 2009 20:25:54 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MLRAT-000747-Ri for emacs-devel@gnu.org; Mon, 29 Jun 2009 20:25:53 -0400 Original-Received: from [199.232.76.173] (port=49121 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MLRAT-00073v-O8 for emacs-devel@gnu.org; Mon, 29 Jun 2009 20:25:49 -0400 Original-Received: from smtp-out3.starman.ee ([85.253.0.5]:55227 helo=smtp-gw1.starman.ee) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MLRAS-0007wq-Lj for emacs-devel@gnu.org; Mon, 29 Jun 2009 20:25:49 -0400 Original-Received: from mx2.starman.ee (mx2.starman.ee [62.65.192.17]) by smtp-gw1.starman.ee (Postfix) with ESMTP id 424EDA21520; Tue, 30 Jun 2009 03:25:45 +0300 (EEST) X-Virus-Scanned: by Amavisd-New at mx2.starman.ee Original-Received: from mail.starman.ee (82.131.55.132.cable.starman.ee [82.131.55.132]) by mx2.starman.ee (Postfix) with ESMTP id 101AB3F4053; Tue, 30 Jun 2009 03:25:39 +0300 (EEST) In-Reply-To: (Stefan Monnier's message of "Mon, 29 Jun 2009 10:42:28 +0200") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1.50 (x86_64-pc-linux-gnu) X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:111824 Archived-At: >>> - Why check (stringp filename) in Info-virtual-file? Isn't it an >>> error to call it with something else than a string. >> >> Really only nil can be expected now, > > Can it? In which circumstance? `filename' can be nil when `Info-virtual-fun' is called with non-nil `nodename' to check for a node name when a file name is irrelevant - its argument value is nil in this case. >> but `stringp' is a precaution >> against t (the current internal value for "dir" ) and former symbol >> values `apropos', `toc' (maybe some external packages still use them). > > Please mention it in a comment. Ok. >> Info-virtual-fun expects an argument `op' for an operation type, >> but these places call (Info-virtual-file file) with only a file name >> argument to check whether a file name is virtual (if its name is defined >> in a list of definitions of virtual Info files). > > Why not provide a dummy `find-file' value for the `op'? > And wrap this in an Info-virtual-file-p function? This is done in the next version below with other fixes including the handling of "dir": Index: lisp/info.el =================================================================== RCS file: /sources/emacs/emacs/lisp/info.el,v retrieving revision 1.559 diff -c -w -b -r1.559 info.el *** lisp/info.el 23 May 2009 23:31:52 -0000 1.559 --- lisp/info.el 29 Jun 2009 23:50:55 -0000 *************** *** 329,334 **** --- 329,381 ---- (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 choosen to not cause file name clashes with + real 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 choosen to not cause node name clashes with + real 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))) + + (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. *************** *** 481,487 **** (or (assoc file Info-file-supports-index-cookies-list) ;; Skip virtual Info files (and (or (not (stringp file)) ! (member file '("dir" apropos history toc))) (setq Info-file-supports-index-cookies-list (cons (cons file nil) Info-file-supports-index-cookies-list))) (save-excursion --- 528,534 ---- (or (assoc file Info-file-supports-index-cookies-list) ;; Skip virtual Info files (and (or (not (stringp file)) ! (Info-virtual-file-p file)) (setq Info-file-supports-index-cookies-list (cons (cons file nil) Info-file-supports-index-cookies-list))) (save-excursion *************** *** 660,672 **** 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. --- 707,719 ---- just return nil (no error)." ;; Convert filename to lower case if not found as specified. ;; Expand it. ! (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. *************** *** 705,718 **** 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))) (defun Info-find-node (filename nodename &optional no-going-back) "Go to an Info node specified as separate FILENAME and NODENAME. --- 752,764 ---- 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. *************** *** 862,889 **** (setq Info-current-node nil) (unwind-protect (let ((case-fold-search t) 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)) --- 908,944 ---- (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) ! (cond ! ((functionp virtual-fun) ! (let ((inhibit-read-only t) ! (filename (or filename Info-current-file))) ! (setq buffer-file-name nil) ! (erase-buffer) ! (setq Info-current-file filename) ! (Info-virtual-call virtual-fun filename nodename no-going-back) ! (set-buffer-modified-p nil) ! (set-marker Info-tag-table-marker 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)) + (if (and Info-current-node-virtual (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)) *************** *** 919,929 **** (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))) ! )) ;; Use string-equal, not equal, to ignore text props. (if (string-equal nodename "*") (progn (setq Info-current-node nodename) --- 974,982 ---- (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) *************** *** 1999,2076 **** (setq Info-history-forward history-forward) (goto-char opoint))) ;;;###autoload (defun Info-directory () "Go to the Info directory node." (interactive) (Info-find-node "dir" "top")) ! (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))))) ! (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)))))) ! (defun Info-insert-toc (nodes node-list level curr-file) "Insert table of contents with references to nodes." (let ((section "Top")) (while nodes --- 2052,2165 ---- (setq Info-history-forward history-forward) (goto-char opoint))) + (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 () "Go to the Info directory node." (interactive) (Info-find-node "dir" "top")) ! (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) ! (Info-find-node "*History*" "Top") ! (Info-next-reference) ! (Info-next-reference)) ! ! (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) ! (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-toc-insert (nodes node-list level curr-file) "Insert table of contents with references to nodes." (let ((section "Top")) (while nodes *************** *** 2078,2088 **** (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) (setq nodes (cdr nodes)))))) ! (defun Info-build-toc (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))) --- 2167,2177 ---- (unless (member (nth 2 node) (list nil section)) (insert (setq section (nth 2 node)) "\n")) (insert (make-string level ?\t)) ! (insert "*Note " (car nodes) ":: \n") ! (Info-toc-insert (nth 3 node) node-list (1+ level) curr-file) (setq nodes (cdr nodes)))))) ! (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,2184 **** 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. 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))) (defun Info-follow-reference (footnotename &optional fork) --- 2251,2278 ---- 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 (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-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))))) (defun Info-follow-reference (footnotename &optional fork) *************** *** 2792,2798 **** (or (assoc file Info-index-nodes) ;; Skip virtual Info files (and (or (not (stringp file)) ! (member file '("dir" apropos history toc))) (setq Info-index-nodes (cons (cons file nil) Info-index-nodes))) (if (Info-file-supports-index-cookies file) ;; Find nodes with index cookie --- 2886,2892 ---- (or (assoc file Info-index-nodes) ;; Skip virtual Info files (and (or (not (stringp file)) ! (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 *************** *** 2860,2867 **** 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)) (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 --- 2954,2962 ---- 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 (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 *************** *** 3001,3010 **** (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." (interactive "sIndex apropos: ") (unless (string= string "") (let ((pattern (format "\n\\* +\\([^\n]*%s[^\n]*\\):[ \t]+\\([^\n]+\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?" --- 3096,3251 ---- (progn (beginning-of-line) t) ;; non-nil for recursive call (goto-char (point-min))))) ! (add-to-list 'Info-virtual-nodes ! '("\\`\\*Index.*\\*\\'" ! (find-node . Info-virtual-index-find-node) ! )) ! ! (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." ! ;; `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) ! (Info-index topic) ! (setq Info-history-list ohist-list) ! (Info-goto-node orignode) ! (push (cons (cons Info-current-file topic) Info-index-alternatives) ! Info-virtual-index-nodes))) ! (Info-find-node Info-current-file (format "*Index for `%s'*" topic)))) ! ! (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) ! )) ! ! (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,3078 **** (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))))) (defun Info-undefined () "Make command be undefined in Info." --- 3297,3320 ---- (setq Info-history ohist Info-history-list ohist-list) (message "Searching indices...done") ! (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))))) (defun Info-undefined () "Make command be undefined in Info." *************** *** 3248,3253 **** --- 3490,3496 ---- (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) *************** *** 3830,3836 **** (format "(%s)Top" (if (stringp Info-current-file) (file-name-nondirectory Info-current-file) - ;; Can be `toc', `apropos', or even `history'. Info-current-file))))) (insert (if (bolp) "" " > ") (cond --- 4073,4078 ---- *************** *** 4414,4420 **** (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))) (defun Info-restore-desktop-buffer (desktop-buffer-file-name --- 4656,4663 ---- (defun Info-desktop-buffer-misc-data (desktop-dirname) "Auxiliary information to be saved in desktop file." ! (unless (or (null Info-current-file) ! (Info-virtual-file-p Info-current-file)) (list Info-current-file Info-current-node))) (defun Info-restore-desktop-buffer (desktop-buffer-file-name -- Juri Linkov http://www.jurta.org/emacs/