Initial Commit
[packages] / xemacs-packages / xemacs-devel / docref.el
1 ;;; docref.el --- Simple cross references for Elisp documentation strings
2
3 ;; Copyright (C) 1994 Free Software Foundation, Inc.
4
5 ;; Author: Vadim Geshel <vadik@unas.cs.kiev.ua>
6 ;; Created: 12 Jul 1994
7 ;; Keywords: docs, help, lisp
8
9 ;; This file is part of XEmacs.
10
11 ;; XEmacs is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; XEmacs is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with XEmacs; see the file COPYING.  If not, write to the Free
23 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24 ;; 02111-1307, USA.
25
26 ;;; Synched up with: FSF 19.34.
27
28 ;;; Commentary:
29
30 ;; #### It's not clear we really wish to keep this one around.
31
32 ;; original name was cross-ref.el.
33
34 ;; This package allows you to use a simple form of cross references in
35 ;; your Emacs Lisp documentation strings. Cross-references look like
36 ;; \\(type@[label@]data), where type defines a method for retrieving
37 ;; reference informatin, data is used by a method routine as an argument,
38 ;; and label "represents" the reference in text. If label is absent, data
39 ;; is used instead.
40 ;; 
41 ;; Special reference labeled `back', when present, can be used to return
42 ;; to the previous contents of help buffer.
43 ;;
44 ;; Cross-referencing currently is intended for use in doc strings only
45 ;; and works only in temporary buffers (created by `with-output-to-temp-buffer').
46 ;; List of temp buffers in which cross-referencing is to be active is specified
47 ;; by variable DOCREF-BUFFERS-LIST, which contains only "*Help*" by default.
48 ;;
49 ;; Documentation strings for this package's functions and variables can serve
50 ;; as examples of usage.
51 ;;
52 ;;; Customization:
53 ;; 
54 ;; See source. The main customization variable is `docref-methods-alist'.
55 ;; It consists of (type . function) pairs, where type is a string which
56 ;; corresponds to type in cross-references and function is called with
57 ;; one argument - reference `data' - when a reference is activated.
58 ;;
59 ;;; Installation:
60 ;;
61 ;; Place this file somewhere in your load-path, byte-compiled it, and add
62 ;; (require 'cross-ref)
63 ;; to your .emacs.
64 \f
65 ;;; Code:
66
67 ;; User customizable variables
68
69 (defvar docref-highlight-p t
70   "*If non-nil, \\(f@docref-subst) highlights cross-references.
71 Under window system it highlights them with face defined by
72 \\(v@docref-highlight-face), on character terminal highlighted references
73 look like cross-references in info mode.")
74
75 (defvar docref-highlight-face 'highlight
76   "*Face used to highlight cross-references (used by \\(f@docref-subst))")
77
78 (defvar docref-methods-alist
79   '(("f" . docref-describe-function)    ; reference to a function documentation
80     ("v" . docref-describe-variable)    ; reference to a variable documentation
81     ("F" . docref-read-file)            ; reference to a file contents
82     ("s" . docref-use-string)           ; reference to a string 
83     ("V" . docref-use-variable-value)   ; reference to variable value
84     ("0" . beep))                       ; just highlighted text
85   "Alist which maps cross-reference ``types'' to retrieval functions.
86
87 The car of each element is a string that serves as `type' in cross-references.
88 \(See \\(f@docref-subst)).  The cdr is a function of one argument,
89 to be called to find this reference.")
90
91 (defvar docref-back-label "\nback"
92   "Label to use by \\(f@docref-subst) for the go-back reference.")
93
94 (defvar docref-back-reference nil
95   "If non-nil, this is a go-back reference to add to the current buffer.
96 The value specifies how to go back.  It should be suitable for use
97 as the second argument to \\(f@docref-insert-label).
98 \\(f@docref-subst) uses this to set up the go-back reference.")
99
100 (defvar docref-last-active-buffer)
101 \f
102 ;;;###autoload
103 (defun docref-setup ()
104   "Process docref cross-references in the current buffer.
105 See also \\(f@docref-subst)."
106   (interactive)
107   (docref-subst (current-buffer))
108   (docref-mode))
109
110 (defvar docref-mode-map nil)
111 (or docref-mode-map
112     (let ((map (make-sparse-keymap)))
113       (define-key map [mouse-2] 'docref-follow-mouse)
114       (define-key map "\C-c\C-b" 'docref-go-back)
115       (define-key map "\C-c\C-c" 'docref-follow)
116       (setq docref-mode-map map)))
117
118 (defun docref-mode ()
119   "Major mode for help buffers that contain cross references.
120 To follow a reference, move to it and type \\[docref-follow], or use
121 \\[docref-follow-mouse].  The command \\[docref-go-back] can used to go
122 back to where you came from."
123   (interactive)
124   (kill-all-local-variables)
125   (setq major-mode 'docref-mode)
126   (setq mode-name "Docref")
127   (use-local-map docref-mode-map)
128   (run-hooks 'docref-mode-hook))
129 \f
130 (defun docref-subst (buf)
131   "Parse documentation cross-references in buffer BUF.
132
133 Find cross-reference information in a buffer and
134 highlight them with face defined by \\(v@docref-highlight-face).
135
136 Cross-reference has the following format: \\ (TYPE[@LABEL]@DATA), where
137 TYPE defines method used to retrieve xref data (like reading from file or
138 calling \\(f@describe-function)), DATA is an argument to this method
139 \(like file name or function name), and LABEL is displayed in text using
140 \\(v@docref-highlight-face).
141
142 The special reference `back' can be used to return back.
143 The variable \\(v@docref-back-label) specifies the label to use for that.
144
145 See \\(v@docref-methods-alist) for currently defined methods."
146   (interactive "b")
147   (save-excursion
148     (set-buffer buf)
149     (goto-char (point-min))
150     ;; The docref-seen property indicates that we have processed this
151     ;; buffer's contents already, so don't do it again.
152     (if (not (get-text-property (point-min) 'docref-seen))
153         (let ((old-modified (buffer-modified-p)))
154           (while (re-search-forward "[\\](\\([^\)\@]+\\)\\(@[^\)\@]+\\)?@\\([^\)]*\\))"
155                                     nil t)
156             (let* ((start (match-beginning 0))
157                    (type (buffer-substring (match-beginning 1) (match-end 1)))
158                    (data (buffer-substring (match-beginning 3) (match-end 3)))
159                    (label
160                     (if (match-beginning 2)
161                         (buffer-substring (+ (match-beginning 2) 1) (match-end 2))
162                       data)))
163               (replace-match "" t)
164               (docref-insert-label label (cons type data))))
165
166           ;; Make a back-reference in this buffer, if desired.
167           ;; (This is true if called from docref-follow.)
168           (if docref-back-reference
169               (progn
170                 (goto-char (point-max))
171                 (put-text-property (point-min) (1+ (point-min))
172                                    'docref-back-position (point))
173                 (docref-insert-label docref-back-label docref-back-reference)))
174           (put-text-property (point-min) (1+ (point-min)) 'docref-seen t)
175           (set-buffer-modified-p old-modified)))))
176
177 (defun docref-insert-label (string ref)
178   (let ((label (concat string))
179         (pos (point)))
180     ;; decorate the label
181     (let ((leading-space-end (save-match-data
182                                (if (string-match "^\\([ \t\n]+\\)" label)
183                                    (match-end 1)
184                                  0)))
185           (trailing-space-start (save-match-data
186                                   (if (string-match "\\([ \t\n]+\\)$" label)
187                                       (match-beginning 1)
188                                     (length label)))))
189       (if docref-highlight-p          
190 ;;        XEmacs: we support faces on TTY's.
191 ;;        (if (not window-system)
192 ;;            (setq label
193 ;;                  (concat (substring label 0 leading-space-end)
194 ;;                          "(*note "
195 ;;                          (substring label leading-space-end trailing-space-start)
196 ;;                          ")"
197 ;;                          (substring label trailing-space-start)))
198             ;; window-system
199             (put-text-property leading-space-end
200                                trailing-space-start
201                                'face docref-highlight-face label))
202       (put-text-property 0 (length label) 'docref ref label)
203       (insert label))))
204 \f
205 (defun docref-follow-mouse (click)
206   "Follow the cross-reference that you click on."
207   (interactive "e")
208   (save-excursion
209     ;; XEmacs changes here.
210     (let* ((window (event-window click))
211            (pos (event-point click))
212            (docref-last-active-buffer (current-buffer)))
213       (set-buffer (window-buffer window))
214       (docref-follow pos))))
215
216 (defun docref-go-back ()
217   "Go back to the previous contents of help buffer."
218   (interactive)
219   (let ((pos (get-text-property (point-min) 'docref-back-position)))
220     (if pos
221         (docref-follow pos)
222       (error "No go-back reference"))))
223
224 (defun docref-follow (&optional pos)
225   "Follow cross-reference at point.
226 For the cross-reference format, see \\(f@docref-subst).
227 The special reference named `back' can be used to return back"
228   (interactive)
229   (or pos (setq pos (point)))
230   (let ((docref-data (get-text-property pos 'docref)))
231     (if docref-data
232         ;; There is a reference at point.  Follow it.
233         (let* ((type (car docref-data))
234                (name (cdr docref-data))
235                (method (assoc type docref-methods-alist))
236                (cur-contents (buffer-string))
237                (opoint (point))
238                (docref-back-reference (cons "s" cur-contents))
239                success)
240           (if (null method)
241               (error "Unknown cross-reference type: %s" type))
242           (unwind-protect
243               (save-excursion
244                 (funcall (cdr method) name)
245                 (setq success t))
246             (or success
247                 (progn
248                   ;; (cdr method) got an error.
249                   ;; Put back the text that we had.
250                   (erase-buffer)
251                   (insert cur-contents)
252                   (goto-char opoint)))
253             (set-buffer-modified-p nil))))))
254 \f
255 ;; Builtin methods for accessing a reference.
256
257 (defun docref-describe-function (data)
258   (save-excursion
259     (if (boundp 'docref-last-active-buffer)
260         (set-buffer docref-last-active-buffer))
261     (describe-function (intern data))))
262   
263 (defun docref-describe-variable (data)
264   (save-excursion
265     (if (boundp 'docref-last-active-buffer)
266         (set-buffer docref-last-active-buffer))
267     (describe-variable (intern data))))
268
269 (defun docref-read-file (data)
270   (with-output-to-temp-buffer (buffer-name)
271     (erase-buffer)
272     (insert-file-contents (expand-file-name data))))
273
274 (defun docref-use-string (data)
275   (with-output-to-temp-buffer (buffer-name)
276     (erase-buffer)
277     (insert data)))
278
279 (defun docref-use-variable-value (data)
280   (let ((sym (intern data)))
281     (with-output-to-temp-buffer (buffer-name)
282       (erase-buffer)
283       (princ (symbol-value sym)))))
284
285 (provide 'docref)
286
287 ;;; docref.el ends here