;;; docref.el --- Simple cross references for Elisp documentation strings ;; Copyright (C) 1994 Free Software Foundation, Inc. ;; Author: Vadim Geshel ;; Created: 12 Jul 1994 ;; Keywords: docs, help, lisp ;; This file is part of XEmacs. ;; XEmacs is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; XEmacs is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with XEmacs; see the file COPYING. If not, write to the Free ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ;; 02111-1307, USA. ;;; Synched up with: FSF 19.34. ;;; Commentary: ;; #### It's not clear we really wish to keep this one around. ;; original name was cross-ref.el. ;; This package allows you to use a simple form of cross references in ;; your Emacs Lisp documentation strings. Cross-references look like ;; \\(type@[label@]data), where type defines a method for retrieving ;; reference informatin, data is used by a method routine as an argument, ;; and label "represents" the reference in text. If label is absent, data ;; is used instead. ;; ;; Special reference labeled `back', when present, can be used to return ;; to the previous contents of help buffer. ;; ;; Cross-referencing currently is intended for use in doc strings only ;; and works only in temporary buffers (created by `with-output-to-temp-buffer'). ;; List of temp buffers in which cross-referencing is to be active is specified ;; by variable DOCREF-BUFFERS-LIST, which contains only "*Help*" by default. ;; ;; Documentation strings for this package's functions and variables can serve ;; as examples of usage. ;; ;;; Customization: ;; ;; See source. The main customization variable is `docref-methods-alist'. ;; It consists of (type . function) pairs, where type is a string which ;; corresponds to type in cross-references and function is called with ;; one argument - reference `data' - when a reference is activated. ;; ;;; Installation: ;; ;; Place this file somewhere in your load-path, byte-compiled it, and add ;; (require 'cross-ref) ;; to your .emacs. ;;; Code: ;; User customizable variables (defvar docref-highlight-p t "*If non-nil, \\(f@docref-subst) highlights cross-references. Under window system it highlights them with face defined by \\(v@docref-highlight-face), on character terminal highlighted references look like cross-references in info mode.") (defvar docref-highlight-face 'highlight "*Face used to highlight cross-references (used by \\(f@docref-subst))") (defvar docref-methods-alist '(("f" . docref-describe-function) ; reference to a function documentation ("v" . docref-describe-variable) ; reference to a variable documentation ("F" . docref-read-file) ; reference to a file contents ("s" . docref-use-string) ; reference to a string ("V" . docref-use-variable-value) ; reference to variable value ("0" . beep)) ; just highlighted text "Alist which maps cross-reference ``types'' to retrieval functions. The car of each element is a string that serves as `type' in cross-references. \(See \\(f@docref-subst)). The cdr is a function of one argument, to be called to find this reference.") (defvar docref-back-label "\nback" "Label to use by \\(f@docref-subst) for the go-back reference.") (defvar docref-back-reference nil "If non-nil, this is a go-back reference to add to the current buffer. The value specifies how to go back. It should be suitable for use as the second argument to \\(f@docref-insert-label). \\(f@docref-subst) uses this to set up the go-back reference.") (defvar docref-last-active-buffer) ;;;###autoload (defun docref-setup () "Process docref cross-references in the current buffer. See also \\(f@docref-subst)." (interactive) (docref-subst (current-buffer)) (docref-mode)) (defvar docref-mode-map nil) (or docref-mode-map (let ((map (make-sparse-keymap))) (define-key map [mouse-2] 'docref-follow-mouse) (define-key map "\C-c\C-b" 'docref-go-back) (define-key map "\C-c\C-c" 'docref-follow) (setq docref-mode-map map))) (defun docref-mode () "Major mode for help buffers that contain cross references. To follow a reference, move to it and type \\[docref-follow], or use \\[docref-follow-mouse]. The command \\[docref-go-back] can used to go back to where you came from." (interactive) (kill-all-local-variables) (setq major-mode 'docref-mode) (setq mode-name "Docref") (use-local-map docref-mode-map) (run-hooks 'docref-mode-hook)) (defun docref-subst (buf) "Parse documentation cross-references in buffer BUF. Find cross-reference information in a buffer and highlight them with face defined by \\(v@docref-highlight-face). Cross-reference has the following format: \\ (TYPE[@LABEL]@DATA), where TYPE defines method used to retrieve xref data (like reading from file or calling \\(f@describe-function)), DATA is an argument to this method \(like file name or function name), and LABEL is displayed in text using \\(v@docref-highlight-face). The special reference `back' can be used to return back. The variable \\(v@docref-back-label) specifies the label to use for that. See \\(v@docref-methods-alist) for currently defined methods." (interactive "b") (save-excursion (set-buffer buf) (goto-char (point-min)) ;; The docref-seen property indicates that we have processed this ;; buffer's contents already, so don't do it again. (if (not (get-text-property (point-min) 'docref-seen)) (let ((old-modified (buffer-modified-p))) (while (re-search-forward "[\\](\\([^\)\@]+\\)\\(@[^\)\@]+\\)?@\\([^\)]*\\))" nil t) (let* ((start (match-beginning 0)) (type (buffer-substring (match-beginning 1) (match-end 1))) (data (buffer-substring (match-beginning 3) (match-end 3))) (label (if (match-beginning 2) (buffer-substring (+ (match-beginning 2) 1) (match-end 2)) data))) (replace-match "" t) (docref-insert-label label (cons type data)))) ;; Make a back-reference in this buffer, if desired. ;; (This is true if called from docref-follow.) (if docref-back-reference (progn (goto-char (point-max)) (put-text-property (point-min) (1+ (point-min)) 'docref-back-position (point)) (docref-insert-label docref-back-label docref-back-reference))) (put-text-property (point-min) (1+ (point-min)) 'docref-seen t) (set-buffer-modified-p old-modified))))) (defun docref-insert-label (string ref) (let ((label (concat string)) (pos (point))) ;; decorate the label (let ((leading-space-end (save-match-data (if (string-match "^\\([ \t\n]+\\)" label) (match-end 1) 0))) (trailing-space-start (save-match-data (if (string-match "\\([ \t\n]+\\)$" label) (match-beginning 1) (length label))))) (if docref-highlight-p ;; XEmacs: we support faces on TTY's. ;; (if (not window-system) ;; (setq label ;; (concat (substring label 0 leading-space-end) ;; "(*note " ;; (substring label leading-space-end trailing-space-start) ;; ")" ;; (substring label trailing-space-start))) ;; window-system (put-text-property leading-space-end trailing-space-start 'face docref-highlight-face label)) (put-text-property 0 (length label) 'docref ref label) (insert label)))) (defun docref-follow-mouse (click) "Follow the cross-reference that you click on." (interactive "e") (save-excursion ;; XEmacs changes here. (let* ((window (event-window click)) (pos (event-point click)) (docref-last-active-buffer (current-buffer))) (set-buffer (window-buffer window)) (docref-follow pos)))) (defun docref-go-back () "Go back to the previous contents of help buffer." (interactive) (let ((pos (get-text-property (point-min) 'docref-back-position))) (if pos (docref-follow pos) (error "No go-back reference")))) (defun docref-follow (&optional pos) "Follow cross-reference at point. For the cross-reference format, see \\(f@docref-subst). The special reference named `back' can be used to return back" (interactive) (or pos (setq pos (point))) (let ((docref-data (get-text-property pos 'docref))) (if docref-data ;; There is a reference at point. Follow it. (let* ((type (car docref-data)) (name (cdr docref-data)) (method (assoc type docref-methods-alist)) (cur-contents (buffer-string)) (opoint (point)) (docref-back-reference (cons "s" cur-contents)) success) (if (null method) (error "Unknown cross-reference type: %s" type)) (unwind-protect (save-excursion (funcall (cdr method) name) (setq success t)) (or success (progn ;; (cdr method) got an error. ;; Put back the text that we had. (erase-buffer) (insert cur-contents) (goto-char opoint))) (set-buffer-modified-p nil)))))) ;; Builtin methods for accessing a reference. (defun docref-describe-function (data) (save-excursion (if (boundp 'docref-last-active-buffer) (set-buffer docref-last-active-buffer)) (describe-function (intern data)))) (defun docref-describe-variable (data) (save-excursion (if (boundp 'docref-last-active-buffer) (set-buffer docref-last-active-buffer)) (describe-variable (intern data)))) (defun docref-read-file (data) (with-output-to-temp-buffer (buffer-name) (erase-buffer) (insert-file-contents (expand-file-name data)))) (defun docref-use-string (data) (with-output-to-temp-buffer (buffer-name) (erase-buffer) (insert data))) (defun docref-use-variable-value (data) (let ((sym (intern data))) (with-output-to-temp-buffer (buffer-name) (erase-buffer) (princ (symbol-value sym))))) (provide 'docref) ;;; docref.el ends here