unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* adding manual cross-ref links to *Help*
@ 2011-06-11 15:57 Drew Adams
  2011-06-11 18:07 ` Juri Linkov
  0 siblings, 1 reply; 5+ messages in thread
From: Drew Adams @ 2011-06-11 15:57 UTC (permalink / raw)
  To: emacs-devel

I've added cross-reference links to the manuals from *Help* buffers.  This text
is added:

  For more information check the manuals.

`manuals' here is a link to an Info virtual menu of links to the indexed
occurrences (of the help target) in the manuals.

For example, if you do 'C-h f forward-char' and then click the `manuals' link
you get an Info buffer that is a menu of the index entries for 'forward-char' in
all of the manuals searched.  In the case of function 'forward-char' there are
two links:

  * forward-char [elisp]:   (elisp)Character Motion.
  * forward-char [emacs]:   (emacs)Moving Point.

I can submit a patch for this if people are interested.  To try it out first,
just load help-fns+.el, which you can find here:
http://www.emacswiki.org/emacs/download/help-fns%2b.el

User option `help-cross-reference-manuals' controls which manuals to search.  By
default, only the Emacs and Elisp manuals are searched.  If you customize it to
search all manuals present, then the `C-h f forward-char' example also includes
this entry (on my system):

  * forward-char [mh-e]:    (mh-e)Processing Mail Tour.

The same user option controls whether to also search the manuals when composing
*Help*, thus to avoid creating the manuals link if there are in fact no index
entries for the given help target.  By default (to save time), the link is
always created - there is no manuals search until you click the link.


[Note (esp. to Juri):
Info already has an apropos search and an index menu.  But the former includes
hits that are not literal (exact) matches, and the latter is only for a single
manual AFAICT.  So I added functions `Info-indexed-find-file' and
`Info-indexed-find-node' and added them to `Info-virtual-files'.  It might be
better (dunno) to instead extend `Info-virtual-index' to work across multiple
manuals etc.]




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: adding manual cross-ref links to *Help*
  2011-06-11 15:57 adding manual cross-ref links to *Help* Drew Adams
@ 2011-06-11 18:07 ` Juri Linkov
  2011-06-13 21:01   ` Drew Adams
  0 siblings, 1 reply; 5+ messages in thread
From: Juri Linkov @ 2011-06-11 18:07 UTC (permalink / raw)
  To: Drew Adams; +Cc: emacs-devel

> [Note (esp. to Juri):
> Info already has an apropos search and an index menu.  But the former includes
> hits that are not literal (exact) matches, and the latter is only for a single
> manual AFAICT.

Adding cross-reference links is easier to do with Help buffer templates
that improves `Info-virtual-files'.  I'll submit a patch here soon.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* RE: adding manual cross-ref links to *Help*
  2011-06-11 18:07 ` Juri Linkov
@ 2011-06-13 21:01   ` Drew Adams
  2011-06-14 17:29     ` Juri Linkov
  0 siblings, 1 reply; 5+ messages in thread
From: Drew Adams @ 2011-06-13 21:01 UTC (permalink / raw)
  To: 'Juri Linkov'; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1116 bytes --]

> Adding cross-reference links is easier to do with Help buffer 
> templates that improves `Info-virtual-files'.  I'll submit a
> patch here soon.

Thanks.  It's fine if you implement the same features in a different way.  I
welcome the feature, however it's done.  I requested it at least as far back as
2006.  I finally got around to adding it to my own code, thanks to the virtual
indexing code that you added to Emacs.

I hope you will give users as much control over whether and when the indexes of
the manuals are searched, for this.

