merge from 1.8 branch
[bpt/guile.git] / ice-9 / session.scm
index 24438c2..1c9f480 100644 (file)
-;;;;   Copyright (C) 1997, 2000 Free Software Foundation, Inc.
+;;;;   Copyright (C) 1997, 2000, 2001, 2003, 2006 Free Software Foundation, Inc.
+;;;;
+;;;; This library is free software; you can redistribute it and/or
+;;;; modify it under the terms of the GNU Lesser General Public
+;;;; License as published by the Free Software Foundation; either
+;;;; version 2.1 of the License, or (at your option) any later version.
 ;;;; 
-;;;; This program 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.
-;;;; 
-;;;; This program is distributed in the hope that it will be useful,
+;;;; This library 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 this software; see the file COPYING.  If not, write to
-;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
-;;;; Boston, MA 02111-1307 USA
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;;; Lesser General Public License for more details.
 ;;;; 
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;;;;
 \f
 
 (define-module (ice-9 session)
   :use-module (ice-9 documentation)
-  )
+  :use-module (ice-9 regex)
+  :use-module (ice-9 rdelim)
+  :export (help apropos apropos-internal apropos-fold
+          apropos-fold-accessible apropos-fold-exported apropos-fold-all
+          source arity system-module))
 
 \f
 
 ;;; Documentation
 ;;;
