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