Commit | Line | Data |
---|---|---|
47f3ce52 AW |
1 | ;;;; (texinfo reflection) -- documenting Scheme as stexinfo |
2 | ;;;; | |
8470b3f4 | 3 | ;;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc. |
47f3ce52 AW |
4 | ;;;; Copyright (C) 2003,2004,2009 Andy Wingo <wingo at pobox dot com> |
5 | ;;;; | |
6 | ;;;; This library is free software; you can redistribute it and/or | |
7 | ;;;; modify it under the terms of the GNU Lesser General Public | |
8 | ;;;; License as published by the Free Software Foundation; either | |
9 | ;;;; version 3 of the License, or (at your option) any later version. | |
10 | ;;;; | |
11 | ;;;; This library is distributed in the hope that it will be useful, | |
12 | ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | ;;;; Lesser General Public License for more details. | |
15 | ;;;; | |
16 | ;;;; You should have received a copy of the GNU Lesser General Public | |
17 | ;;;; License along with this library; if not, write to the Free Software | |
18 | ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | ;;;; | |
20 | \f | |
21 | ;;; Commentary: | |
22 | ;; | |
23 | ;;Routines to generare @code{stexi} documentation for objects and | |
24 | ;;modules. | |
25 | ;; | |
26 | ;;Note that in this context, an @dfn{object} is just a value associated | |
27 | ;;with a location. It has nothing to do with GOOPS. | |
28 | ;; | |
29 | ;;; Code: | |
30 | ||
31 | (define-module (texinfo reflection) | |
32 | #:use-module ((srfi srfi-1) #:select (append-map)) | |
33 | #:use-module (oop goops) | |
34 | #:use-module (texinfo) | |
35 | #:use-module (texinfo plain-text) | |
36 | #:use-module (srfi srfi-13) | |
37 | #:use-module (ice-9 session) | |
38 | #:use-module (ice-9 documentation) | |
39 | #:use-module (ice-9 optargs) | |
4f08d0b5 | 40 | #:use-module (system vm program) |
47f3ce52 AW |
41 | #:use-module ((sxml transform) #:select (pre-post-order)) |
42 | #:export (module-stexi-documentation | |
43 | script-stexi-documentation | |
44 | object-stexi-documentation | |
45 | package-stexi-standard-copying | |
46 | package-stexi-standard-titlepage | |
47 | package-stexi-generic-menu | |
48 | package-stexi-standard-menu | |
49 | package-stexi-extended-menu | |
50 | package-stexi-standard-prologue | |
c55cb58a AW |
51 | package-stexi-documentation |
52 | package-stexi-documentation-for-include)) | |
47f3ce52 AW |
53 | |
54 | ;; List for sorting the definitions in a module | |
55 | (define defs | |
56 | '(deftp defcv defivar deftypeivar defop deftypeop defmethod | |
57 | deftypemethod defopt defvr defvar deftypevr deftypevar deffn | |
58 | deftypefn defmac defspec defun deftypefun)) | |
59 | ||
60 | (define (sort-defs ordering a b) | |
61 | (define (def x) | |
62 | ;; a and b are lists of the form ((anchor ...) (def* ...)...) | |
63 | (cadr x)) | |
64 | (define (name x) | |
65 | (cadr (assq 'name (cdadr (def x))))) | |
66 | (define (priority x) | |
67 | (list-index defs (car (def x)))) | |
68 | (define (order x) | |
69 | (or (list-index ordering (string->symbol (name x))) | |
70 | ;; if the def is not in the list, a big number | |
71 | 1234567890)) | |
72 | (define (compare-in-order proc eq? < . args) | |
73 | (if (not (eq? (proc a) (proc b))) | |
74 | (< (proc a) (proc b)) | |
75 | (or (null? args) | |
76 | (apply compare-in-order args)))) | |
77 | (compare-in-order order = < | |
78 | priority = < | |
79 | name string=? string<=?)) | |
80 | ||
81 | (define (list*-join l infix restfix) | |
82 | (let lp ((in l) (out '())) | |
83 | (cond ((null? in) (reverse! out)) | |
84 | ((symbol? in) (reverse! (cons* in restfix out))) | |
85 | (else (lp (cdr in) (if (null? out) | |
86 | (list (car in)) | |
87 | (cons* (car in) infix out))))))) | |
88 | ||
89 | (define (process-args args) | |
90 | (map (lambda (x) (if (symbol? x) (symbol->string x) x)) | |
91 | (list*-join (or args '()) | |
92 | " " " . "))) | |
93 | ||
94 | (define (get-proc-args proc) | |
95 | (cond | |
8470b3f4 AW |
96 | ((procedure-arguments proc) |
97 | => (lambda (args) | |
98 | (let ((required-args (assq-ref args 'required)) | |
99 | (optional-args (assq-ref args 'optional)) | |
100 | (keyword-args (assq-ref args 'keyword)) | |
101 | (rest-arg (assq-ref args 'rest))) | |
47f3ce52 AW |
102 | (process-args |
103 | (append | |
8470b3f4 AW |
104 | ;; start with the required args... |
105 | (map symbol->string required-args) | |
47f3ce52 | 106 | |
8470b3f4 AW |
107 | ;; add any optional args if needed... |
108 | (map (lambda (a) | |
109 | (if (list? a) | |
110 | (format #f "[~a = ~s]" (car a) (cadr a)) | |
111 | (format #f "[~a]" a))) | |
112 | optional-args) | |
47f3ce52 | 113 | |
8470b3f4 AW |
114 | ;; now the keyword args.. |
115 | (map (lambda (a) | |
116 | (if (pair? a) | |
117 | (format #f "[~a]" (car a)) | |
118 | (format #f "[#:~a]" a))) | |
119 | keyword-args) | |
47f3ce52 | 120 | |
8470b3f4 AW |
121 | ;; now the rest arg... |
122 | (if rest-arg | |
123 | (list "." (symbol->string rest-arg)) | |
124 | '())))))))) | |
47f3ce52 | 125 | |
4f08d0b5 | 126 | (define (macro-arguments name type transformer) |
47f3ce52 | 127 | (process-args |
4f08d0b5 AW |
128 | (case type |
129 | ((syntax-rules) | |
130 | (let ((patterns (program-property transformer 'patterns))) | |
131 | (if (pair? patterns) | |
132 | (car patterns) | |
133 | '()))) | |
134 | ((identifier-syntax) | |
135 | '()) | |
136 | ((defmacro) | |
137 | (or (program-property transformer 'defmacro-args) | |
138 | '())) | |
139 | (else | |
140 | ;; a procedural (syntax-case) macro. how to document these? | |
141 | '())))) | |
142 | ||
143 | (define (macro-additional-stexi name type transformer) | |
144 | (case type | |
145 | ((syntax-rules) | |
146 | (let ((patterns (program-property transformer 'patterns))) | |
147 | (if (pair? patterns) | |
148 | (map (lambda (x) | |
149 | `(defspecx (% (name ,name) | |
150 | (arguments ,@(process-args x))))) | |
151 | (cdr patterns)) | |
152 | '()))) | |
153 | (else | |
154 | '()))) | |
47f3ce52 AW |
155 | |
156 | (define many-space? (make-regexp "[[:space:]][[:space:]][[:space:]]")) | |
157 | (define initial-space? (make-regexp "^[[:space:]]")) | |
158 | (define (string->stexi str) | |
159 | (or (and (or (not str) (string-null? str)) | |
160 | '(*fragment*)) | |
161 | (and (or (string-index str #\@) | |
162 | (and (not (regexp-exec many-space? str)) | |
163 | (not (regexp-exec initial-space? str)))) | |
164 | (false-if-exception | |
165 | (texi-fragment->stexi str))) | |
166 | `(*fragment* (verbatim ,str)))) | |
167 | ||
168 | (define method-formals | |
169 | (and (defined? 'method-formals) method-formals)) | |
170 | ||
171 | (define (method-stexi-arguments method) | |
172 | (cond | |
173 | (method-formals | |
174 | (let lp ((formals (method-formals method)) | |
175 | (specializers (method-specializers method)) | |
176 | (out '())) | |
177 | (define (arg-texinfo formal specializer) | |
178 | `(" (" (var ,(symbol->string formal)) " " | |
179 | (code ,(symbol->string (class-name specializer))) ")")) | |
180 | (cond | |
181 | ((null? formals) (reverse out)) | |
182 | ((pair? formals) | |
183 | (lp (cdr formals) (cdr specializers) | |
184 | (append (reverse (arg-texinfo (car formals) (car specializers))) | |
185 | out))) | |
186 | (else | |
187 | (append (reverse out) (arg-texinfo formals specializers) | |
188 | (list "...")))))) | |
189 | ((method-source method) | |
190 | (let lp ((bindings (cadr (method-source method))) (out '())) | |
191 | (define (arg-texinfo arg) | |
192 | `(" (" (var ,(symbol->string (car arg))) " " | |
193 | (code ,(symbol->string (cadr arg))) ")")) | |
194 | (cond | |
195 | ((null? bindings) | |
196 | (reverse out)) | |
197 | ((not (pair? (car bindings))) | |
198 | (append (reverse out) (arg-texinfo bindings) (list "..."))) | |
199 | (else | |
200 | (lp (cdr bindings) | |
201 | (append (reverse (arg-texinfo (car bindings))) out)))))) | |
202 | (else (warn method) '()))) | |
203 | ||
204 | (define* (object-stexi-documentation object #:optional (name "[unknown]") | |
205 | #:key (force #f)) | |
206 | (if (symbol? name) | |
207 | (set! name (symbol->string name))) | |
208 | (let ((stexi ((lambda (x) | |
209 | (cond ((string? x) (string->stexi x)) | |
210 | ((and (pair? x) (eq? (car x) '*fragment*)) x) | |
211 | (force `(*fragment*)) | |
212 | (else #f))) | |
213 | (object-documentation | |
214 | (if (is-a? object <method>) | |
215 | (method-procedure object) | |
216 | object))))) | |
217 | (define (make-def type args) | |
218 | `(,type (% ,@args) ,@(cdr stexi))) | |
219 | (cond | |
220 | ((not stexi) #f) | |
221 | ;; stexi is now a list, headed by *fragment*. | |
222 | ((and (pair? (cdr stexi)) (pair? (cadr stexi)) | |
223 | (memq (caadr stexi) defs)) | |
224 | ;; it's already a deffoo. | |
225 | stexi) | |
226 | ((is-a? object <class>) | |
227 | (make-def 'deftp `((name ,name) | |
228 | (category "Class")))) | |
229 | ((is-a? object <macro>) | |
4f08d0b5 AW |
230 | (let* ((proc (macro-transformer object)) |
231 | (type (and proc (program-property proc 'macro-type)))) | |
232 | `(defspec (% (name ,name) | |
233 | (arguments ,@(macro-arguments name type proc))) | |
234 | ,@(macro-additional-stexi name type proc) | |
235 | ,@(cdr stexi)))) | |
236 | ||
47f3ce52 AW |
237 | ((is-a? object <procedure>) |
238 | (make-def 'defun `((name ,name) | |
239 | (arguments ,@(get-proc-args object))))) | |
240 | ((is-a? object <method>) | |
241 | (make-def 'deffn `((category "Method") | |
242 | (name ,name) | |
243 | (arguments ,@(method-stexi-arguments object))))) | |
244 | ((is-a? object <generic>) | |
245 | `(*fragment* | |
246 | ,(make-def 'deffn `((name ,name) | |
247 | (category "Generic"))) | |
248 | ,@(map | |
249 | (lambda (method) | |
250 | (object-stexi-documentation method name #:force force)) | |
251 | (generic-function-methods object)))) | |
252 | (else | |
253 | (make-def 'defvar `((name ,name))))))) | |
254 | ||
255 | (define (module-name->node-name sym-name) | |
256 | (string-join (map symbol->string sym-name) " ")) | |
257 | ||
258 | ;; this copied from (ice-9 session); need to find a better way | |
259 | (define (module-filename name) | |
260 | (let* ((name (map symbol->string name)) | |
261 | (reverse-name (reverse name)) | |
262 | (leaf (car reverse-name)) | |
263 | (dir-hint-module-name (reverse (cdr reverse-name))) | |
264 | (dir-hint (apply string-append | |
265 | (map (lambda (elt) | |
266 | (string-append elt "/")) | |
267 | dir-hint-module-name)))) | |
268 | (%search-load-path (in-vicinity dir-hint leaf)))) | |
269 | ||
270 | (define (read-module name) | |
271 | (let ((filename (module-filename name))) | |
272 | (if filename | |
273 | (let ((port (open-input-file filename))) | |
274 | (let lp ((out '()) (form (read port))) | |
275 | (if (eof-object? form) | |
276 | (reverse out) | |
277 | (lp (cons form out) (read port))))) | |
278 | '()))) | |
279 | ||
280 | (define (module-export-list sym-name) | |
281 | (define (module-form-export-list form) | |
282 | (and (pair? form) | |
283 | (eq? (car form) 'define-module) | |
284 | (equal? (cadr form) sym-name) | |
285 | (and=> (memq #:export (cddr form)) cadr))) | |
286 | (let lp ((forms (read-module sym-name))) | |
287 | (cond ((null? forms) '()) | |
288 | ((module-form-export-list (car forms)) => identity) | |
289 | (else (lp (cdr forms)))))) | |
290 | ||
291 | (define* (module-stexi-documentation sym-name | |
292 | #:optional (docs-resolver | |
293 | (lambda (name def) def))) | |
294 | "Return documentation for the module named @var{sym-name}. The | |
295 | documentation will be formatted as @code{stexi} | |
296 | (@pxref{texinfo,texinfo})." | |
297 | (let* ((commentary (and=> (module-commentary sym-name) | |
298 | (lambda (x) (string-trim-both x #\newline)))) | |
299 | (stexi (string->stexi commentary)) | |
300 | (node-name (module-name->node-name sym-name)) | |
301 | (name-str (with-output-to-string | |
302 | (lambda () (display sym-name)))) | |
303 | (module (resolve-interface sym-name)) | |
304 | (export-list (module-export-list sym-name))) | |
305 | (define (anchor-name sym) | |
306 | (string-append node-name " " (symbol->string sym))) | |
307 | (define (make-defs) | |
308 | (sort! | |
309 | (module-map | |
310 | (lambda (sym var) | |
311 | `((anchor (% (name ,(anchor-name sym)))) | |
312 | ,@((lambda (x) | |
313 | (if (eq? (car x) '*fragment*) | |
314 | (cdr x) | |
315 | (list x))) | |
316 | (if (variable-bound? var) | |
317 | (docs-resolver | |
318 | sym | |
319 | (object-stexi-documentation (variable-ref var) sym | |
320 | #:force #t)) | |
321 | (begin | |
322 | (warn "variable unbound!" sym) | |
323 | `(defvar (% (name ,(symbol->string sym))) | |
324 | "[unbound!]")))))) | |
325 | module) | |
326 | (lambda (a b) (sort-defs export-list a b)))) | |
327 | ||
328 | `(texinfo (% (title ,name-str)) | |
329 | (node (% (name ,node-name))) | |
330 | (section "Overview") | |
331 | ,@(cdr stexi) | |
332 | (section "Usage") | |
333 | ,@(apply append! (make-defs))))) | |
334 | ||
335 | (define (script-stexi-documentation scriptpath) | |
336 | "Return documentation for given script. The documentation will be | |
337 | taken from the script's commentary, and will be returned in the | |
338 | @code{stexi} format (@pxref{texinfo,texinfo})." | |
339 | (let ((commentary (file-commentary scriptpath))) | |
340 | `(texinfo (% (title ,(basename scriptpath))) | |
341 | (node (% (name ,(basename scriptpath)))) | |
342 | ,@(if commentary | |
343 | (cdr | |
344 | (string->stexi | |
345 | (string-trim-both commentary #\newline))) | |
346 | '())))) | |
347 | ||
348 | (cond | |
349 | ((defined? 'add-value-help-handler!) | |
350 | (add-value-help-handler! | |
351 | (lambda (name value) | |
352 | (stexi->plain-text | |
353 | (object-stexi-documentation value name #:force #t)))) | |
354 | (add-name-help-handler! | |
355 | (lambda (name) | |
356 | (and (list? name) | |
357 | (and-map symbol? name) | |
358 | (stexi->plain-text (module-stexi-documentation name))))))) | |
359 | ||
360 | ;; we could be dealing with an old (ice-9 session); fondle it to get | |
361 | ;; module-commentary | |
362 | (define module-commentary (@@ (ice-9 session) module-commentary)) | |
363 | ||
364 | (define (package-stexi-standard-copying name version updated years | |
365 | copyright-holder permissions) | |
366 | "Create a standard texinfo @code{copying} section. | |
367 | ||
368 | @var{years} is a list of years (as integers) in which the modules | |
369 | being documented were released. All other arguments are strings." | |
370 | `(copying | |
371 | (para "This manual is for " ,name | |
372 | " (version " ,version ", updated " ,updated ")") | |
373 | (para "Copyright " ,(string-join (map number->string years) ",") | |
374 | " " ,copyright-holder) | |
375 | (quotation | |
376 | (para ,permissions)))) | |
377 | ||
378 | (define (package-stexi-standard-titlepage name version updated authors) | |
379 | "Create a standard GNU title page. | |
380 | ||
381 | @var{authors} is a list of @code{(@var{name} . @var{email})} | |
382 | pairs. All other arguments are strings. | |
383 | ||
384 | Here is an example of the usage of this procedure: | |
385 | ||
386 | @smallexample | |
387 | (package-stexi-standard-titlepage | |
388 | \"Foolib\" | |
389 | \"3.2\" | |
390 | \"26 September 2006\" | |
391 | '((\"Alyssa P Hacker\" . \"alyssa@@example.com\")) | |
392 | '(2004 2005 2006) | |
393 | \"Free Software Foundation, Inc.\" | |
394 | \"Standard GPL permissions blurb goes here\") | |
395 | @end smallexample | |
396 | " | |
397 | `(;(setchapternewpage (% (all "odd"))) makes manuals too long | |
398 | (titlepage | |
399 | (title ,name) | |
400 | (subtitle "version " ,version ", updated " ,updated) | |
401 | ,@(map (lambda (pair) | |
402 | `(author ,(car pair) | |
403 | " (" (email ,(cdr pair)) ")")) | |
404 | authors) | |
405 | (page) | |
406 | (vskip (% (all "0pt plus 1filll"))) | |
407 | (insertcopying)))) | |
408 | ||
409 | (define (package-stexi-generic-menu name entries) | |
410 | "Create a menu from a generic alist of entries, the car of which | |
411 | should be the node name, and the cdr the description. As an exception, | |
412 | an entry of @code{#f} will produce a separator." | |
413 | (define (make-entry node description) | |
414 | `("* " ,node "::" | |
415 | ,(make-string (max (- 21 (string-length node)) 2) #\space) | |
416 | ,@description "\n")) | |
417 | `((ifnottex | |
418 | (node (% (name "Top"))) | |
419 | (top (% (title ,name))) | |
420 | (insertcopying) | |
421 | (menu | |
422 | ,@(apply | |
423 | append | |
424 | (map | |
425 | (lambda (entry) | |
426 | (if entry | |
427 | (make-entry (car entry) (cdr entry)) | |
428 | '("\n"))) | |
429 | entries)))) | |
430 | (iftex | |
431 | (shortcontents)))) | |
432 | ||
433 | ||
434 | (define (package-stexi-standard-menu name modules module-descriptions | |
435 | extra-entries) | |
436 | "Create a standard top node and menu, suitable for processing | |
437 | by makeinfo." | |
438 | (package-stexi-generic-menu | |
439 | name | |
440 | (let ((module-entries (map cons | |
441 | (map module-name->node-name modules) | |
442 | module-descriptions)) | |
443 | (separate-sections (lambda (x) (if (null? x) x (cons #f x))))) | |
444 | `(,@module-entries | |
445 | ,@(separate-sections extra-entries))))) | |
446 | ||
447 | (define (package-stexi-extended-menu name module-pairs script-pairs | |
448 | extra-entries) | |
449 | "Create an \"extended\" menu, like the standard menu but with a | |
450 | section for scripts." | |
451 | (package-stexi-generic-menu | |
452 | name | |
453 | (let ((module-entries (map cons | |
454 | (map module-name->node-name | |
455 | (map car module-pairs)) | |
456 | (map cdr module-pairs))) | |
457 | (script-entries (map cons | |
458 | (map basename (map car script-pairs)) | |
459 | (map cdr script-pairs))) | |
460 | (separate-sections (lambda (x) (if (null? x) x (cons #f x))))) | |
461 | `(,@module-entries | |
462 | ,@(separate-sections script-entries) | |
463 | ,@(separate-sections extra-entries))))) | |
464 | ||
465 | (define (package-stexi-standard-prologue name filename category | |
466 | description copying titlepage | |
467 | menu) | |
468 | "Create a standard prologue, suitable for later serialization | |
469 | to texinfo and .info creation with makeinfo. | |
470 | ||
471 | Returns a list of stexinfo forms suitable for passing to | |
472 | @code{package-stexi-documentation} as the prologue. @xref{texinfo | |
473 | reflection package-stexi-documentation}, @ref{texinfo reflection | |
474 | package-stexi-standard-titlepage,package-stexi-standard-titlepage}, | |
475 | @ref{texinfo reflection | |
476 | package-stexi-standard-copying,package-stexi-standard-copying}, | |
477 | and @ref{texinfo reflection | |
478 | package-stexi-standard-menu,package-stexi-standard-menu}." | |
479 | `(,copying | |
480 | (dircategory (% (category ,category))) | |
481 | (direntry | |
482 | "* " ,name ": (" ,filename "). " ,description ".") | |
483 | ,@titlepage | |
484 | ,@menu)) | |
485 | ||
486 | (define (stexi->chapter stexi) | |
487 | (pre-post-order | |
488 | stexi | |
489 | `((texinfo . ,(lambda (tag attrs node . body) | |
490 | `(,node | |
491 | (chapter ,@(assq-ref (cdr attrs) 'title)) | |
492 | ,@body))) | |
493 | (*text* . ,(lambda (tag text) text)) | |
494 | (*default* . ,(lambda args args))))) | |
495 | ||
496 | (define* (package-stexi-documentation modules name filename | |
497 | prologue epilogue | |
498 | #:key | |
499 | (module-stexi-documentation-args | |
500 | '()) | |
501 | (scripts '())) | |
502 | "Create stexi documentation for a @dfn{package}, where a | |
503 | package is a set of modules that is released together. | |
504 | ||
505 | @var{modules} is expected to be a list of module names, where a | |
506 | module name is a list of symbols. The stexi that is returned will | |
507 | be titled @var{name} and a texinfo filename of @var{filename}. | |
508 | ||
509 | @var{prologue} and @var{epilogue} are lists of stexi forms that | |
510 | will be spliced into the output document before and after the | |
511 | generated modules documentation, respectively. | |
512 | @xref{texinfo reflection package-stexi-standard-prologue}, to | |
513 | create a conventional GNU texinfo prologue. | |
514 | ||
515 | @var{module-stexi-documentation-args} is an optional argument that, if | |
516 | given, will be added to the argument list when | |
517 | @code{module-texi-documentation} is called. For example, it might be | |
518 | useful to define a @code{#:docs-resolver} argument." | |
519 | (define (verify-modules-list l) | |
520 | (define (all pred l) | |
521 | (and (pred (car l)) | |
522 | (or (null? (cdr l)) (all pred (cdr l))))) | |
523 | (false-if-exception | |
524 | (all (lambda (x) (all symbol? x)) modules))) | |
525 | (if (not (verify-modules-list modules)) | |
526 | (error "expected modules to be a list of a list of symbols" | |
527 | modules)) | |
528 | ||
529 | `(texinfo | |
530 | (% (title ,name) | |
531 | (filename ,filename)) | |
532 | ,@prologue | |
533 | ,@(append-map (lambda (mod) | |
534 | (stexi->chapter | |
535 | (apply module-stexi-documentation | |
536 | mod module-stexi-documentation-args))) | |
537 | modules) | |
538 | ,@(append-map (lambda (script) | |
539 | (stexi->chapter | |
540 | (script-stexi-documentation script))) | |
541 | scripts) | |
542 | ,@epilogue)) | |
543 | ||
c55cb58a AW |
544 | (define* (package-stexi-documentation-for-include modules module-descriptions |
545 | #:key | |
546 | (module-stexi-documentation-args '())) | |
547 | "Create stexi documentation for a @dfn{package}, where a | |
548 | package is a set of modules that is released together. | |
549 | ||
550 | @var{modules} is expected to be a list of module names, where a | |
551 | module name is a list of symbols. Returns an stexinfo fragment. | |
552 | ||
553 | Unlike @code{package-stexi-documentation}, this function simply produces | |
554 | a menu and the module documentations instead of producing a full texinfo | |
555 | document. This can be useful if you write part of your manual by hand, | |
556 | and just use @code{@@include} to pull in the automatically generated | |
557 | parts. | |
558 | ||
559 | @var{module-stexi-documentation-args} is an optional argument that, if | |
560 | given, will be added to the argument list when | |
561 | @code{module-texi-documentation} is called. For example, it might be | |
562 | useful to define a @code{#:docs-resolver} argument." | |
563 | (define (make-entry node description) | |
564 | `("* " ,node "::" | |
565 | ,(make-string (max (- 21 (string-length node)) 2) #\space) | |
566 | ,@description "\n")) | |
567 | `(*fragment* | |
568 | (menu | |
569 | ,@(append-map (lambda (modname desc) | |
570 | (make-entry (module-name->node-name modname) | |
571 | desc)) | |
572 | modules | |
573 | module-descriptions)) | |
574 | ,@(append-map (lambda (modname) | |
575 | (stexi->chapter | |
576 | (apply module-stexi-documentation | |
577 | modname | |
578 | module-stexi-documentation-args))) | |
579 | modules))) | |
580 | ||
47f3ce52 | 581 | ;;; arch-tag: bbe2bc03-e16d-4a9e-87b9-55225dc9836c |