But your explanation is a bit vague.  What is this about Help buffer templates?
And what is not easy about just adding a line like this to each `describe-*'
command:

(Info-make-manuals-xref function) ; Link to manuals.

Yes, my overall patch is larger than that.  It could be much smaller if your
virtual indexing code allowed for links to more than one manual at a time.
Likewise, if index-entry matching could optionally use an exact match.  Those
two enhancements would I think be welcome in any case.

Anyway, my patch is attached, in case someone decides to go for it.

[-- Attachment #2: help-fns-2011-06-13.patch --]
[-- Type: application/octet-stream, Size: 2665 bytes --]

diff -c -w help-fns.el help-fns-patched-2011-06-13.el
*** help-fns.el	Mon Jun 13 09:36:56 2011
--- help-fns-patched-2011-06-13.el	Mon Jun 13 11:20:32 2011
***************
*** 212,217 ****
--- 212,242 ----
    :group 'help
    :version "23.2")
  
+ (defcustom help-cross-reference-manuals '(("emacs" "elisp"))
+   "*Manuals to search, for a `*Help*' buffer link to the manuals.
+ A cons.
+ 
+  The car is a list of manuals to search, or the symbol `all', to
+   search all.  If nil, then do not create a cross-reference link.
+ 
+  The cdr is a boolean:
+ 
+   Non-`nil' means search the manuals, then create a cross-ref link:
+         create it only if some search hits are found.
+ 
+   `nil' means create a cross-ref link without searching manuals
+         first (but only if there are some manuals to search)."
+   :set #'(lambda (sym defs)
+            (custom-set-default sym defs)
+            (setq Info-indexed-nodes  ()))
+   :type '(cons
+           (choice :tag "Which Manuals"
+            (repeat :tag "Specific Manuals (files)" string)
+            (const  :tag "All Manuals" all))
+           (boolean :tag "Search Before Creating Button?"))
+   :group 'help
+   :version "24.1")
+ 
  (defun help-highlight-arg (arg)
    "Highlight ARG as an argument name for a *Help* buffer.
  Return ARG in face `help-argument-name'; ARG is also downcased
***************
*** 586,593 ****
  			    (use (format ";\nuse `%s' instead." use))
  			    (t "."))
  		      "\n"))
! 	    (insert "\n"
! 		    (or doc "Not documented."))))))))
  
  \f
  ;; Variables
--- 611,619 ----
  			    (use (format ";\nuse `%s' instead." use))
  			    (t "."))
  		      "\n"))
! 	    (insert "\n")
!             (when doc (Info-make-manuals-xref function)) ; Link to manuals.
!             (insert (or doc "Not documented."))))))))
  
  \f
  ;; Variables
***************
*** 880,889 ****
  		  (princ output))))
  
  	    (with-current-buffer standard-output
  	      ;; Return the text we displayed.
  	      (buffer-string))))))))
  
