Adjust `unbound-variable' GOOPS heuristic for `goops.scm'.
[bpt/guile.git] / module / ice-9 / session.scm
CommitLineData
cd5fea8d 1;;;; Copyright (C) 1997, 2000, 2001, 2003, 2006 Free Software Foundation, Inc.
7bb1bfc2 2;;;;
73be1d9e
MV
3;;;; This library is free software; you can redistribute it and/or
4;;;; modify it under the terms of the GNU Lesser General Public
5;;;; License as published by the Free Software Foundation; either
53befeb7 6;;;; version 3 of the License, or (at your option) any later version.
73be1d9e
MV
7;;;;
8;;;; This library is distributed in the hope that it will be useful,
0e81dabd 9;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
10;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11;;;; Lesser General Public License for more details.
12;;;;
13;;;; You should have received a copy of the GNU Lesser General Public
14;;;; License along with this library; if not, write to the Free Software
92205699 15;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
a482f2cc 16;;;;
0e81dabd
MD
17\f
18
bbefd480 19(define-module (ice-9 session)
3bdca000 20 :use-module (ice-9 documentation)
3510b484 21 :use-module (ice-9 regex)
1a179b03 22 :use-module (ice-9 rdelim)
4f7a0504
AW
23 :export (help
24 add-value-help-handler! remove-value-help-handler!
25 add-name-help-handler! remove-name-help-handler!
26 apropos apropos-internal apropos-fold apropos-fold-accessible
27 apropos-fold-exported apropos-fold-all source arity
df22662f
AW
28 procedure-arguments
29 module-commentary))
0e81dabd
MD
30
31\f
32
53d81399
AW
33(define *value-help-handlers*
34 `(,(lambda (name value)
35 (object-documentation value))))
4f7a0504
AW
36
37(define (add-value-help-handler! proc)
38 "Adds a handler for performing `help' on a value.
39
40`proc' will be called as (PROC NAME VALUE). `proc' should return #t to
41indicate that it has performed help, a string to override the default
42object documentation, or #f to try the other handlers, potentially
43falling back on the normal behavior for `help'."
44 (set! *value-help-handlers* (cons proc *value-help-handlers*)))
45
46(define (remove-value-help-handler! proc)
53d81399 47 "Removes a handler for performing `help' on a value."
4f7a0504
AW
48 (set! *value-help-handlers* (delete! proc *value-help-handlers*)))
49
50(define (try-value-help name value)
51 (or-map (lambda (proc) (proc name value)) *value-help-handlers*))
52
53
54(define *name-help-handlers* '())
55
56(define (add-name-help-handler! proc)
57 "Adds a handler for performing `help' on a name.
58
59`proc' will be called with the unevaluated name as its argument. That is
60to say, when the user calls `(help FOO)', the name is FOO, exactly as
61the user types it.
62
53d81399
AW
63`proc' should return #t to indicate that it has performed help, a string
64to override the default object documentation, or #f to try the other
65handlers, potentially falling back on the normal behavior for `help'."
4f7a0504
AW
66 (set! *name-help-handlers* (cons proc *name-help-handlers*)))
67
68(define (remove-name-help-handler! proc)
53d81399 69 "Removes a handler for performing `help' on a name."
4f7a0504
AW
70 (set! *name-help-handlers* (delete! proc *name-help-handlers*)))
71
72(define (try-name-help name)
73 (or-map (lambda (proc) (proc name)) *name-help-handlers*))
74
75
13ae9151
MD
76;;; Documentation
77;;;
99b1dd09
AW
78(define-macro (help . exp)
79 "(help [NAME])
13ae9151 80Prints useful information. Try `(help)'."
99b1dd09 81 (cond ((not (= (length exp) 1))
2588eccd
AW
82 (help-usage)
83 '(begin))
99b1dd09
AW
84 ((not (provided? 'regex))
85 (display "`help' depends on the `regex' feature.
2588eccd
AW
86You don't seem to have regular expressions installed.\n")
87 '(begin))
99b1dd09
AW
88 (else
89 (let ((name (car exp))
90 (not-found (lambda (type x)
91 (simple-format #t "No ~A found for ~A\n"
92 type x))))
93 (cond
94
df22662f
AW
95 ;; User-specified
96 ((try-name-help name)
97 => (lambda (x) (if (not (eq? x #t)) (display x))))
98
99b1dd09
AW
99 ;; SYMBOL
100 ((symbol? name)
101 (help-doc name
102 (simple-format
103 #f "^~A$"
104 (regexp-quote (symbol->string name)))))
105
106 ;; "STRING"
107 ((string? name)
108 (help-doc name name))
109
110 ;; (unquote SYMBOL)
111 ((and (list? name)
112 (= (length name) 2)
113 (eq? (car name) 'unquote))
df22662f
AW
114 (let ((doc (try-value-help (cadr name)
115 (local-eval (cadr name) env))))
116 (cond ((not doc) (not-found 'documentation (cadr name)))
117 ((eq? doc #t)) ;; pass
118 (else (write-line doc)))))
99b1dd09
AW
119
120 ;; (quote SYMBOL)
121 ((and (list? name)
122 (= (length name) 2)
123 (eq? (car name) 'quote)
124 (symbol? (cadr name)))
125 (cond ((search-documentation-files (cadr name))
126 => write-line)
127 (else (not-found 'documentation (cadr name)))))
128
129 ;; (SYM1 SYM2 ...)
130 ((and (list? name)
131 (and-map symbol? name)
132 (not (null? name))
133 (not (eq? (car name) 'quote)))
134 (cond ((module-commentary name)
135 => (lambda (doc)
136 (display name) (write-line " commentary:")
137 (write-line doc)))
138 (else (not-found 'commentary name))))
139
140 ;; unrecognized
de25f281 141 (else
99b1dd09
AW
142 (help-usage)))
143 '(begin)))))
13ae9151 144
7bb1bfc2
TTN
145(define (module-filename name) ; fixme: better way? / done elsewhere?
146 (let* ((name (map symbol->string name))
147 (reverse-name (reverse name))
148 (leaf (car reverse-name))
149 (dir-hint-module-name (reverse (cdr reverse-name)))
150 (dir-hint (apply string-append
151 (map (lambda (elt)
152 (string-append elt "/"))
153 dir-hint-module-name))))
154 (%search-load-path (in-vicinity dir-hint leaf))))
155
156(define (module-commentary name)
157 (cond ((module-filename name) => file-commentary)
158 (else #f)))
159
3bdca000
MD
160(define (help-doc term regexp)
161 (let ((entries (apropos-fold (lambda (module name object data)
162 (cons (list module
163 name
53d81399 164 (try-value-help name object)
db611983
NJ
165 (cond ((closure? object)
166 "a procedure")
167 ((procedure? object)
168 "a primitive procedure")
169 (else
170 "an object")))
3bdca000
MD
171 data))
172 '()
173 regexp
174 apropos-fold-exported))
175 (module car)
176 (name cadr)
db611983
NJ
177 (doc caddr)
178 (type cadddr))
de25f281
TTN
179 (cond ((not (null? entries))
180 (let ((first? #t)
181 (undocumented-entries '())
182 (documented-entries '())
183 (documentations '()))
184
185 (for-each (lambda (entry)
186 (let ((entry-summary (simple-format
187 #f "~S: ~S\n"
188 (module-name (module entry))
189 (name entry))))
190 (if (doc entry)
191 (begin
192 (set! documented-entries
193 (cons entry-summary documented-entries))
194 ;; *fixme*: Use `describe' when we have GOOPS?
195 (set! documentations
196 (cons (simple-format
197 #f "`~S' is ~A in the ~S module.\n\n~A\n"
198 (name entry)
199 (type entry)
200 (module-name (module entry))
201 (doc entry))
202 documentations)))
203 (set! undocumented-entries
204 (cons entry-summary
205 undocumented-entries)))))
206 entries)
207
208 (if (and (not (null? documented-entries))
209 (or (> (length documented-entries) 1)
210 (not (null? undocumented-entries))))
211 (begin
212 (display "Documentation found for:\n")
213 (for-each (lambda (entry) (display entry))
214 documented-entries)
215 (set! first? #f)))
216
217 (for-each (lambda (entry)
218 (if first?
219 (set! first? #f)
220 (newline))
221 (display entry))
222 documentations)
223
224 (if (not (null? undocumented-entries))
225 (begin
226 (if first?
227 (set! first? #f)
228 (newline))
229 (display "No documentation found for:\n")
230 (for-each (lambda (entry) (display entry))
231 undocumented-entries)))))
232 ((search-documentation-files term)
233 => (lambda (doc)
234 (write-line "Documentation from file:")
235 (write-line doc)))
236 (else
237 ;; no matches
238 (display "Did not find any object ")
239 (simple-format #t
240 (if (symbol? term)
241 "named `~A'\n"
242 "matching regexp \"~A\"\n")
243 term)))))
3bdca000 244
13ae9151 245(define (help-usage)
3bdca000
MD
246 (display "Usage: (help NAME) gives documentation about objects named NAME (a symbol)
247 (help REGEXP) ditto for objects with names matching REGEXP (a string)
1de3b33b 248 (help 'NAME) gives documentation for NAME, even if it is not an object
d1c50f73 249 (help ,EXPR) gives documentation for object returned by EXPR
8bbe4c82 250 (help (my module)) gives module commentary for `(my module)'
13ae9151
MD
251 (help) gives this text
252
3bdca000
MD
253`help' searches among bindings exported from loaded modules, while
254`apropos' searches among bindings visible from the \"current\" module.
255
2f52380c
MD
256Examples: (help help)
257 (help cons)
3bdca000 258 (help \"output-string\")
13ae9151
MD
259
260Other useful sources of helpful information:
261
262(apropos STRING)
263(arity PROCEDURE)
264(name PROCEDURE-OR-MACRO)
265(source PROCEDURE-OR-MACRO)
266
267Tools:
268
2f52380c
MD
269(backtrace) ;show backtrace from last error
270(debug) ;enter the debugger
271(trace [PROCEDURE]) ;trace procedure (no arg => show)
272(untrace [PROCEDURE]) ;untrace (no arg => untrace all)
13ae9151
MD
273
274(OPTIONSET-options 'full) ;display option information
275(OPTIONSET-enable 'OPTION)
276(OPTIONSET-disable 'OPTION)
277(OPTIONSET-set! OPTION VALUE)
278
279where OPTIONSET is one of debug, read, eval, print
280
281"))
282
0e81dabd
MD
283;;; {Apropos}
284;;;
285;;; Author: Roland Orre <orre@nada.kth.se>
286;;;
287
1a179b03 288(define (apropos rgx . options)
0e81dabd
MD
289 "Search for bindings: apropos regexp {options= 'full 'shadow 'value}"
290 (if (zero? (string-length rgx))
291 "Empty string not allowed"
4f161c5c 292 (let* ((match (make-regexp rgx))
3742da68 293 (uses (module-uses (current-module)))
0e81dabd 294 (modules (cons (current-module)
3742da68
MD
295 (if (and (not (null? uses))
296 (eq? (module-name (car uses))
297 'duplicates))
298 (cdr uses)
299 uses)))
0e81dabd
MD
300 (separator #\tab)
301 (shadow (member 'shadow options))
302 (value (member 'value options)))
303 (cond ((member 'full options)
304 (set! shadow #t)
305 (set! value #t)))
306 (for-each
307 (lambda (module)
296ff5e7
MV
308 (let* ((name (module-name module))
309 (obarray (module-obarray module)))
310 ;; XXX - should use hash-fold here
1798b73d
MD
311 (hash-for-each
312 (lambda (symbol variable)
313 (cond ((regexp-exec match (symbol->string symbol))
314 (display name)
315 (display ": ")
316 (display symbol)
317 (cond ((variable-bound? variable)
318 (let ((val (variable-ref variable)))
319 (cond ((or (procedure? val) value)
320 (display separator)
321 (display val)))))
322 (else
323 (display separator)
324 (display "(unbound)")))
325 (if (and shadow
326 (not (eq? (module-ref module symbol)
327 (module-ref (current-module) symbol))))
328 (display " shadowed"))
329 (newline))))
10764e3c 330 obarray)))
0e81dabd 331 modules))))
68aed3ea 332
1a179b03 333(define (apropos-internal rgx)
68aed3ea 334 "Return a list of accessible variable names."
3bdca000
MD
335 (apropos-fold (lambda (module name var data)
336 (cons name data))
337 '()
338 rgx
339 (apropos-fold-accessible (current-module))))
340
1a179b03 341(define (apropos-fold proc init rgx folder)
3bdca000
MD
342 "Folds PROCEDURE over bindings matching third arg REGEXP.
343
344Result is
345
346 (PROCEDURE MODULE1 NAME1 VALUE1
347 (PROCEDURE MODULE2 NAME2 VALUE2
348 ...
349 (PROCEDURE MODULEn NAMEn VALUEn INIT)))
350
351where INIT is the second arg to `apropos-fold'.
352
353Fourth arg FOLDER is one of
354
355 (apropos-fold-accessible MODULE) ;fold over bindings accessible in MODULE
356 apropos-fold-exported ;fold over all exported bindings
357 apropos-fold-all ;fold over all bindings"
358 (let ((match (make-regexp rgx))
359 (recorded (make-vector 61 '())))
360 (let ((fold-module
361 (lambda (module data)
362 (let* ((obarray-filter
363 (lambda (name val data)
4adc3028 364 (if (and (regexp-exec match (symbol->string name))
3bdca000
MD
365 (not (hashq-get-handle recorded name)))
366 (begin
367 (hashq-set! recorded name #t)
368 (proc module name val data))
369 data)))
370 (module-filter
371 (lambda (name var data)
aef9dd65
MV
372 (if (variable-bound? var)
373 (obarray-filter name (variable-ref var) data)
374 data))))
296ff5e7 375 (cond (module (hash-fold module-filter
3bdca000
MD
376 data
377 (module-obarray module)))
378 (else data))))))
379 (folder fold-module init))))
380
381(define (make-fold-modules init-thunk traverse extract)
382 "Return procedure capable of traversing a forest of modules.
383The forest traversed is the image of the forest generated by root
384modules returned by INIT-THUNK and the generator TRAVERSE.
385It is an image under the mapping EXTRACT."
386 (lambda (fold-module init)
9aec4751
MD
387 (let* ((table (make-hash-table 31))
388 (first? (lambda (obj)
8d627248
MD
389 (let* ((handle (hash-create-handle! table obj #t))
390 (first? (cdr handle)))
391 (set-cdr! handle #f)
392 first?))))
9aec4751
MD
393 (let rec ((data init)
394 (modules (init-thunk)))
395 (do ((modules modules (cdr modules))
396 (data data (if (first? (car modules))
397 (rec (fold-module (extract (car modules)) data)
398 (traverse (car modules)))
399 data)))
400 ((null? modules) data))))))
3bdca000 401
1a179b03 402(define (apropos-fold-accessible module)
3bdca000
MD
403 (make-fold-modules (lambda () (list module))
404 module-uses
de25f281 405 identity))
3bdca000
MD
406
407(define (root-modules)
408 (cons the-root-module
409 (submodules (nested-ref the-root-module '(app modules)))))
410
411(define (submodules m)
412 (hash-fold (lambda (name var data)
aef9dd65 413 (let ((obj (and (variable-bound? var) (variable-ref var))))
3bdca000
MD
414 (if (and (module? obj)
415 (eq? (module-kind obj) 'directory))
416 (cons obj data)
417 data)))
418 '()
419 (module-obarray m)))
420
1a179b03 421(define apropos-fold-exported
3bdca000
MD
422 (make-fold-modules root-modules submodules module-public-interface))
423
1a179b03 424(define apropos-fold-all
de25f281 425 (make-fold-modules root-modules submodules identity))
7cfae7e6 426
1a179b03 427(define (source obj)
7cfae7e6
MD
428 (cond ((procedure? obj) (procedure-source obj))
429 ((macro? obj) (procedure-source (macro-transformer obj)))
430 (else #f)))
4a9f464e 431
1a179b03 432(define (arity obj)
c7bb434f
TTN
433 (define (display-arg-list arg-list)
434 (display #\`)
435 (display (car arg-list))
436 (let loop ((ls (cdr arg-list)))
437 (cond ((null? ls)
438 (display #\'))
439 ((not (pair? ls))
440 (display "', the rest in `")
441 (display ls)
442 (display #\'))
443 (else
444 (if (pair? (cdr ls))
445 (display "', `")
446 (display "' and `"))
447 (display (car ls))
448 (loop (cdr ls))))))
449 (define (display-arg-list/summary arg-list type)
450 (let ((len (length arg-list)))
451 (display len)
452 (display " ")
453 (display type)
454 (if (> len 1)
455 (display " arguments: ")
456 (display " argument: "))
457 (display-arg-list arg-list)))
458 (cond
459 ((procedure-property obj 'arglist)
460 => (lambda (arglist)
461 (let ((required-args (car arglist))
462 (optional-args (cadr arglist))
463 (keyword-args (caddr arglist))
464 (allow-other-keys? (cadddr arglist))
465 (rest-arg (car (cddddr arglist)))
466 (need-punctuation #f))
467 (cond ((not (null? required-args))
468 (display-arg-list/summary required-args "required")
469 (set! need-punctuation #t)))
470 (cond ((not (null? optional-args))
471 (if need-punctuation (display ", "))
472 (display-arg-list/summary optional-args "optional")
473 (set! need-punctuation #t)))
474 (cond ((not (null? keyword-args))
475 (if need-punctuation (display ", "))
476 (display-arg-list/summary keyword-args "keyword")
477 (set! need-punctuation #t)))
478 (cond (allow-other-keys?
479 (if need-punctuation (display ", "))
480 (display "other keywords allowed")
481 (set! need-punctuation #t)))
482 (cond (rest-arg
483 (if need-punctuation (display ", "))
484 (display "the rest in `")
485 (display rest-arg)
486 (display "'"))))))
487 (else
488 (let ((arity (procedure-property obj 'arity)))
489 (display (car arity))
490 (cond ((caddr arity)
491 (display " or more"))
492 ((not (zero? (cadr arity)))
493 (display " required and ")
494 (display (cadr arity))
495 (display " optional")))
496 (if (and (not (caddr arity))
497 (= (car arity) 1)
498 (<= (cadr arity) 1))
499 (display " argument")
500 (display " arguments"))
501 (if (closure? obj)
502 (let ((formals (cadr (procedure-source obj))))
503 (cond
504 ((pair? formals)
505 (display ": ")
506 (display-arg-list formals))
507 (else
508 (display " in `")
509 (display formals)
510 (display #\'))))))))
511 (display ".\n"))
6ae34994 512
0704c813
AW
513
514(define (procedure-arguments proc)
515 "Return an alist describing the arguments that `proc' accepts, or `#f'
516if the information cannot be obtained.
517
518The alist keys that are currently defined are `required', `optional',
519`keyword', and `rest'."
520 (cond
521 ((procedure-property proc 'arglist)
522 => (lambda (arglist)
523 `((required . ,(car arglist))
524 (optional . ,(cadr arglist))
525 (keyword . ,(caddr arglist))
526 (rest . ,(car (cddddr arglist))))))
527 ((procedure-source proc)
528 => cadr)
529 (((@ (system vm program) program?) proc)
530 ((@ (system vm program) program-arguments) proc))
531 (else #f)))
532
533
de25f281 534;;; session.scm ends here