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