documentation.scm 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. ;;; documentation.scm --- Run-time documentation facility
  2. ;;; Copyright (C) 2000-2003,2006,2009,2010,2024 Free Software Foundation, Inc.
  3. ;;;
  4. ;;; This library is free software: you can redistribute it and/or modify
  5. ;;; it under the terms of the GNU Lesser General Public License as
  6. ;;; published by the Free Software Foundation, either version 3 of the
  7. ;;; License, or (at your option) any later version.
  8. ;;;
  9. ;;; This library is distributed in the hope that it will be useful, but
  10. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;; Lesser General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU Lesser General Public
  15. ;;; License along with this program. If not, see
  16. ;;; <http://www.gnu.org/licenses/>.
  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. #:use-module (ice-9 regex)
  80. #:use-module (ice-9 match)
  81. #:export (file-commentary
  82. documentation-files search-documentation-files
  83. object-documentation))
  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. (define (parse-path var)
  136. (match (getenv var)
  137. (#f #f)
  138. ;; Ignore e.g. "export GUILE_SYSTEM_EXTENSIONS_PATH=".
  139. ("" '())
  140. (val (string-split val #\:))))
  141. ;;
  142. ;; documentation-files is the list of places to look for documentation
  143. ;;
  144. (define documentation-files
  145. (map (lambda (vicinity)
  146. (in-vicinity vicinity "guile-procedures.txt"))
  147. (or (parse-path "GUILE_DOCSTRINGS_PATH")
  148. (list (%library-dir) (%package-data-dir) (%site-dir)))))
  149. (define entry-delimiter "\f")
  150. (define (find-documentation-in-file name file)
  151. (and (file-exists? file)
  152. (call-with-input-file file
  153. (lambda (port)
  154. (let ((name (symbol->string name)))
  155. (let ((len (string-length name)))
  156. (read-delimited entry-delimiter port) ;skip to first entry
  157. (let loop ((entry (read-delimited entry-delimiter port)))
  158. (cond ((eof-object? entry) #f)
  159. ;; match?
  160. ((and ;; large enough?
  161. (>= (string-length entry) len)
  162. ;; matching name?
  163. (string=? (substring entry 0 len) name)
  164. ;; terminated?
  165. (memq (string-ref entry len) '(#\newline)))
  166. ;; cut away name tag and extra surrounding newlines
  167. (substring entry (+ len 2) (- (string-length entry) 2)))
  168. (else (loop (read-delimited entry-delimiter port)))))))))))
  169. (define (search-documentation-files name . files)
  170. (or-map (lambda (file)
  171. (find-documentation-in-file name file))
  172. (cond ((null? files) documentation-files)
  173. (else files))))
  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. (procedure-documentation object))
  180. (object-property object 'documentation)
  181. (and (macro? object)
  182. (object-documentation (macro-transformer object)))
  183. (and (procedure? object)
  184. (procedure-name object)
  185. (let ((docstring (search-documentation-files
  186. (procedure-name object))))
  187. (if docstring
  188. (set-procedure-property! object 'documentation docstring))
  189. docstring))))
  190. ;;; documentation.scm ends here