* Virtual Info files and nodes
@ 2009-06-28 21:28 Juri Linkov
2009-06-29 0:14 ` Stefan Monnier
0 siblings, 1 reply; 7+ messages in thread
From: Juri Linkov @ 2009-06-28 21:28 UTC (permalink / raw
To: emacs-devel
I'd like to present a patch that properly implements virtual Info files
and nodes. It plugs in to just three functions in the Info package and
provides an API to redefine these functions to dynamically generate
either a virtual file or a virtual node:
Info-toc-nodes: property `toc-nodes'
Info-find-file: property `find-file'
Info-find-node-2: property `find-node'
Each class of virtual files or nodes should register itself either
to the `Info-virtual-files' or `Info-virtual-nodes' global variables.
The first element of a definition should be a regexp that matches
a class of virtual files or nodes, and the rest is an alist that
maps a property name to the overriding function name. For instance,
a definition for the Apropos virtual file is:
'("^\\*Apropos\\*$"
(toc-nodes . Info-apropos-toc-nodes)
(find-file . Info-apropos-find-file)
(find-node . Info-apropos-find-node)
))
All this is like routes and controllers used in Web services to generate
dynamic HTML pages, where a Web server name corresponds to the Info file
name, and an URL path corresponds to the Info node name. In a similar
way this feature generates dynamic Info pages instead of static Info pages.
This patch reimplements History, Table of Contents and Apropos, and also
adds new virtual nodes for the results of Info Index search:
1. Since History is a global list that refers to different visited Info
manuals, it should be a virtual Info file instead of a node.
2. Unlike History, each Table of Contents belongs to one manual, so it
should be a virtual node within the current Info manual. It was
incorrectly implemented as a virtual file that causes several problems,
but the following patch fixes this by implementing a virtual node.
3. Apropos collects matches from all available Info manuals, so it is
a virtual file with the Top node that refers to previous results.
4. Index search collects index entries from one Info manuals so it provides
a virtual node for each search string and a separate virtual node
with references to virtual nodes with previous results.
A new keybinding `I' reads a string and displays a virtual node with
index search matches. And `I RET' with an empty string displays
a virtual node with a list of previous results.
This patch leaves the processing of normal static Info manuals unaffected.
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 28 Jun 2009 21:27:28 -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 (filename)
+ "Return a definition of the virtual Info file FILENAME."
+ (and (stringp filename)
+ (assoc-default filename Info-virtual-files 'string-match)))
+
+ (defun Info-virtual-node (nodename)
+ "Return a definition of the virtual Info node NODENAME."
+ (and (stringp nodename)
+ (assoc-default nodename Info-virtual-nodes 'string-match)))
+
+ (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 (cdr-safe (assoc op (Info-virtual-file filename)))
+ (cdr-safe (assoc op (Info-virtual-node nodename)))))
+
+ (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
;; 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,535 ----
(or (assoc file Info-file-supports-index-cookies-list)
;; Skip virtual Info files
(and (or (not (stringp file))
! (Info-virtual-file file)
! (member file '("dir")))
(setq Info-file-supports-index-cookies-list
(cons (cons file nil) Info-file-supports-index-cookies-list)))
(save-excursion
***************
*** 660,666 ****
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
--- 708,718 ----
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))
(cond
***************
*** 711,718 ****
(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.
--- 763,769 ----
(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,872 ****
(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
--- 913,942 ----
(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
***************
*** 875,886 ****
(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))))
--- 945,950 ----
***************
*** 923,929 ****
(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)
--- 987,994 ----
(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)
***************
*** 2005,2076 ****
(interactive)
(Info-find-node "dir" "top"))
\f
! (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
--- 2070,2157 ----
(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)
! (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)
! (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)))
--- 2159,2169 ----
(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)))
\f
(defun Info-follow-reference (footnotename &optional fork)
--- 2243,2270 ----
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))
! (member filename '("dir")))
! (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)
***************
*** 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
--- 2878,2885 ----
(or (assoc file Info-index-nodes)
;; Skip virtual Info files
(and (or (not (stringp file))
! (Info-virtual-file file)
! (member file '("dir")))
(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
--- 2947,2955 ----
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]+\\))\\)?"
--- 3089,3244 ----
(progn (beginning-of-line) t) ;; non-nil for recursive call
(goto-char (point-min)))))
\f
! (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))))
! \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)
! ))
!
! (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."
--- 3290,3313 ----
(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)))))
\f
(defun Info-undefined ()
"Make command be undefined in Info."
***************
*** 3248,3253 ****
--- 3483,3489 ----
(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
--- 4066,4071 ----
***************
*** 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
--- 4649,4656 ----
(defun Info-desktop-buffer-misc-data (desktop-dirname)
"Auxiliary information to be saved in desktop file."
! (unless (or (member Info-current-file '(nil))
! (Info-virtual-file 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/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Virtual Info files and nodes
2009-06-28 21:28 Virtual Info files and nodes Juri Linkov
@ 2009-06-29 0:14 ` Stefan Monnier
2009-06-29 0:48 ` Juri Linkov
0 siblings, 1 reply; 7+ messages in thread
From: Stefan Monnier @ 2009-06-29 0:14 UTC (permalink / raw
To: Juri Linkov; +Cc: emacs-devel
> I'd like to present a patch that properly implements virtual Info files
> and nodes. It plugs in to just three functions in the Info package and
> provides an API to redefine these functions to dynamically generate
> either a virtual file or a virtual node:
It looks fairly good. I have a few comments/questions:
- Can't it be pushed a bit further to make it handle the "dir" special
case as well?
- Why regexps for filenames?
- Why ^...$ when you really mean \`...\'?
- Why check (stringp filename) in Info-virtual-file? Isn't it an
error to call it with something else than a string.
- Why have separate Info-virtual-file and Info-virtual-node functions?
(the latter is only called (once) from Info-virtual-fun, and the former
is called once from Info-virtual-fun and another time where it seems
that Info-virtual-fun could be used in its stead).
Stefan
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Virtual Info files and nodes
2009-06-29 0:14 ` Stefan Monnier
@ 2009-06-29 0:48 ` Juri Linkov
2009-06-29 8:42 ` Stefan Monnier
0 siblings, 1 reply; 7+ messages in thread
From: Juri Linkov @ 2009-06-29 0:48 UTC (permalink / raw
To: Stefan Monnier; +Cc: emacs-devel
> It looks fairly good. I have a few comments/questions:
> - Can't it be pushed a bit further to make it handle the "dir" special
> case as well?
Hmm, I haven't thought about "dir" because "dir" is a very old case,
but why not - it is just one call to `Info-insert-dir' and the last
special case is gone. Will do this in the next version of the patch.
> - Why regexps for filenames?
This is a forethought for possible future cases. But maybe only strings
should be allowed now, and regexps can be added only when necessary.
> - Why ^...$ when you really mean \`...\'?
Ok, will fix this.
> - 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, 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).
> - Why have separate Info-virtual-file and Info-virtual-node functions?
> (the latter is only called (once) from Info-virtual-fun, and the former
> is called once from Info-virtual-fun and another time where it seems
> that Info-virtual-fun could be used in its stead).
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).
--
Juri Linkov
http://www.jurta.org/emacs/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Virtual Info files and nodes
2009-06-29 0:48 ` Juri Linkov
@ 2009-06-29 8:42 ` Stefan Monnier
2009-06-29 23:55 ` Juri Linkov
0 siblings, 1 reply; 7+ messages in thread
From: Stefan Monnier @ 2009-06-29 8:42 UTC (permalink / raw
To: Juri Linkov; +Cc: emacs-devel
>> - Why regexps for filenames?
> This is a forethought for possible future cases. But maybe only strings
> should be allowed now, and regexps can be added only when necessary.
Nah, you might as well stick to regexps indeed. I'm not sure what
I was thinking.
>> - 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?
> 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.
>> - Why have separate Info-virtual-file and Info-virtual-node functions?
>> (the latter is only called (once) from Info-virtual-fun, and the former
>> is called once from Info-virtual-fun and another time where it seems
>> that Info-virtual-fun could be used in its stead).
> 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?
Stefan
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Virtual Info files and nodes
2009-06-29 8:42 ` Stefan Monnier
@ 2009-06-29 23:55 ` Juri Linkov
[not found] ` <jwvr5x11odl.fsf-monnier+emacs@gnu.org>
0 siblings, 1 reply; 7+ messages in thread
From: Juri Linkov @ 2009-06-29 23:55 UTC (permalink / raw
To: Stefan Monnier; +Cc: emacs-devel
>>> - 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)))
+
+ \f
(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"))
\f
! (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)))
\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 ()
"Go to the Info directory node."
(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)
! (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)
! (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)))
\f
(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)))))
\f
(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)))))
\f
! (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))))
! \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)
! ))
!
! (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)))))
\f
(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/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Virtual Info files and nodes
[not found] ` <jwvr5x11odl.fsf-monnier+emacs@gnu.org>
@ 2009-07-01 0:01 ` Juri Linkov
2009-07-01 15:02 ` Stefan Monnier
0 siblings, 1 reply; 7+ messages in thread
From: Juri Linkov @ 2009-07-01 0:01 UTC (permalink / raw
To: Stefan Monnier; +Cc: emacs-devel
>>>>> - 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'
>
> That part is obvious enough.
>
>> to check for a node name when a file name is irrelevant -
>> its argument value is nil in this case.
>
> But again: is that ever the case?
> I'd think a node name can only be meaningful when accompagnied by a filename.
There are valid calls with filename=nil where nil means the current
Info file: in Info-find-node-2 filename=nil means to not re-read
the current Info file, other places rely on the value nil returned
from Info-find-file when its argument filename is nil since this
function checks for (stringp filename). So the same check should be
also in Info-virtual-fun because a call to Info-virtual-fun is outside
of branches that check for a non-string filename in Info-find-file
and Info-find-node-2.
>> This is done in the next version below with other fixes including the
>> handling of "dir":
>
> Thanks, looks fairly good now. I just worry a little bit about binding
> inhibit-read-only around the call to (Info-virtual-call virtual-fun
> filename nodename no-going-back) since that may involve a lot of code
> that accesses other buffers. Maybe (setq buffer-read-only nil) would be
> a better choice here.
This code was a copy from a branch that reads a static Info file.
But this could be different like:
(let ((filename (or filename Info-current-file)))
(setq buffer-file-name nil)
(setq buffer-read-only nil)
(erase-buffer)
(setq Info-current-file filename)
(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))
--
Juri Linkov
http://www.jurta.org/emacs/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Virtual Info files and nodes
2009-07-01 0:01 ` Juri Linkov
@ 2009-07-01 15:02 ` Stefan Monnier
0 siblings, 0 replies; 7+ messages in thread
From: Stefan Monnier @ 2009-07-01 15:02 UTC (permalink / raw
To: Juri Linkov; +Cc: emacs-devel
> There are valid calls with filename=nil where nil means the current
> Info file: in Info-find-node-2 filename=nil means to not re-read
> the current Info file, other places rely on the value nil returned
> from Info-find-file when its argument filename is nil since this
> function checks for (stringp filename). So the same check should be
> also in Info-virtual-fun because a call to Info-virtual-fun is outside
> of branches that check for a non-string filename in Info-find-file
> and Info-find-node-2.
If you could explain that somewhere in a comment in info.el, that would
be swell. I have always found info.el's code a bit delicate to modify
because of these special cases with special meanings which are difficult
to predict.
>> Thanks, looks fairly good now. I just worry a little bit about binding
>> inhibit-read-only around the call to (Info-virtual-call virtual-fun
>> filename nodename no-going-back) since that may involve a lot of code
>> that accesses other buffers. Maybe (setq buffer-read-only nil) would be
>> a better choice here.
> This code was a copy from a branch that reads a static Info file.
I know, but for a static file, the affected code was more confined.
> But this could be different like:
> (let ((filename (or filename Info-current-file)))
> (setq buffer-file-name nil)
> (setq buffer-read-only nil)
> (erase-buffer)
> (setq Info-current-file filename)
> (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))
Yes, that's exactly what I was alluding to.
Stefan
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2009-07-01 15:02 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-06-28 21:28 Virtual Info files and nodes Juri Linkov
2009-06-29 0:14 ` Stefan Monnier
2009-06-29 0:48 ` Juri Linkov
2009-06-29 8:42 ` Stefan Monnier
2009-06-29 23:55 ` Juri Linkov
[not found] ` <jwvr5x11odl.fsf-monnier+emacs@gnu.org>
2009-07-01 0:01 ` Juri Linkov
2009-07-01 15:02 ` Stefan Monnier
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.