- 
  ;;;###autoload
  (defun describe-syntax (&optional buffer)
    "Describe the syntax specifications in the syntax table of BUFFER.
--- 906,915 ----
  		  (princ output))))
  
  	    (with-current-buffer standard-output
+               (unless valvoid (Info-make-manuals-xref variable)) ; Link to manuals.
  	      ;; Return the text we displayed.
  	      (buffer-string))))))))
  
  ;;;###autoload
  (defun describe-syntax (&optional buffer)
    "Describe the syntax specifications in the syntax table of BUFFER.

Diff finished.  Mon Jun 13 11:46:48 2011

[-- Attachment #3: info-2011-06-13.patch --]
[-- Type: application/octet-stream, Size: 10070 bytes --]

diff -c -w info.el info-patched-2011-06-13.el
*** info.el	Mon Jun 13 09:44:32 2011
--- info-patched-2011-06-13.el	Mon Jun 13 13:54:26 2011
***************
*** 2151,2156 ****
--- 2151,2329 ----
    (Info-next-reference)
    (Info-next-reference))
  \f
+ (defvar Info-indexed-file "*Indexed*"
+   "Info file for virtual manual from `Info-index-entries-across-manuals'.")
+ 
+ (defvar Info-indexed-nodes ()
+   "Alist of cached nodes with matching index entries.
+ Each element is (NODENAME STRING MATCHES), where:
+  NODENAME is the name of the node that is indexed,
+  STRING is the search string passed to `Info-index-occurrences',
+  MATCHES is a list of index matches found by `Info-index-occurrences'.
+ 
+ This has the same structure as `Info-apropos-nodes', but the search
+ was made by `Info-index-occurrences', not by `Info-apropos-matches',
+ so that matches are exact.")
+ 
+ (defun Info-indexed-find-file (filename &optional _noerror)
+   "Index-search implementation of `Info-find-file'."
+   filename)
+ 
+ (defun Info-indexed-find-node (_filename nodename &optional _no-going-back)
+   "Index-search implementation of `Info-find-node-2'."
+   (let* ((nodeinfo  (assoc nodename Info-indexed-nodes))
+          (matches   (nth 2 nodeinfo)))
+     (when matches
+       (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n" Info-indexed-file nodename))
+       (insert "Index Matches\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)) ""))))))))
+ 
+ (add-to-list 'Info-virtual-files '("\\`\\*Indexed\\*\\'"
+                                    (find-file . Info-indexed-find-file)
+                                    (find-node . Info-indexed-find-node)))
+ 
+ (defun Info-make-manuals-xref (symbol)
+   "Create a cross-ref link for index entries for SYMBOL in manuals.
+ `help-cross-reference-manuals' controls which manual(s) are searched.
+ Do nothing if its car is `nil' (no manuals to search).
+ If its cdr is `nil' then create the link without first searching any
+ manuals.  Otherwise, create the link only if there are search hits in
+ the manuals."
+   (when (car help-cross-reference-manuals) ; Create no link if no manuals to search.
+     (let ((manuals       (car help-cross-reference-manuals))
+           (search-now-p  (cdr help-cross-reference-manuals))
+           (symb-name     (if (stringp symbol) symbol (symbol-name symbol))))
+       (when (or (not search-now-p)
+                 (save-current-buffer (Info-any-index-occurrences-p symb-name manuals)))
+         (let ((buffer-read-only  nil))
+           (insert (format "\n\nFor more information %s the "
+                           (if (cdr help-cross-reference-manuals) "see" "check")))
+           (help-insert-xref-button "manuals" 'help-info-manual-lookup symb-name manuals)
+           (insert ".\n\n"))))))
+ 
+ (define-button-type 'help-info-manual-lookup
+     :supertype 'help-xref
+     'help-function #'(lambda (string &optional manuals)
+                        (Info-index-entries-across-manuals string manuals))
+     'help-echo "mouse-2, RET: Look it up in the manuals")
+ 
+ (defun Info-index-entries-across-manuals (string &optional manuals)
+   "Look up STRING in indexes of Info MANUALS on your system.
+ Looks for exact matches: STRING is expected to be an index entry.
+ Build an Info menu of the possible matches.
+ MANUALS has the form of `help-cross-reference-manuals'."
+   (let ((nodes  Info-indexed-nodes)
+         nodename)
+     (while (and nodes (not (equal string (nth 1 (car nodes)))))
+       (setq nodes  (cdr nodes)))
+     (if nodes
+         (Info-find-node Info-indexed-file (car (car nodes)))
+       (setq nodename  (format "Index for `%s'" string))
+       (push (list nodename string (Info-index-occurrences string manuals)) Info-indexed-nodes)
+       (Info-find-node Info-indexed-file nodename))))
+ 
+ ;; Similar to `Info-apropos-matches', but using exact matches. 
+ (defun Info-index-occurrences (index-entry &optional manuals)
+   "Collect occurrences of INDEX-ENTRY in MANUALS.
+ MANUALS has the form of `help-cross-reference-manuals'.
+ Return a list of the form ((FILE INDEX-ENTRY NODE LINE)), where:
+  FILE is the name of an Info file,
+  NODE is an Info node name,
+  LINE is the line number of the INDEX-ENTRY occurrence in that node."
+   (unless (string= index-entry "")
+     ;; Unlike `Info-apropos-matches', we match only the exact string as an index entry.
+     (let ((pattern  (format "\n\\* +\\([^\n]*%s\\):[ \t]+\\([^\n]+\\)\
+ \\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
+                             (regexp-quote index-entry)))
+           matches index-nodes node)
+       (message "Searching indexes of %s..." (if (eq manuals 'all)
+                                                 "all manuals"
+                                               (concat "manuals "
+                                                       (mapconcat #'identity manuals ", "))))
+       (condition-case nil
+           (with-temp-buffer
+             (Info-mode)
+             (Info-directory)
+             (goto-char (point-min))
+             (re-search-forward "\\* Menu: *\n" nil t)
+             (when (eq manuals 'all)
+               (setq manuals  ())
+               (let (manual)
+                 (while (re-search-forward "\\*.*: *(\\([^)]+\\))" nil t)
+                   ;; `add-to-list' ensures no dups in `manuals', so the `dolist' runs faster.
+                   (setq manual  (match-string 1))
+                   (set-text-properties 0 (length manual) nil manual)
+                   (add-to-list 'manuals manual))))
+             (dolist (manual  manuals)
+               (message "Searching indexes of manual `%s'..." manual)
+               (when (setq index-nodes  (Info-index-nodes (Info-find-file manual)))
+                 (Info-find-node manual (car index-nodes))
+                 (while (progn (goto-char (point-min))
+                               (while (re-search-forward pattern nil t)
+                                 (setq matches  (cons (list manual
+                                                            (match-string-no-properties 1)
+                                                            (match-string-no-properties 2)
+                                                            (match-string-no-properties 3))
+                                                      matches)))
+                               (setq index-nodes  (cdr index-nodes)
+                                     node         (car index-nodes)))
+                   (Info-goto-node node)))))
+         (error nil))
+       matches)))
+ 
+ ;; Like `Info-index-occurrences', but just return non-nil as soon as we know there is a match.
+ (defun Info-any-index-occurrences-p (index-entry &optional manuals)
+   "Return non-nil if there are any occurrences of INDEX-ENTRY in MANUALS.
+ MANUALS has the form of `help-cross-reference-manuals'."
+   (and (not (string= index-entry ""))
+        ;; Unlike `Info-apropos-matches', we match only the exact string as an index entry.
+        (let ((pattern  (format "\n\\* +\\([^\n]*%s\\):[ \t]+\\([^\n]+\\)\
+ \\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
+                                (regexp-quote index-entry)))
+              (any?     nil)
+              index-nodes node)
+          (message "Searching indexes of %s..." (if (eq manuals 'all)
+                                                    "all manuals"
+                                                  (concat "manuals "
+                                                          (mapconcat #'identity manuals ", "))))
+          (condition-case nil
+              (with-temp-buffer
+                (Info-mode)
+                (Info-directory)
+                (goto-char (point-min))
+                (re-search-forward "\\* Menu: *\n" nil t)
+                (when (eq manuals 'all)
+                  (setq manuals  ())
+                  (let (manual)
+                    (while (re-search-forward "\\*.*: *(\\([^)]+\\))" nil t)
+                      ;; `add-to-list' ensures no dups in `manuals', so the `dolist' runs faster.
+                      (setq manual  (match-string 1))
+                      (set-text-properties 0 (length manual) nil manual)
+                      (add-to-list 'manuals manual))))
+                (setq any?  (catch 'Info-any-index-occurrences-p
+                              (dolist (manual  manuals)
+                                (message "Searching indexes of manual `%s'..." manual)
+                                (when (setq index-nodes  (Info-index-nodes (Info-find-file manual)))
+                                  (Info-find-node manual (car index-nodes))
+                                  (while (progn (goto-char (point-min))
+                                                (when (re-search-forward pattern nil t)
+                                                  (throw 'Info-any-index-occurrences-p t))
+                                                (setq index-nodes  (cdr index-nodes)
+                                                      node         (car index-nodes)))
+                                    (Info-goto-node node))))
+                              nil)))
+            (error nil))
+          any?)))
+ \f
  (add-to-list 'Info-virtual-nodes
  	     '("\\`\\*TOC\\*\\'"
  	       (find-node . Info-toc-find-node)

Diff finished.  Mon Jun 13 13:54:47 2011

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: adding manual cross-ref links to *Help*
  2011-06-13 21:01   ` Drew Adams
@ 2011-06-14 17:29     ` Juri Linkov
  2011-06-14 18:11       ` Drew Adams
  0 siblings, 1 reply; 5+ messages in thread
From: Juri Linkov @ 2011-06-14 17:29 UTC (permalink / raw)
  To: Drew Adams; +Cc: emacs-devel

> Yes, my overall patch is larger than that.  It could be much smaller if your
> virtual indexing code allowed for links to more than one manual at a time.

Thanks, I'll look into this.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* RE: adding manual cross-ref links to *Help*
  2011-06-14 17:29     ` Juri Linkov
@ 2011-06-14 18:11       ` Drew Adams
  0 siblings, 0 replies; 5+ messages in thread
From: Drew Adams @ 2011-06-14 18:11 UTC (permalink / raw)
  To: 'Juri Linkov'; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 223 bytes --]

> Thanks, I'll look into this.

BTW, to make this feature complete, you will also want to add the attached patch
of `help.el', for `describe-mode'.  It adds links to manuals for each of the
modes described (major & minor).

[-- Attachment #2: help-2011-06-14.patch --]
[-- Type: application/octet-stream, Size: 2045 bytes --]

diff -c -w help.el help-patched-2011-06-14.el
*** help.el	Tue Jun 14 11:07:36 2011
--- help-patched-2011-06-14.el	Tue Jun 14 11:09:18 2011
***************
*** 852,858 ****
  				     "no indicator"
  				   (format "indicator%s"
  					   indicator))))
