11cc83814Sespie;; texi-docstring-magic.el -- munge internal docstrings into texi 21cc83814Sespie;; 31cc83814Sespie;; Keywords: lisp, docs, tex 41cc83814Sespie;; Author: David Aspinall <da@dcs.ed.ac.uk> 51cc83814Sespie;; Copyright (C) 1998 David Aspinall 61cc83814Sespie;; Maintainer: David Aspinall <da@dcs.ed.ac.uk> 71cc83814Sespie;; 8*a1acfa9bSespie;; $Id: texi-docstring-magic.el,v 1.1.1.2 2006/07/17 16:03:50 espie Exp $ 91cc83814Sespie;; 101cc83814Sespie;; This package is distributed under the terms of the 111cc83814Sespie;; GNU General Public License, Version 2. 121cc83814Sespie;; You should have a copy of the GPL with your version of 131cc83814Sespie;; GNU Emacs or the Texinfo distribution. 141cc83814Sespie;; 151cc83814Sespie;; 161cc83814Sespie;; This package generates Texinfo source fragments from Emacs 171cc83814Sespie;; docstrings. This avoids documenting functions and variables 181cc83814Sespie;; in more than one place, and automatically adds Texinfo markup 191cc83814Sespie;; to docstrings. 201cc83814Sespie;; 211cc83814Sespie;; It relies heavily on you following the Elisp documentation 221cc83814Sespie;; conventions to produce sensible output, check the Elisp manual 231cc83814Sespie;; for details. In brief: 241cc83814Sespie;; 251cc83814Sespie;; * The first line of a docstring should be a complete sentence. 261cc83814Sespie;; * Arguments to functions should be written in upper case: ARG1..ARGN 271cc83814Sespie;; * User options (variables users may want to set) should have docstrings 281cc83814Sespie;; beginning with an asterisk. 291cc83814Sespie;; 301cc83814Sespie;; Usage: 311cc83814Sespie;; 321cc83814Sespie;; Write comments of the form: 331cc83814Sespie;; 341cc83814Sespie;; @c TEXI DOCSTRING MAGIC: my-package-function-or-variable-name 351cc83814Sespie;; 361cc83814Sespie;; In your texi source, mypackage.texi. From within an Emacs session 371cc83814Sespie;; where my-package is loaded, visit mypackage.texi and run 381cc83814Sespie;; M-x texi-docstring-magic to update all of the documentation strings. 391cc83814Sespie;; 401cc83814Sespie;; This will insert @defopt, @deffn and the like underneath the 411cc83814Sespie;; magic comment strings. 421cc83814Sespie;; 431cc83814Sespie;; The default value for user options will be printed. 441cc83814Sespie;; 451cc83814Sespie;; Symbols are recognized if they are defined for faces, functions, 461cc83814Sespie;; or variables (in that order). 471cc83814Sespie;; 481cc83814Sespie;; Automatic markup rules: 491cc83814Sespie;; 501cc83814Sespie;; 1. Indented lines are gathered into @lisp environment. 511cc83814Sespie;; 2. Pieces of text `stuff' or surrounded in quotes marked up with @samp. 521cc83814Sespie;; 3. Words *emphasized* are made @strong{emphasized} 531cc83814Sespie;; 4. Words sym-bol which are symbols become @code{sym-bol}. 541cc83814Sespie;; 5. Upper cased words ARG corresponding to arguments become @var{arg}. 551cc83814Sespie;; In fact, you can any word longer than three letters, so that 561cc83814Sespie;; metavariables can be used easily. 571cc83814Sespie;; FIXME: to escape this, use `ARG' 581cc83814Sespie;; 6. Words 'sym which are lisp-quoted are marked with @code{'sym}. 591cc83814Sespie;; 601cc83814Sespie;; ----- 611cc83814Sespie;; 621cc83814Sespie;; Useful key binding when writing Texinfo: 631cc83814Sespie;; 641cc83814Sespie;; (define-key TeXinfo-mode-map "C-cC-d" 'texi-docstring-magic-insert-magic) 651cc83814Sespie;; 661cc83814Sespie;; ----- 671cc83814Sespie;; 681cc83814Sespie;; Useful enhancements to do: 691cc83814Sespie;; 701cc83814Sespie;; * Use customize properties (e.g. group, simple types) 711cc83814Sespie;; * Look for a "texi-docstring" property for symbols 721cc83814Sespie;; so TeXInfo can be defined directly in case automatic markup 731cc83814Sespie;; goes badly wrong. 741cc83814Sespie;; * Add tags to special comments so that user can specify face, 751cc83814Sespie;; function, or variable binding for a symbol in case more than 761cc83814Sespie;; one binding exists. 771cc83814Sespie;; 781cc83814Sespie;; ------ 791cc83814Sespie 801cc83814Sespie(defun texi-docstring-magic-splice-sep (strings sep) 811cc83814Sespie "Return concatenation of STRINGS spliced together with separator SEP." 821cc83814Sespie (let (str) 831cc83814Sespie (while strings 841cc83814Sespie (setq str (concat str (car strings))) 851cc83814Sespie (if (cdr strings) 861cc83814Sespie (setq str (concat str sep))) 871cc83814Sespie (setq strings (cdr strings))) 881cc83814Sespie str)) 891cc83814Sespie 901cc83814Sespie(defconst texi-docstring-magic-munge-table 911cc83814Sespie '(;; 1. Indented lines are gathered into @lisp environment. 921cc83814Sespie ("\\(^.*\\S-.*$\\)" 931cc83814Sespie t 941cc83814Sespie (let 951cc83814Sespie ((line (match-string 0 docstring))) 961cc83814Sespie (if (eq (char-syntax (string-to-char line)) ?\ ) 971cc83814Sespie ;; whitespace 981cc83814Sespie (if in-quoted-region 991cc83814Sespie line 1001cc83814Sespie (setq in-quoted-region t) 1011cc83814Sespie (concat "@lisp\n" line)) 1021cc83814Sespie ;; non-white space 1031cc83814Sespie (if in-quoted-region 1041cc83814Sespie (progn 1051cc83814Sespie (setq in-quoted-region nil) 1061cc83814Sespie (concat "@end lisp\n" line)) 1071cc83814Sespie line)))) 1081cc83814Sespie ;; 2. Pieces of text `stuff' or surrounded in quotes 1091cc83814Sespie ;; are marked up with @samp. NB: Must be backquote 1101cc83814Sespie ;; followed by forward quote for this to work. 1111cc83814Sespie ;; Can't use two forward quotes else problems with 1121cc83814Sespie ;; symbols. 1131cc83814Sespie ;; Odd hack: because ' is a word constituent in text/texinfo 1141cc83814Sespie ;; mode, putting this first enables the recognition of args 1151cc83814Sespie ;; and symbols put inside quotes. 1161cc83814Sespie ("\\(`\\([^']+\\)'\\)" 1171cc83814Sespie t 1181cc83814Sespie (concat "@samp{" (match-string 2 docstring) "}")) 1191cc83814Sespie ;; 3. Words *emphasized* are made @strong{emphasized} 1201cc83814Sespie ("\\(\\*\\(\\w+\\)\\*\\)" 1211cc83814Sespie t 1221cc83814Sespie (concat "@strong{" (match-string 2 docstring) "}")) 1231cc83814Sespie ;; 4. Words sym-bol which are symbols become @code{sym-bol}. 1241cc83814Sespie ;; Must have at least one hyphen to be recognized, 1251cc83814Sespie ;; terminated in whitespace, end of line, or punctuation. 1261cc83814Sespie ;; (Only consider symbols made from word constituents 1271cc83814Sespie ;; and hyphen. 1281cc83814Sespie ("\\(\\(\\w+\\-\\(\\w\\|\\-\\)+\\)\\)\\(\\s\)\\|\\s-\\|\\s.\\|$\\)" 1291cc83814Sespie (or (boundp (intern (match-string 2 docstring))) 1301cc83814Sespie (fboundp (intern (match-string 2 docstring)))) 1311cc83814Sespie (concat "@code{" (match-string 2 docstring) "}" 1321cc83814Sespie (match-string 4 docstring))) 1331cc83814Sespie ;; 5. Upper cased words ARG corresponding to arguments become 1341cc83814Sespie ;; @var{arg} 1351cc83814Sespie ;; In fact, include any word so long as it is more than 3 characters 1361cc83814Sespie ;; long. (Comes after symbols to avoid recognizing the 1371cc83814Sespie ;; lowercased form of an argument as a symbol) 1381cc83814Sespie ;; FIXME: maybe we don't want to downcase stuff already 1391cc83814Sespie ;; inside @samp 1401cc83814Sespie ;; FIXME: should - terminate? should _ be included? 1411cc83814Sespie ("\\([A-Z0-9\\-]+\\)\\(/\\|\)\\|}\\|\\s-\\|\\s.\\|$\\)" 1421cc83814Sespie (or (> (length (match-string 1 docstring)) 3) 1431cc83814Sespie (member (downcase (match-string 1 docstring)) args)) 1441cc83814Sespie (concat "@var{" (downcase (match-string 1 docstring)) "}" 1451cc83814Sespie (match-string 2 docstring))) 1461cc83814Sespie 1471cc83814Sespie ;; 6. Words 'sym which are lisp quoted are 1481cc83814Sespie ;; marked with @code. 1491cc83814Sespie ("\\(\\(\\s-\\|^\\)'\\(\\(\\w\\|\\-\\)+\\)\\)\\(\\s\)\\|\\s-\\|\\s.\\|$\\)" 1501cc83814Sespie t 1511cc83814Sespie (concat (match-string 2 docstring) 1521cc83814Sespie "@code{'" (match-string 3 docstring) "}" 1531cc83814Sespie (match-string 5 docstring))) 1541cc83814Sespie ;; 7,8. Clean up for @lisp environments left with spurious newlines 1551cc83814Sespie ;; after 1. 1561cc83814Sespie ("\\(\\(^\\s-*$\\)\n@lisp\\)" t "@lisp") 1571cc83814Sespie ("\\(\\(^\\s-*$\\)\n@end lisp\\)" t "@end lisp")) 1581cc83814Sespie "Table of regexp matches and replacements used to markup docstrings. 1591cc83814SespieFormat of table is a list of elements of the form 1601cc83814Sespie (regexp predicate replacement-form) 1611cc83814SespieIf regexp matches and predicate holds, then replacement-form is 1621cc83814Sespieevaluated to get the replacement for the match. 1631cc83814Sespiepredicate and replacement-form can use variables arg, 1641cc83814Sespieand forms such as (match-string 1 docstring) 1651cc83814SespieMatch string 1 is assumed to determine the 1661cc83814Sespielength of the matched item, hence where parsing restarts from. 1671cc83814SespieThe replacement must cover the whole match (match string 0), 1681cc83814Sespieincluding any whitespace included to delimit matches.") 1691cc83814Sespie 1701cc83814Sespie 1711cc83814Sespie(defun texi-docstring-magic-munge-docstring (docstring args) 1721cc83814Sespie "Markup DOCSTRING for texi according to regexp matches." 1731cc83814Sespie (let ((case-fold-search nil)) 1741cc83814Sespie (dolist (test texi-docstring-magic-munge-table docstring) 1751cc83814Sespie (let ((regexp (nth 0 test)) 1761cc83814Sespie (predicate (nth 1 test)) 1771cc83814Sespie (replace (nth 2 test)) 1781cc83814Sespie (i 0) 1791cc83814Sespie in-quoted-region) 1801cc83814Sespie 1811cc83814Sespie (while (and 1821cc83814Sespie (< i (length docstring)) 1831cc83814Sespie (string-match regexp docstring i)) 1841cc83814Sespie (setq i (match-end 1)) 1851cc83814Sespie (if (eval predicate) 1861cc83814Sespie (let* ((origlength (- (match-end 0) (match-beginning 0))) 1871cc83814Sespie (replacement (eval replace)) 1881cc83814Sespie (newlength (length replacement))) 1891cc83814Sespie (setq docstring 1901cc83814Sespie (replace-match replacement t t docstring)) 1911cc83814Sespie (setq i (+ i (- newlength origlength)))))) 1921cc83814Sespie (if in-quoted-region 1931cc83814Sespie (setq docstring (concat docstring "\n@end lisp")))))) 1941cc83814Sespie ;; Force a new line after (what should be) the first sentence, 1951cc83814Sespie ;; if not already a new paragraph. 1961cc83814Sespie (let* 1971cc83814Sespie ((pos (string-match "\n" docstring)) 1981cc83814Sespie (needscr (and pos 1991cc83814Sespie (not (string= "\n" 2001cc83814Sespie (substring docstring 2011cc83814Sespie (1+ pos) 2021cc83814Sespie (+ pos 2))))))) 2031cc83814Sespie (if (and pos needscr) 2041cc83814Sespie (concat (substring docstring 0 pos) 2051cc83814Sespie "@*\n" 2061cc83814Sespie (substring docstring (1+ pos))) 2071cc83814Sespie docstring))) 2081cc83814Sespie 2091cc83814Sespie(defun texi-docstring-magic-texi (env grp name docstring args &optional endtext) 2101cc83814Sespie "Make a texi def environment ENV for entity NAME with DOCSTRING." 2111cc83814Sespie (concat "@def" env (if grp (concat " " grp) "") " " name 2121cc83814Sespie " " 2131cc83814Sespie (texi-docstring-magic-splice-sep args " ") 2141cc83814Sespie ;; " " 2151cc83814Sespie ;; (texi-docstring-magic-splice-sep extras " ") 2161cc83814Sespie "\n" 2171cc83814Sespie (texi-docstring-magic-munge-docstring docstring args) 2181cc83814Sespie "\n" 2191cc83814Sespie (or endtext "") 2201cc83814Sespie "@end def" env "\n")) 2211cc83814Sespie 2221cc83814Sespie(defun texi-docstring-magic-format-default (default) 2231cc83814Sespie "Make a default value string for the value DEFAULT. 2241cc83814SespieMarkup as @code{stuff} or @lisp stuff @end lisp." 2251cc83814Sespie (let ((text (format "%S" default))) 2261cc83814Sespie (concat 2271cc83814Sespie "\nThe default value is " 2281cc83814Sespie (if (string-match "\n" text) 2291cc83814Sespie ;; Carriage return will break @code, use @lisp 2301cc83814Sespie (if (stringp default) 2311cc83814Sespie (concat "the string: \n@lisp\n" default "\n@end lisp\n") 2321cc83814Sespie (concat "the value: \n@lisp\n" text "\n@end lisp\n")) 2331cc83814Sespie (concat "@code{" text "}.\n"))))) 2341cc83814Sespie 2351cc83814Sespie 2361cc83814Sespie(defun texi-docstring-magic-texi-for (symbol) 2371cc83814Sespie (cond 2381cc83814Sespie ;; Faces 2391cc83814Sespie ((find-face symbol) 2401cc83814Sespie (let* 2411cc83814Sespie ((face symbol) 2421cc83814Sespie (name (symbol-name face)) 2431cc83814Sespie (docstring (or (face-doc-string face) 2441cc83814Sespie "Not documented.")) 2451cc83814Sespie (useropt (eq ?* (string-to-char docstring)))) 2461cc83814Sespie ;; Chop off user option setting 2471cc83814Sespie (if useropt 2481cc83814Sespie (setq docstring (substring docstring 1))) 2491cc83814Sespie (texi-docstring-magic-texi "fn" "Face" name docstring nil))) 2501cc83814Sespie ((fboundp symbol) 2511cc83814Sespie ;; Functions. 2521cc83814Sespie ;; We don't handle macros, aliases, or compiled fns properly. 2531cc83814Sespie (let* 2541cc83814Sespie ((function symbol) 2551cc83814Sespie (name (symbol-name function)) 2561cc83814Sespie (docstring (or (documentation function) 2571cc83814Sespie "Not documented.")) 2581cc83814Sespie (def (symbol-function function)) 2591cc83814Sespie (argsyms (cond ((eq (car-safe def) 'lambda) 2601cc83814Sespie (nth 1 def)))) 2611cc83814Sespie (args (mapcar 'symbol-name argsyms))) 2621cc83814Sespie (if (commandp function) 2631cc83814Sespie (texi-docstring-magic-texi "fn" "Command" name docstring args) 2641cc83814Sespie (texi-docstring-magic-texi "un" nil name docstring args)))) 2651cc83814Sespie ((boundp symbol) 2661cc83814Sespie ;; Variables. 2671cc83814Sespie (let* 2681cc83814Sespie ((variable symbol) 2691cc83814Sespie (name (symbol-name variable)) 2701cc83814Sespie (docstring (or (documentation-property variable 2711cc83814Sespie 'variable-documentation) 2721cc83814Sespie "Not documented.")) 2731cc83814Sespie (useropt (eq ?* (string-to-char docstring))) 2741cc83814Sespie (default (if useropt 2751cc83814Sespie (texi-docstring-magic-format-default 2761cc83814Sespie (default-value symbol))))) 2771cc83814Sespie ;; Chop off user option setting 2781cc83814Sespie (if useropt 2791cc83814Sespie (setq docstring (substring docstring 1))) 2801cc83814Sespie (texi-docstring-magic-texi 2811cc83814Sespie (if useropt "opt" "var") nil name docstring nil default))) 2821cc83814Sespie (t 2831cc83814Sespie (error "Don't know anything about symbol %s" (symbol-name symbol))))) 2841cc83814Sespie 2851cc83814Sespie(defconst texi-docstring-magic-comment 2861cc83814Sespie "@c TEXI DOCSTRING MAGIC:" 2871cc83814Sespie "Magic string in a texi buffer expanded into @defopt, or @deffn.") 2881cc83814Sespie 2891cc83814Sespie(defun texi-docstring-magic () 2901cc83814Sespie "Update all texi docstring magic annotations in buffer." 2911cc83814Sespie (interactive) 2921cc83814Sespie (save-excursion 2931cc83814Sespie (goto-char (point-min)) 2941cc83814Sespie (let ((magic (concat "^" 2951cc83814Sespie (regexp-quote texi-docstring-magic-comment) 2961cc83814Sespie "\\s-*\\(\\(\\w\\|\\-\\)+\\)$")) 2971cc83814Sespie p 2981cc83814Sespie symbol) 2991cc83814Sespie (while (re-search-forward magic nil t) 3001cc83814Sespie (setq symbol (intern (match-string 1))) 3011cc83814Sespie (forward-line) 3021cc83814Sespie (setq p (point)) 3031cc83814Sespie ;; If comment already followed by an environment, delete it. 3041cc83814Sespie (if (and 3051cc83814Sespie (looking-at "@def\\(\\w+\\)\\s-") 3061cc83814Sespie (search-forward (concat "@end def" (match-string 1)) nil t)) 3071cc83814Sespie (progn 3081cc83814Sespie (forward-line) 3091cc83814Sespie (delete-region p (point)))) 3101cc83814Sespie (insert 3111cc83814Sespie (texi-docstring-magic-texi-for symbol)))))) 3121cc83814Sespie 3131cc83814Sespie(defun texi-docstring-magic-face-at-point () 3141cc83814Sespie (ignore-errors 3151cc83814Sespie (let ((stab (syntax-table))) 3161cc83814Sespie (unwind-protect 3171cc83814Sespie (save-excursion 3181cc83814Sespie (set-syntax-table emacs-lisp-mode-syntax-table) 3191cc83814Sespie (or (not (zerop (skip-syntax-backward "_w"))) 3201cc83814Sespie (eq (char-syntax (char-after (point))) ?w) 3211cc83814Sespie (eq (char-syntax (char-after (point))) ?_) 3221cc83814Sespie (forward-sexp -1)) 3231cc83814Sespie (skip-chars-forward "'") 3241cc83814Sespie (let ((obj (read (current-buffer)))) 3251cc83814Sespie (and (symbolp obj) (find-face obj) obj))) 3261cc83814Sespie (set-syntax-table stab))))) 3271cc83814Sespie 3281cc83814Sespie(defun texi-docstring-magic-insert-magic (symbol) 3291cc83814Sespie (interactive 3301cc83814Sespie (let* ((v (or (variable-at-point) 3311cc83814Sespie (function-at-point) 3321cc83814Sespie (texi-docstring-magic-face-at-point))) 3331cc83814Sespie (val (let ((enable-recursive-minibuffers t)) 3341cc83814Sespie (completing-read 3351cc83814Sespie (if v 3361cc83814Sespie (format "Magic docstring for symbol (default %s): " v) 3371cc83814Sespie "Magic docstring for symbol: ") 3381cc83814Sespie obarray '(lambda (sym) 3391cc83814Sespie (or (boundp sym) 3401cc83814Sespie (fboundp sym) 3411cc83814Sespie (find-face sym))) 3421cc83814Sespie t nil 'variable-history)))) 3431cc83814Sespie (list (if (equal val "") v (intern val))))) 3441cc83814Sespie (insert "\n" texi-docstring-magic-comment " " (symbol-name symbol))) 3451cc83814Sespie 3461cc83814Sespie 3471cc83814Sespie(provide 'texi-docstring-magic) 348