-(define-public help
+(define help
   (procedure->syntax
     (lambda (exp env)
       "(help [NAME])
 Prints useful information.  Try `(help)'."
-      (if (not (= (length exp) 2))
-         (help-usage)
-         (let ((name (cadr exp)))
-           (cond ((symbol? name)
-                  (help-doc name
-                            (string-append "^"
-                                           (symbol->string name)
-                                           "$")))
-                 ((string? name)
-                  (help-doc name name))
-                 ((and (list? name)
-                       (= (length name) 2)
-                       (eq? (car name) 'unquote))
-                  (let ((doc (object-documentation (local-eval (cadr name)
-                                                               env))))
-                    (if (not doc)
-                        (simple-format #t "No documentation found for ~S\n"
-                                       (cadr name))
-                        (write-line doc))))
-                 (else
-                  (help-usage)))
-           *unspecified*)))))
+      (cond ((not (= (length exp) 2))
+             (help-usage))
+            ((not (provided? 'regex))
+             (display "`help' depends on the `regex' feature.
+You don't seem to have regular expressions installed.\n"))
+            (else
+             (let ((name (cadr exp))
+                   (not-found (lambda (type x)
+                                (simple-format #t "No ~A found for ~A\n"
+                                               type x))))
+               (cond
+
+                ;; SYMBOL
+                ((symbol? name)
+                 (help-doc name
+                           (simple-format
+                            #f "^~A$"
+                            (regexp-quote (symbol->string name)))))
+
+                ;; "STRING"
+                ((string? name)
+                 (help-doc name name))
+
+                ;; (unquote SYMBOL)
+                ((and (list? name)
+                      (= (length name) 2)
+                      (eq? (car name) 'unquote))
+                 (cond ((object-documentation
+                         (local-eval (cadr name) env))
+                        => write-line)
+                       (else (not-found 'documentation (cadr name)))))
+
+                ;; (quote SYMBOL)
+                ((and (list? name)
+                      (= (length name) 2)
+                      (eq? (car name) 'quote)
+                      (symbol? (cadr name)))
+                 (cond ((search-documentation-files (cadr name))
+                        => write-line)
+                       (else (not-found 'documentation (cadr name)))))
+
+                ;; (SYM1 SYM2 ...)
+                ((and (list? name)
+                      (and-map symbol? name)
+                      (not (null? name))
+                      (not (eq? (car name) 'quote)))
+                 (cond ((module-commentary name)
+                        => (lambda (doc)
+                             (display name) (write-line " commentary:")
+                             (write-line doc)))
+                       (else (not-found 'commentary name))))
+
+                ;; unrecognized
+                (else
+                 (help-usage)))
+               *unspecified*))))))
+
+(define (module-filename name)          ; fixme: better way? / done elsewhere?
+  (let* ((name (map symbol->string name))
+         (reverse-name (reverse name))
+        (leaf (car reverse-name))
+        (dir-hint-module-name (reverse (cdr reverse-name)))
+        (dir-hint (apply string-append
+                          (map (lambda (elt)
+                                 (string-append elt "/"))
+                               dir-hint-module-name))))
+    (%search-load-path (in-vicinity dir-hint leaf))))
+
+(define (module-commentary name)
+  (cond ((module-filename name) => file-commentary)
+        (else #f)))
 
 (define (help-doc term regexp)
   (let ((entries (apropos-fold (lambda (module name object data)
                                 (cons (list module
                                             name
-                                            (object-documentation object))
+                                            (object-documentation object)
+                                            (cond ((closure? object)
+                                                   "a procedure")
+                                                  ((procedure? object)
+                                                   "a primitive procedure")
+                                                  (else
+                                                   "an object")))
                                       data))
                               '()
                               regexp
                               apropos-fold-exported))
        (module car)
        (name cadr)
-       (doc caddr))
-    (if (null? entries)
-       ;; no matches
-       (begin
-         (display "Did not find any object ")
-         (simple-format #t
-                        (if (symbol? term)
-                            "named `~A'\n"
-                            "matching regexp \"~A\"\n")
-                        term))
-       (let ((first? #t))
-         (if (or-map doc entries)
-             ;; entries with documentation
-             (for-each (lambda (entry)
-                         ;; *fixme*: Use `describe' when we have GOOPS?
-                         (if (doc entry)
-                             (begin
-                               (if first?
-                                   (set! first? #f)
-                                   (newline))
-                               (simple-format #t "~S: ~S\n~A\n"
-                                              (module-name (module entry))
-                                              (name entry)
-                                              (doc entry)))))
-                       entries))
-         (if (or-map (lambda (x) (not (doc x))) entries)
-             ;; entries without documentation
-             (begin
-               (if (not first?)
-                   (display "\nNo documentation found for:\n"))
-               (for-each (lambda (entry)
-                           (if (not (doc entry))
-                               (simple-format #t "~S: ~S\n"
-                                              (module-name (module entry))
-                                              (name entry))))
-                         entries)))))))
+       (doc caddr)
+       (type cadddr))
+    (cond ((not (null? entries))
+           (let ((first? #t)
+                 (undocumented-entries '())
+                 (documented-entries '())
+                 (documentations '()))
+
+             (for-each (lambda (entry)
+                         (let ((entry-summary (simple-format
+                                               #f "~S: ~S\n"
+                                               (module-name (module entry))
+                                               (name entry))))
+                           (if (doc entry)
+                               (begin
+                                 (set! documented-entries
+                                       (cons entry-summary documented-entries))
+                                 ;; *fixme*: Use `describe' when we have GOOPS?
+                                 (set! documentations
+                                       (cons (simple-format
+                                              #f "`~S' is ~A in the ~S module.\n\n~A\n"
+                                              (name entry)
+                                              (type entry)
+                                              (module-name (module entry))
+                                              (doc entry))
+                                             documentations)))
+                               (set! undocumented-entries
+                                     (cons entry-summary
+                                           undocumented-entries)))))
+                       entries)
+
+             (if (and (not (null? documented-entries))
+                      (or (> (length documented-entries) 1)
+                          (not (null? undocumented-entries))))
+                 (begin
+                   (display "Documentation found for:\n")
+                   (for-each (lambda (entry) (display entry))
+                             documented-entries)
+                   (set! first? #f)))
+
+             (for-each (lambda (entry)
+                         (if first?
+                             (set! first? #f)
+                             (newline))
+                         (display entry))
+                       documentations)
+
+             (if (not (null? undocumented-entries))
+                 (begin
+                   (if first?
+                       (set! first? #f)
+                       (newline))
+                   (display "No documentation found for:\n")
+                   (for-each (lambda (entry) (display entry))
+                             undocumented-entries)))))
+          ((search-documentation-files term)
+           => (lambda (doc)
+                (write-line "Documentation from file:")
+                (write-line doc)))
+          (else
+           ;; no matches
+           (display "Did not find any object ")
+           (simple-format #t
+                          (if (symbol? term)
+                              "named `~A'\n"
+                              "matching regexp \"~A\"\n")
+                          term)))))
 
 (define (help-usage)
   (display "Usage: (help NAME) gives documentation about objects named NAME (a symbol)
        (help REGEXP) ditto for objects with names matching REGEXP (a string)
+       (help 'NAME) gives documentation for NAME, even if it is not an object
        (help ,EXPR) gives documentation for object returned by EXPR
+       (help (my module)) gives module commentary for `(my module)'
        (help) gives this text
 
 `help' searches among bindings exported from loaded modules, while
@@ -142,15 +233,18 @@ where OPTIONSET is one of debug, read, eval, print
 ;;; Author: Roland Orre <orre@nada.kth.se>
 ;;;
 
-(define (id x) x)
-
-(define-public (apropos rgx . options)
+(define (apropos rgx . options)
   "Search for bindings: apropos regexp {options= 'full 'shadow 'value}"
   (if (zero? (string-length rgx))
       "Empty string not allowed"
       (let* ((match (make-regexp rgx))
+            (uses (module-uses (current-module)))
             (modules (cons (current-module)
-                           (module-uses (current-module))))
+                           (if (and (not (null? uses))
+                                    (eq? (module-name (car uses))
+                                         'duplicates))
+                               (cdr uses)
+                               uses)))
             (separator #\tab)
             (shadow (member 'shadow options))
             (value (member 'value options)))
@@ -159,47 +253,32 @@ where OPTIONSET is one of debug, read, eval, print
               (set! value #t)))
        (for-each
         (lambda (module)
-          (let* ((builtin (or (eq? module the-scm-module)
-                              (eq? module the-root-module)))
-                 (name (module-name module))
-                 (obarrays (if builtin
-                               (list (builtin-weak-bindings)
-                                     (builtin-bindings))
-                               (list (module-obarray module))))
-                 (get-refs (if builtin
-                               (list id id)
-                               (list variable-ref)))
-                 )
-            (for-each
-             (lambda (obarray get-ref)
-               (array-for-each
-                (lambda (oblist)
-                  (for-each
-                   (lambda (x)
-                     (cond ((regexp-exec match (car x))
-                            (display name)
-                            (display ": ")
-                            (display (car x))
-                            (cond ((procedure? (get-ref (cdr x)))
-                                   (display separator)
-                                   (display (get-ref (cdr x))))
-                                  (value
-                                   (display separator)
-                                   (display (get-ref (cdr x)))))
-                            (if (and shadow
-                                     (not (eq? (module-ref module
-                                                           (car x))
-                                               (module-ref (current-module)
-                                                           (car x)))))
-                                (display " shadowed"))
-                            (newline)
-                            )))
-                   oblist))
-                obarray))
-             obarrays get-refs)))
+          (let* ((name (module-name module))
+                 (obarray (module-obarray module)))
+            ;; XXX - should use hash-fold here
+            (hash-for-each
+             (lambda (symbol variable)
+               (cond ((regexp-exec match (symbol->string symbol))
+                      (display name)
+                      (display ": ")
+                      (display symbol)
+                      (cond ((variable-bound? variable)
+                             (let ((val (variable-ref variable)))
+                               (cond ((or (procedure? val) value)
+                                      (display separator)
+                                      (display val)))))
+                            (else
+                             (display separator)
+                             (display "(unbound)")))
+                      (if (and shadow
+                               (not (eq? (module-ref module symbol)
+                                         (module-ref (current-module) symbol))))
+                          (display " shadowed"))
+                      (newline))))
+             obarray)))
         modules))))
 
-(define-public (apropos-internal rgx)
+(define (apropos-internal rgx)
   "Return a list of accessible variable names."
   (apropos-fold (lambda (module name var data)
                  (cons name data))
@@ -207,7 +286,7 @@ where OPTIONSET is one of debug, read, eval, print
                rgx
                (apropos-fold-accessible (current-module))))
 
-(define-public (apropos-fold proc init rgx folder)
+(define (apropos-fold proc init rgx folder)
   "Folds PROCEDURE over bindings matching third arg REGEXP.
 
 Result is
@@ -230,7 +309,7 @@ Fourth arg FOLDER is one of
           (lambda (module data)
             (let* ((obarray-filter
                     (lambda (name val data)
-                      (if (and (regexp-exec match name)
+                      (if (and (regexp-exec match (symbol->string name))
                                (not (hashq-get-handle recorded name)))
                           (begin
                             (hashq-set! recorded name #t)
@@ -238,15 +317,10 @@ Fourth arg FOLDER is one of
                           data)))
                    (module-filter
                     (lambda (name var data)
-                      (obarray-filter name (variable-ref var) data))))
-              (cond ((or (eq? module the-scm-module)
-                         (eq? module the-root-module))
-                     (hash-fold obarray-filter
-                                (hash-fold obarray-filter
-                                           data
-                                           (builtin-bindings))
-                                (builtin-weak-bindings)))
-                    (module (hash-fold module-filter
+                      (if (variable-bound? var)
+                          (obarray-filter name (variable-ref var) data)
+                          data))))
+              (cond (module (hash-fold module-filter
                                        data
                                        (module-obarray module)))
                     (else data))))))
@@ -258,17 +332,25 @@ The forest traversed is the image of the forest generated by root
 modules returned by INIT-THUNK and the generator TRAVERSE.
 It is an image under the mapping EXTRACT."
   (lambda (fold-module init)
-    (let rec ((data init)
-             (modules (init-thunk)))
-      (do ((modules modules (cdr modules))
-          (data data (rec (fold-module (extract (car modules)) data)
-                          (traverse (car modules)))))
-         ((null? modules) data)))))
-
-(define-public (apropos-fold-accessible module)
+    (let* ((table (make-hash-table 31))
+          (first? (lambda (obj)
+                    (let* ((handle (hash-create-handle! table obj #t))
+                           (first? (cdr handle)))
+                      (set-cdr! handle #f)
+                      first?))))
+      (let rec ((data init)
+               (modules (init-thunk)))
+       (do ((modules modules (cdr modules))
+            (data data (if (first? (car modules))
+                           (rec (fold-module (extract (car modules)) data)
+                                (traverse (car modules)))
+                           data)))
+           ((null? modules) data))))))
+
+(define (apropos-fold-accessible module)
   (make-fold-modules (lambda () (list module))
                     module-uses
-                    (lambda (x) x)))
+                    identity))
 
 (define (root-modules)
   (cons the-root-module
@@ -276,7 +358,7 @@ It is an image under the mapping EXTRACT."
 
 (define (submodules m)
   (hash-fold (lambda (name var data)
-              (let ((obj (variable-ref var)))
+              (let ((obj (and (variable-bound? var) (variable-ref var))))
                 (if (and (module? obj)
                          (eq? (module-kind obj) 'directory))
                     (cons obj data)
@@ -284,57 +366,99 @@ It is an image under the mapping EXTRACT."
             '()
             (module-obarray m)))
 
-(define-public apropos-fold-exported
+(define apropos-fold-exported
   (make-fold-modules root-modules submodules module-public-interface))
 
-(define-public apropos-fold-all
-  (make-fold-modules root-modules submodules (lambda (x) x)))
+(define apropos-fold-all
+  (make-fold-modules root-modules submodules identity))
 
-(define-public (source obj)
+(define (source obj)
   (cond ((procedure? obj) (procedure-source obj))
        ((macro? obj) (procedure-source (macro-transformer obj)))
        (else #f)))
 
-(define-public (arity obj)
-  (let ((arity (procedure-property obj 'arity)))
-    (display (car arity))
-    (cond ((caddr arity)
-          (display " or more"))
-         ((not (zero? (cadr arity)))
-          (display " required and ")
-          (display (cadr arity))
-          (display " optional")))
-    (if (and (not (caddr arity))
-            (= (car arity) 1)
-            (<= (cadr arity) 1))
-       (display " argument")
-       (display " arguments"))
-    (if (closure? obj)
-       (let ((formals (cadr (procedure-source obj))))
-         (if (pair? formals)
-             (begin
-               (display ": `")
-               (display (car formals))
-               (let loop ((ls (cdr formals)))
-                 (cond ((null? ls)
-                        (display #\'))
-                       ((not (pair? ls))
-                        (display "', the rest in `")
-                        (display ls)
-                        (display #\'))
-                       (else
-                        (if (pair? (cdr ls))
-                            (display "', `")
-                            (display "' and `"))
-                        (display (car ls))
-                        (loop (cdr ls))))))
-             (begin
-               (display " in `")
-               (display formals)
-               (display #\')))))
-    (display ".\n")))
-
-(define-public system-module
+(define (arity obj)
+  (define (display-arg-list arg-list)
+    (display #\`)
+    (display (car arg-list))
+    (let loop ((ls (cdr arg-list)))
+      (cond ((null? ls)
+            (display #\'))
+           ((not (pair? ls))
+            (display "', the rest in `")
+            (display ls)
+            (display #\'))
+           (else
+            (if (pair? (cdr ls))
+                (display "', `")
+                (display "' and `"))
+            (display (car ls))
+            (loop (cdr ls))))))
+  (define (display-arg-list/summary arg-list type)
+    (let ((len (length arg-list)))
+      (display len)
+      (display " ")
+      (display type)
+      (if (> len 1)
+         (display " arguments: ")
+         (display " argument: "))
+      (display-arg-list arg-list)))
+  (cond
+   ((procedure-property obj 'arglist)
+    => (lambda (arglist)
+        (let ((required-args (car arglist))
+              (optional-args (cadr arglist))
+              (keyword-args (caddr arglist))
+              (allow-other-keys? (cadddr arglist))
+              (rest-arg (car (cddddr arglist)))
+              (need-punctuation #f))
+          (cond ((not (null? required-args))
+                 (display-arg-list/summary required-args "required")
+                 (set! need-punctuation #t)))
+          (cond ((not (null? optional-args))
+                 (if need-punctuation (display ", "))
+                 (display-arg-list/summary optional-args "optional")
+                 (set! need-punctuation #t)))
+          (cond ((not (null? keyword-args))
+                 (if need-punctuation (display ", "))
+                 (display-arg-list/summary keyword-args "keyword")
+                 (set! need-punctuation #t)))
+          (cond (allow-other-keys?
+                 (if need-punctuation (display ", "))
+                 (display "other keywords allowed")
+                 (set! need-punctuation #t)))
+          (cond (rest-arg
+                 (if need-punctuation (display ", "))
+                 (display "the rest in `")
+                 (display rest-arg)
+                 (display "'"))))))
+   (else
+    (let ((arity (procedure-property obj 'arity)))
+      (display (car arity))
+      (cond ((caddr arity)
+            (display " or more"))
+           ((not (zero? (cadr arity)))
+            (display " required and ")
+            (display (cadr arity))
+            (display " optional")))
+      (if (and (not (caddr arity))
+              (= (car arity) 1)
+              (<= (cadr arity) 1))
+         (display " argument")
+         (display " arguments"))
+      (if (closure? obj)
+         (let ((formals (cadr (procedure-source obj))))
+           (cond
+            ((pair? formals)
+             (display ": ")
+             (display-arg-list formals))
+            (else
+             (display " in `")
+             (display formals)
+             (display #\'))))))))
+  (display ".\n"))
+
+(define system-module
   (procedure->syntax
    (lambda (exp env)
      (let* ((m (nested-ref the-root-module
@@ -346,3 +470,5 @@ It is an image under the mapping EXTRACT."
         (set-system-module! m s)
         (string-append "Module " (symbol->string (module-name m))
                        " is now a " (if s "system" "user") " module."))))))
+
+;;; session.scm ends here