! 		  (princ (documentation mode-function)))
  		(insert-button pretty-minor-mode
  			       'action (car help-button-cache)
  			       'follow-link t
--- 852,859 ----
                                       "no indicator"
                                     (format "indicator%s"
                                             indicator))))
!                   (princ (documentation mode-function))
!                   (Info-make-manuals-xref mode-function t)) ; Link to manuals.
                  (insert-button pretty-minor-mode
                                 'action (car help-button-cache)
                                 'follow-link t
***************
*** 880,890 ****
  		(re-search-backward "`\\([^`']+\\)'" nil t)
  		(help-xref-button 1 'help-function-def mode file-name)))))
  	(princ ":\n")
! 	(princ (documentation major-mode)))))
    ;; For the sake of IELM and maybe others
    nil)
  
- 
  (defun describe-minor-mode (minor-mode)
    "Display documentation of a minor mode given as MINOR-MODE.
  MINOR-MODE can be a minor mode symbol or a minor mode indicator string
--- 881,893 ----
                  (re-search-backward "`\\([^`']+\\)'" nil t)
                  (help-xref-button 1 'help-function-def mode file-name)))))
          (princ ":\n")
!         (princ (documentation major-mode))
!         (let ((maj  major-mode))
!           (with-current-buffer standard-output
!             (Info-make-manuals-xref maj t)))))) ; Link to manuals.
    ;; For the sake of IELM and maybe others
    nil)
  
  (defun describe-minor-mode (minor-mode)
    "Display documentation of a minor mode given as MINOR-MODE.
  MINOR-MODE can be a minor mode symbol or a minor mode indicator string

Diff finished.  Tue Jun 14 11:10:14 2011

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-06-14 18:11 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-06-11 15:57 adding manual cross-ref links to *Help* Drew Adams
2011-06-11 18:07 ` Juri Linkov
2011-06-13 21:01   ` Drew Adams
2011-06-14 17:29     ` Juri Linkov
2011-06-14 18:11       ` Drew Adams

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).