documentation.scm 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. ;;;; Copyright (C) 2000-2003, 2006, 2017 Free Software Foundation, Inc.
  2. ;;;;
  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
  6. ;;;; version 2.1 of the License, or (at your option) any later version.
  7. ;;;;
  8. ;;;; This library is distributed in the hope that it will be useful,
  9. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  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
  15. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. ;;;;
  17. ;;; Commentary:
  18. ;; * This module exports:
  19. ;;
  20. ;; file-commentary -- a procedure that returns a file's "commentary"
  21. ;;
  22. ;; documentation-files -- a search-list of files using the Guile
  23. ;; Documentation Format Version 2.
  24. ;;
  25. ;; search-documentation-files -- a procedure that takes NAME (a symbol)
  26. ;; and searches `documentation-files' for
  27. ;; associated documentation. optional
  28. ;; arg FILES is a list of filenames to use
  29. ;; instead of `documentation-files'.
  30. ;;
  31. ;; object-documentation -- a procedure that returns its arg's docstring
  32. ;;
  33. ;; * Guile Documentation Format
  34. ;;
  35. ;; Here is the complete and authoritative documentation for the Guile
  36. ;; Documentation Format Version 2:
  37. ;;
  38. ;; HEADER
  39. ;; ^LPROC1
  40. ;; DOCUMENTATION1
  41. ;;
  42. ;; ^LPROC2
  43. ;; DOCUMENTATION2
  44. ;;
  45. ;; ^L...
  46. ;;
  47. ;; The HEADER is completely ignored. The "^L" are formfeeds. PROC1, PROC2
  48. ;; and so on are symbols that name the element documented. DOCUMENTATION1,
  49. ;; DOCUMENTATION2 and so on are the related documentation, w/o any further
  50. ;; formatting. Note that there are two newlines before the next formfeed;
  51. ;; these are discarded when the documentation is read in.
  52. ;;
  53. ;; (Version 1, corresponding to guile-1.4 and prior, is documented as being
  54. ;; not documented anywhere except by this embarrassingly circular comment.)
  55. ;;
  56. ;; * File Commentary
  57. ;;
  58. ;; A file's commentary is the body of text found between comments
  59. ;; ;;; Commentary:
  60. ;; and
  61. ;; ;;; Code:
  62. ;; both of which must be at the beginning of the line. In the result string,
  63. ;; semicolons at the beginning of each line are discarded.
  64. ;;
  65. ;; You can specify to `file-commentary' alternate begin and end strings, and
  66. ;; scrub procedure. Use #t to get default values. For example:
  67. ;;
  68. ;; (file-commentary "documentation.scm")
  69. ;; You should see this text!
  70. ;;
  71. ;; (file-commentary "documentation.scm" "^;;; Code:" "ends here$")
  72. ;; You should see the rest of this file.
  73. ;;
  74. ;; (file-commentary "documentation.scm" #t #t string-upcase)
  75. ;; You should see this text very loudly (note semicolons untouched).
  76. ;;; Code:
  77. (define-module (ice-9 documentation)
  78. :use-module (ice-9 rdelim)
  79. :export (file-commentary
  80. documentation-files search-documentation-files
  81. object-documentation)
  82. :autoload (ice-9 regex) (match:suffix)
  83. :no-backtrace)
  84. ;;
  85. ;; commentary extraction
  86. ;;
  87. (define (file-commentary filename . cust) ; (IN-LINE-RE AFTER-LINE-RE SCRUB)
  88. ;; These are constants but are not at the top level because the repl in
  89. ;; boot-9.scm loads session.scm which in turn loads this file, and we want
  90. ;; that to work even even when regexps are not available (ie. make-regexp
  91. ;; doesn't exist), as for instance is the case on mingw.
  92. ;;
  93. (define default-in-line-re (make-regexp "^;;; Commentary:"))
  94. (define default-after-line-re (make-regexp "^;;; Code:"))
  95. (define default-scrub (let ((dirt (make-regexp "^;+ ?")))
  96. (lambda (line)
  97. (let ((m (regexp-exec dirt line)))
  98. (if m (match:suffix m) line)))))
  99. ;; fixme: might be cleaner to use optargs here...
  100. (let ((in-line-re (if (> 1 (length cust))
  101. default-in-line-re
  102. (let ((v (car cust)))
  103. (cond ((regexp? v) v)
  104. ((string? v) (make-regexp v))
  105. (else default-in-line-re)))))
  106. (after-line-re (if (> 2 (length cust))
  107. default-after-line-re
  108. (let ((v (cadr cust)))
  109. (cond ((regexp? v) v)
  110. ((string? v) (make-regexp v))
  111. (else default-after-line-re)))))
  112. (scrub (if (> 3 (length cust))
  113. default-scrub
  114. (let ((v (caddr cust)))
  115. (cond ((procedure? v) v)
  116. (else default-scrub))))))
  117. (call-with-input-file filename
  118. (lambda (port)
  119. (let loop ((line (read-delimited "\n" port))
  120. (doc "")
  121. (parse-state 'before))
  122. (if (or (eof-object? line) (eq? 'after parse-state))
  123. doc
  124. (let ((new-state
  125. (cond ((regexp-exec in-line-re line) 'in)
  126. ((regexp-exec after-line-re line) 'after)
  127. (else parse-state))))
  128. (if (eq? 'after new-state)
  129. doc
  130. (loop (read-delimited "\n" port)
  131. (if (and (eq? 'in new-state) (eq? 'in parse-state))
  132. (string-append doc (scrub line) "\n")
  133. doc)
  134. new-state)))))))))
  135. ;;
  136. ;; documentation-files is the list of places to look for documentation
  137. ;;
  138. (define documentation-files
  139. (map (lambda (vicinity)
  140. (in-vicinity (vicinity) "guile-procedures.txt"))
  141. (list %library-dir
  142. %package-data-dir
  143. %site-dir
  144. (lambda () "."))))
  145. (define entry-delimiter "\f")
  146. (define (find-documentation-in-file name file)
  147. (and (file-exists? file)
  148. (call-with-input-file file
  149. (lambda (port)
  150. (let ((name (symbol->string name)))
  151. (let ((len (string-length name)))
  152. (read-delimited entry-delimiter port) ;skip to first entry
  153. (let loop ((entry (read-delimited entry-delimiter port)))
  154. (cond ((eof-object? entry) #f)
  155. ;; match?
  156. ((and ;; large enough?
  157. (>= (string-length entry) len)
  158. ;; matching name?
  159. (string=? (substring entry 0 len) name)
  160. ;; terminated?
  161. (memq (string-ref entry len) '(#\newline)))
  162. ;; cut away name tag and extra surrounding newlines
  163. (substring entry (+ len 2) (- (string-length entry) 2)))
  164. (else (loop (read-delimited entry-delimiter port)))))))))))
  165. (define (search-documentation-files name . files)
  166. (or-map (lambda (file)
  167. (find-documentation-in-file name file))
  168. (cond ((null? files) documentation-files)
  169. (else files))))
  170. ;; helper until the procedure documentation property is cleaned up
  171. (define (proc-doc proc)
  172. (or (procedure-documentation proc)
  173. (procedure-property proc 'documentation)))
  174. (define (object-documentation object)
  175. "Return the docstring for OBJECT.
  176. OBJECT can be a procedure, macro or any object that has its
  177. `documentation' property set."
  178. (or (and (procedure? object)
  179. (proc-doc object))
  180. (and (defmacro? object)
  181. (proc-doc (defmacro-transformer object)))
  182. (and (macro? object)
  183. (let ((transformer (macro-transformer object)))
  184. (and transformer
  185. (proc-doc transformer))))
  186. (object-property object 'documentation)
  187. (and (procedure? object)
  188. (not (closure? object))
  189. (procedure-name object)
  190. (let ((docstring (search-documentation-files
  191. (procedure-name object))))
  192. (if docstring
  193. (set-procedure-property! object 'documentation docstring))
  194. docstring))))
  195. ;;; documentation.scm ends here