No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / texinfo / util / texi-docstring-magic.el
blob1b4a676eb531f4d0cc28b20ec6e73d086bdfe20d
1 ;; texi-docstring-magic.el -- munge internal docstrings into texi
2 ;;
3 ;; Keywords: lisp, docs, tex
4 ;; Author: David Aspinall <da@dcs.ed.ac.uk>
5 ;; Copyright (C) 1998 David Aspinall
6 ;; Maintainer: David Aspinall <da@dcs.ed.ac.uk>
7 ;;
8 ;; Id: texi-docstring-magic.el,v 1.2 2004/04/11 17:56:47 karl Exp
9 ;;
10 ;; This package is distributed under the terms of the
11 ;; GNU General Public License, Version 2.
12 ;; You should have a copy of the GPL with your version of
13 ;; GNU Emacs or the Texinfo distribution.
14 ;;
16 ;; This package generates Texinfo source fragments from Emacs
17 ;; docstrings. This avoids documenting functions and variables
18 ;; in more than one place, and automatically adds Texinfo markup
19 ;; to docstrings.
21 ;; It relies heavily on you following the Elisp documentation
22 ;; conventions to produce sensible output, check the Elisp manual
23 ;; for details. In brief:
25 ;; * The first line of a docstring should be a complete sentence.
26 ;; * Arguments to functions should be written in upper case: ARG1..ARGN
27 ;; * User options (variables users may want to set) should have docstrings
28 ;; beginning with an asterisk.
29 ;;
30 ;; Usage:
32 ;; Write comments of the form:
34 ;; @c TEXI DOCSTRING MAGIC: my-package-function-or-variable-name
36 ;; In your texi source, mypackage.texi. From within an Emacs session
37 ;; where my-package is loaded, visit mypackage.texi and run
38 ;; M-x texi-docstring-magic to update all of the documentation strings.
40 ;; This will insert @defopt, @deffn and the like underneath the
41 ;; magic comment strings.
42 ;;
43 ;; The default value for user options will be printed.
45 ;; Symbols are recognized if they are defined for faces, functions,
46 ;; or variables (in that order).
48 ;; Automatic markup rules:
50 ;; 1. Indented lines are gathered into @lisp environment.
51 ;; 2. Pieces of text `stuff' or surrounded in quotes marked up with @samp.
52 ;; 3. Words *emphasized* are made @strong{emphasized}
53 ;; 4. Words sym-bol which are symbols become @code{sym-bol}.
54 ;; 5. Upper cased words ARG corresponding to arguments become @var{arg}.
55 ;; In fact, you can any word longer than three letters, so that
56 ;; metavariables can be used easily.
57 ;; FIXME: to escape this, use `ARG'
58 ;; 6. Words 'sym which are lisp-quoted are marked with @code{'sym}.
60 ;; -----
62 ;; Useful key binding when writing Texinfo:
64 ;; (define-key TeXinfo-mode-map "C-cC-d" 'texi-docstring-magic-insert-magic)
66 ;; -----
68 ;; Useful enhancements to do:
70 ;; * Use customize properties (e.g. group, simple types)
71 ;; * Look for a "texi-docstring" property for symbols
72 ;; so TeXInfo can be defined directly in case automatic markup
73 ;; goes badly wrong.
74 ;; * Add tags to special comments so that user can specify face,
75 ;; function, or variable binding for a symbol in case more than
76 ;; one binding exists.
78 ;; ------
80 (defun texi-docstring-magic-splice-sep (strings sep)
81 "Return concatenation of STRINGS spliced together with separator SEP."
82 (let (str)
83 (while strings
84 (setq str (concat str (car strings)))
85 (if (cdr strings)
86 (setq str (concat str sep)))
87 (setq strings (cdr strings)))
88 str))
90 (defconst texi-docstring-magic-munge-table
91 '(;; 1. Indented lines are gathered into @lisp environment.
92 ("\\(^.*\\S-.*$\\)"
94 (let
95 ((line (match-string 0 docstring)))
96 (if (eq (char-syntax (string-to-char line)) ?\ )
97 ;; whitespace
98 (if in-quoted-region
99 line
100 (setq in-quoted-region t)
101 (concat "@lisp\n" line))
102 ;; non-white space
103 (if in-quoted-region
104 (progn
105 (setq in-quoted-region nil)
106 (concat "@end lisp\n" line))
107 line))))
108 ;; 2. Pieces of text `stuff' or surrounded in quotes
109 ;; are marked up with @samp. NB: Must be backquote
110 ;; followed by forward quote for this to work.
111 ;; Can't use two forward quotes else problems with
112 ;; symbols.
113 ;; Odd hack: because ' is a word constituent in text/texinfo
114 ;; mode, putting this first enables the recognition of args
115 ;; and symbols put inside quotes.
116 ("\\(`\\([^']+\\)'\\)"
118 (concat "@samp{" (match-string 2 docstring) "}"))
119 ;; 3. Words *emphasized* are made @strong{emphasized}
120 ("\\(\\*\\(\\w+\\)\\*\\)"
122 (concat "@strong{" (match-string 2 docstring) "}"))
123 ;; 4. Words sym-bol which are symbols become @code{sym-bol}.
124 ;; Must have at least one hyphen to be recognized,
125 ;; terminated in whitespace, end of line, or punctuation.
126 ;; (Only consider symbols made from word constituents
127 ;; and hyphen.
128 ("\\(\\(\\w+\\-\\(\\w\\|\\-\\)+\\)\\)\\(\\s\)\\|\\s-\\|\\s.\\|$\\)"
129 (or (boundp (intern (match-string 2 docstring)))
130 (fboundp (intern (match-string 2 docstring))))
131 (concat "@code{" (match-string 2 docstring) "}"
132 (match-string 4 docstring)))
133 ;; 5. Upper cased words ARG corresponding to arguments become
134 ;; @var{arg}
135 ;; In fact, include any word so long as it is more than 3 characters
136 ;; long. (Comes after symbols to avoid recognizing the
137 ;; lowercased form of an argument as a symbol)
138 ;; FIXME: maybe we don't want to downcase stuff already
139 ;; inside @samp
140 ;; FIXME: should - terminate? should _ be included?
141 ("\\([A-Z0-9\\-]+\\)\\(/\\|\)\\|}\\|\\s-\\|\\s.\\|$\\)"
142 (or (> (length (match-string 1 docstring)) 3)
143 (member (downcase (match-string 1 docstring)) args))
144 (concat "@var{" (downcase (match-string 1 docstring)) "}"
145 (match-string 2 docstring)))
147 ;; 6. Words 'sym which are lisp quoted are
148 ;; marked with @code.
149 ("\\(\\(\\s-\\|^\\)'\\(\\(\\w\\|\\-\\)+\\)\\)\\(\\s\)\\|\\s-\\|\\s.\\|$\\)"
151 (concat (match-string 2 docstring)
152 "@code{'" (match-string 3 docstring) "}"
153 (match-string 5 docstring)))
154 ;; 7,8. Clean up for @lisp environments left with spurious newlines
155 ;; after 1.
156 ("\\(\\(^\\s-*$\\)\n@lisp\\)" t "@lisp")
157 ("\\(\\(^\\s-*$\\)\n@end lisp\\)" t "@end lisp"))
158 "Table of regexp matches and replacements used to markup docstrings.
159 Format of table is a list of elements of the form
160 (regexp predicate replacement-form)
161 If regexp matches and predicate holds, then replacement-form is
162 evaluated to get the replacement for the match.
163 predicate and replacement-form can use variables arg,
164 and forms such as (match-string 1 docstring)
165 Match string 1 is assumed to determine the
166 length of the matched item, hence where parsing restarts from.
167 The replacement must cover the whole match (match string 0),
168 including any whitespace included to delimit matches.")
171 (defun texi-docstring-magic-munge-docstring (docstring args)
172 "Markup DOCSTRING for texi according to regexp matches."
173 (let ((case-fold-search nil))
174 (dolist (test texi-docstring-magic-munge-table docstring)
175 (let ((regexp (nth 0 test))
176 (predicate (nth 1 test))
177 (replace (nth 2 test))
178 (i 0)
179 in-quoted-region)
181 (while (and
182 (< i (length docstring))
183 (string-match regexp docstring i))
184 (setq i (match-end 1))
185 (if (eval predicate)
186 (let* ((origlength (- (match-end 0) (match-beginning 0)))
187 (replacement (eval replace))
188 (newlength (length replacement)))
189 (setq docstring
190 (replace-match replacement t t docstring))
191 (setq i (+ i (- newlength origlength))))))
192 (if in-quoted-region
193 (setq docstring (concat docstring "\n@end lisp"))))))
194 ;; Force a new line after (what should be) the first sentence,
195 ;; if not already a new paragraph.
196 (let*
197 ((pos (string-match "\n" docstring))
198 (needscr (and pos
199 (not (string= "\n"
200 (substring docstring
201 (1+ pos)
202 (+ pos 2)))))))
203 (if (and pos needscr)
204 (concat (substring docstring 0 pos)
205 "@*\n"
206 (substring docstring (1+ pos)))
207 docstring)))
209 (defun texi-docstring-magic-texi (env grp name docstring args &optional endtext)
210 "Make a texi def environment ENV for entity NAME with DOCSTRING."
211 (concat "@def" env (if grp (concat " " grp) "") " " name
213 (texi-docstring-magic-splice-sep args " ")
214 ;; " "
215 ;; (texi-docstring-magic-splice-sep extras " ")
216 "\n"
217 (texi-docstring-magic-munge-docstring docstring args)
218 "\n"
219 (or endtext "")
220 "@end def" env "\n"))
222 (defun texi-docstring-magic-format-default (default)
223 "Make a default value string for the value DEFAULT.
224 Markup as @code{stuff} or @lisp stuff @end lisp."
225 (let ((text (format "%S" default)))
226 (concat
227 "\nThe default value is "
228 (if (string-match "\n" text)
229 ;; Carriage return will break @code, use @lisp
230 (if (stringp default)
231 (concat "the string: \n@lisp\n" default "\n@end lisp\n")
232 (concat "the value: \n@lisp\n" text "\n@end lisp\n"))
233 (concat "@code{" text "}.\n")))))
236 (defun texi-docstring-magic-texi-for (symbol)
237 (cond
238 ;; Faces
239 ((find-face symbol)
240 (let*
241 ((face symbol)
242 (name (symbol-name face))
243 (docstring (or (face-doc-string face)
244 "Not documented."))
245 (useropt (eq ?* (string-to-char docstring))))
246 ;; Chop off user option setting
247 (if useropt
248 (setq docstring (substring docstring 1)))
249 (texi-docstring-magic-texi "fn" "Face" name docstring nil)))
250 ((fboundp symbol)
251 ;; Functions.
252 ;; We don't handle macros, aliases, or compiled fns properly.
253 (let*
254 ((function symbol)
255 (name (symbol-name function))
256 (docstring (or (documentation function)
257 "Not documented."))
258 (def (symbol-function function))
259 (argsyms (cond ((eq (car-safe def) 'lambda)
260 (nth 1 def))))
261 (args (mapcar 'symbol-name argsyms)))
262 (if (commandp function)
263 (texi-docstring-magic-texi "fn" "Command" name docstring args)
264 (texi-docstring-magic-texi "un" nil name docstring args))))
265 ((boundp symbol)
266 ;; Variables.
267 (let*
268 ((variable symbol)
269 (name (symbol-name variable))
270 (docstring (or (documentation-property variable
271 'variable-documentation)
272 "Not documented."))
273 (useropt (eq ?* (string-to-char docstring)))
274 (default (if useropt
275 (texi-docstring-magic-format-default
276 (default-value symbol)))))
277 ;; Chop off user option setting
278 (if useropt
279 (setq docstring (substring docstring 1)))
280 (texi-docstring-magic-texi
281 (if useropt "opt" "var") nil name docstring nil default)))
283 (error "Don't know anything about symbol %s" (symbol-name symbol)))))
285 (defconst texi-docstring-magic-comment
286 "@c TEXI DOCSTRING MAGIC:"
287 "Magic string in a texi buffer expanded into @defopt, or @deffn.")
289 (defun texi-docstring-magic ()
290 "Update all texi docstring magic annotations in buffer."
291 (interactive)
292 (save-excursion
293 (goto-char (point-min))
294 (let ((magic (concat "^"
295 (regexp-quote texi-docstring-magic-comment)
296 "\\s-*\\(\\(\\w\\|\\-\\)+\\)$"))
298 symbol)
299 (while (re-search-forward magic nil t)
300 (setq symbol (intern (match-string 1)))
301 (forward-line)
302 (setq p (point))
303 ;; If comment already followed by an environment, delete it.
304 (if (and
305 (looking-at "@def\\(\\w+\\)\\s-")
306 (search-forward (concat "@end def" (match-string 1)) nil t))
307 (progn
308 (forward-line)
309 (delete-region p (point))))
310 (insert
311 (texi-docstring-magic-texi-for symbol))))))
313 (defun texi-docstring-magic-face-at-point ()
314 (ignore-errors
315 (let ((stab (syntax-table)))
316 (unwind-protect
317 (save-excursion
318 (set-syntax-table emacs-lisp-mode-syntax-table)
319 (or (not (zerop (skip-syntax-backward "_w")))
320 (eq (char-syntax (char-after (point))) ?w)
321 (eq (char-syntax (char-after (point))) ?_)
322 (forward-sexp -1))
323 (skip-chars-forward "'")
324 (let ((obj (read (current-buffer))))
325 (and (symbolp obj) (find-face obj) obj)))
326 (set-syntax-table stab)))))
328 (defun texi-docstring-magic-insert-magic (symbol)
329 (interactive
330 (let* ((v (or (variable-at-point)
331 (function-at-point)
332 (texi-docstring-magic-face-at-point)))
333 (val (let ((enable-recursive-minibuffers t))
334 (completing-read
335 (if v
336 (format "Magic docstring for symbol (default %s): " v)
337 "Magic docstring for symbol: ")
338 obarray '(lambda (sym)
339 (or (boundp sym)
340 (fboundp sym)
341 (find-face sym)))
342 t nil 'variable-history))))
343 (list (if (equal val "") v (intern val)))))
344 (insert "\n" texi-docstring-magic-comment " " (symbol-name symbol)))
347 (provide 'texi-docstring-magic)