hyper-literate/scribble-lib/scribble/private/manual-bind.rkt
Matthew Flatt aca15dcc85 Fix use of namespace-require that can create conflicts
The current expader's `namespace-require` has a bug that prevents
it from reporting a conclict when `(for-label <lib>)` creates
a conflict due to different provided bindings of the same name
at different phases from <lib>. Avoid depending on that bug.
2016-08-01 13:23:24 -06:00

274 lines
11 KiB
Racket

#lang scheme/base
(require racket/string
racket/format
"../struct.rkt"
"../scheme.rkt"
"../search.rkt"
"../basic.rkt"
"../manual-struct.rkt"
(only-in "../core.rkt" make-style)
"../html-properties.rkt"
"manual-ex.rkt"
racket/contract/base
(for-syntax scheme/base)
(for-label scheme/base
scheme/class))
(provide definition-site
libs->taglet
annote-exporting-library
with-exporting-libraries
id-to-target-maker
id-to-form-target-maker
*sig-elem
(struct-out sig)
;; public:
; XXX unknown contract
make-binding-redirect-elements
sigelem)
(provide/contract
; XXX What is return type?
[defidentifier ((identifier?) (#:form? boolean? #:index? boolean? #:show-libs? boolean?) . ->* . any/c)])
(define (gen-absolute-tag)
`(abs ,(make-generated-tag)))
(define-struct sig (id))
(define-syntax-rule (sigelem sig elem)
(*sig-elem (quote-syntax sig) 'elem))
(define (*sig-elem sig elem #:defn? [defn? #f])
(let ([s (to-element/no-color elem)])
(make-delayed-element
(lambda (renderer sec ri)
(let* ([tag (find-scheme-tag sec ri sig #f)]
[taglet (and tag (append (cadr tag) (list elem)))]
[vtag (and tag `(sig-val ,taglet))]
[stag (and tag `(sig-form ,taglet))]
[sd (and stag (resolve-get/tentative sec ri stag))])
(make-element
symbol-color
(list
(cond [sd (make-link-element (if defn? syntax-def-color syntax-link-color) (list s) stag)]
[vtag (make-link-element (if defn? value-def-color value-link-color) (list s) vtag)]
[else s])))))
(lambda () s)
(lambda () s))))
(define hovers (make-weak-hasheq))
(define (intern-hover-style text)
(let ([text (datum-intern-literal text)])
(or (hash-ref hovers text #f)
(let ([s (make-style #f (list (make-hover-property text)))])
(hash-set! hovers text s)
s))))
(define (annote-exporting-library e)
(make-delayed-element
(lambda (render p ri)
(let ([from (resolve-get/tentative p ri '(exporting-libraries #f))])
(if (and from (pair? from))
(make-element
(intern-hover-style
(string-append
"Provided from: "
(string-join (map ~s from) ", ")
(let ([from-pkgs (resolve-get/tentative p ri '(exporting-packages #f))])
(if (and from-pkgs (pair? from-pkgs))
(string-append
" | Package: "
(string-join (map ~a from-pkgs) ", "))
""))))
e)
e)))
(lambda () e)
(lambda () e)))
(define (get-exporting-libraries render p ri)
(resolve-get/tentative p ri '(exporting-libraries #f)))
(define (with-exporting-libraries proc)
(make-delayed-index-desc
(lambda (render part ri)
(proc (or (get-exporting-libraries render part ri) null)))))
(define (definition-site name stx-id form?)
(let ([sig (current-signature)])
(define (gen defn?)
(if sig
(*sig-elem #:defn? defn? (sig-id sig) name)
((if defn? annote-exporting-library values)
(to-element #:defn? defn? (make-just-context name stx-id)))))
(values (gen #t) (gen #f))))
(define checkers (make-hash))
(define (libs->taglet id libs source-libs)
(let ([lib
(or (ormap (lambda (lib)
(let ([checker
(hash-ref
checkers lib
(lambda ()
(let ([ns-id
(let ([ns (make-base-empty-namespace)])
(parameterize ([current-namespace ns])
;; A `(namespace-require `(for-label ,lib))` can
;; fail if `lib` provides different bindings of the
;; same name at different phases. We can require phases
;; 1 and 0 separately, in which case the phase-0
;; binding shadows the phase-1 one in that case.
;; This strategy only works for documenting bindings
;; at phases 0 and 1, though.
(namespace-require `(just-meta 1 (for-label ,lib)))
(namespace-require `(just-meta 0 (for-label ,lib)))
(namespace-syntax-introduce (datum->syntax #f 'x))))])
(let ([checker
(lambda (id)
(free-label-identifier=?
(datum->syntax ns-id (syntax-e id))
id))])
(hash-set! checkers lib checker)
checker))))])
(and (checker id) lib)))
(or source-libs null))
(and (pair? libs) (car libs)))])
(and lib (module-path-index->taglet
(module-path-index-join lib #f)))))
(define (id-to-target-maker id dep?)
(*id-to-target-maker 'def id dep?))
(define (id-to-form-target-maker id dep?)
(*id-to-target-maker 'form id dep?))
(define (*id-to-target-maker sym id dep?)
(let ([sig (current-signature)])
(lambda (content mk)
(make-part-relative-element
(lambda (ci)
(let ([e (ormap (lambda (p)
(ormap (lambda (e)
(and (exporting-libraries? e) e))
(part-to-collect p)))
(collect-info-parents ci))])
(unless e
;; Call raise-syntax-error to capture error message:
(with-handlers ([exn:fail:syntax?
(lambda (exn)
(eprintf "~a\n" (exn-message exn)))])
(raise-syntax-error
'WARNING
"no declared exporting libraries for definition" id)))
(if e
(let* ([lib-taglet (libs->taglet
(if sig (sig-id sig) id)
(exporting-libraries-libs e)
(exporting-libraries-source-libs e))]
[tag (intern-taglet
(list (if sig
(case sym
[(def) 'sig-val]
[(form) 'sig-def])
sym)
`(,lib-taglet
,@(if sig (list (syntax-e (sig-id sig))) null)
,(syntax-e id))))])
(if (or sig (not dep?))
(mk tag)
(make-dep (list lib-taglet (syntax-e id))
(mk tag))))
content)))
(lambda () content)
(lambda () content)))))
(define (defidentifier id
#:form? [form? #f]
#:index? [index? #t]
#:show-libs? [show-libs? #t])
;; This function could have more optional argument to select
;; whether to index the id, include a toc link, etc.
(let ([dep? #t])
(let ([maker (if form?
(id-to-form-target-maker id dep?)
(id-to-target-maker id dep?))])
(define-values (elem elem-ref)
(if show-libs?
(definition-site (syntax-e id) id form?)
(values (to-element id #:defn? #t)
(to-element id))))
(if maker
(maker elem
(lambda (tag)
(let ([elem
(if index?
(make-index-element
#f (list elem) tag
(list (datum-intern-literal (symbol->string (syntax-e id))))
(list elem)
(and show-libs?
(with-exporting-libraries
(lambda (libs)
(make-exported-index-desc (syntax-e id)
libs)))))
elem)])
(make-target-element #f (list elem) tag))))
elem))))
(define (make-binding-redirect-elements mod-path redirects)
(let ([taglet (module-path-index->taglet
(module-path-index-join mod-path #f))])
(make-element
#f
(map
(lambda (redirect)
(let ([id (car redirect)]
[form? (cadr redirect)]
[path (caddr redirect)]
[anchor (cadddr redirect)])
(let ([make-one
(lambda (kind)
(make-redirect-target-element
#f
null
(intern-taglet (list kind (list taglet id)))
path
anchor))])
(make-element
#f
(list (make-one (if form? 'form 'def))
(make-dep (list taglet id) null)
(let ([str (datum-intern-literal (symbol->string id))])
(make-index-element #f
null
(intern-taglet
(list (if form? 'form 'def)
(list taglet id)))
(list str)
(list
(make-element
symbol-color
(list
(make-element
(if form?
syntax-link-color
value-link-color)
(list str)))))
((if form?
make-form-index-desc
make-procedure-index-desc)
id
(list mod-path)))))))))
redirects))))
(define (make-dep t content)
(make-collect-element
#f
content
(lambda (ci)
(collect-put! ci
(intern-taglet (list 'dep t))
#t))))