diff --git a/pkgs/racket-pkgs/racket-doc/pkg/scribblings/implementation.scrbl b/pkgs/racket-pkgs/racket-doc/pkg/scribblings/implementation.scrbl index 4cbd5684e6..c56b1a781a 100644 --- a/pkgs/racket-pkgs/racket-doc/pkg/scribblings/implementation.scrbl +++ b/pkgs/racket-pkgs/racket-doc/pkg/scribblings/implementation.scrbl @@ -6,6 +6,7 @@ setup/main-collects setup/path-to-relative setup/dirs + (only-in scribble/manual secref) (only-in scribble/base-render render%) (only-in scribble/html-render render-mixin) (only-in scribble/xref load-xref make-data+root))) @@ -176,10 +177,15 @@ are not available, the query link is left unmodified to go through a server. The @filepath{local-redirect.js} and @filepath{local-user-redirect.js} -files embed a copy of the cross-reference database, where the copy is -specialized and compacted. These JavaScript files are generated as -part of the special @filepath{local-redirect} document that is -implemented by the @pkgname{racket-index} package. +files map documentation-directory names to specific paths. Most query +references contain a documentation-directory name and a relative path +within the directory, in which case the mapping from directory names +to paths is sufficient. Indirect links, such as those created by +@racket[(seclink #:indirect? #t ...)], embed a cross-reference key, and +so @filepath{local-redirect.js} and @filepath{local-user-redirect.js} +must also embed a part of the cross-reference database. The JavaScript +files are generated as part of the special @filepath{local-redirect} +document that is implemented by the @pkgname{racket-index} package. The indirection through @filepath{local-redirect.js} and @filepath{local-user-redirect.js} reduces the problem of relative @@ -187,7 +193,7 @@ links to the problem of referencing those two files. They are referenced as absolute paths in a user-specific document build. To create a @tech{built package} or @tech{binary package} that includes documentation, each @filepath{.html} file must be modified to remove -the absolute path, and then each @filepath{.html} file must be +the absolute paths, and then each @filepath{.html} file must be modified again on installation to put the target installation's paths in path. diff --git a/pkgs/racket-pkgs/racket-index/scribblings/main/config.rkt b/pkgs/racket-pkgs/racket-index/scribblings/main/config.rkt index 14b2a443b6..267561ac60 100644 --- a/pkgs/racket-pkgs/racket-index/scribblings/main/config.rkt +++ b/pkgs/racket-pkgs/racket-index/scribblings/main/config.rkt @@ -1,4 +1,4 @@ -#lang scheme/base +#lang racket/base (provide (all-defined-out)) diff --git a/pkgs/racket-pkgs/racket-index/scribblings/main/private/local-redirect.rkt b/pkgs/racket-pkgs/racket-index/scribblings/main/private/local-redirect.rkt index 9d8b53825a..b3a5813a1f 100644 --- a/pkgs/racket-pkgs/racket-index/scribblings/main/private/local-redirect.rkt +++ b/pkgs/racket-pkgs/racket-index/scribblings/main/private/local-redirect.rkt @@ -3,9 +3,11 @@ racket/serialize racket/class racket/match + racket/set setup/dirs net/url scribble/html-properties + setup/xref "index-scope.rkt") (provide make-local-redirect) @@ -13,17 +15,17 @@ (define (rewrite-code user?) (define prefix (if user? "user_" "")) @string-append|{ - function |@|prefix|bsearch(str, start, end) { + function |@|prefix|bsearch(str, a, start, end) { if (start >= end) return false; else { var mid = Math.floor((start + end) / 2); - if (|@|prefix|link_targets[mid][0] == str) + if (a[mid][0] == str) return mid; - else if (|@|prefix|link_targets[mid][0] < str) - return |@|prefix|bsearch(str, mid+1, end); + else if (a[mid][0] < str) + return |@|prefix|bsearch(str, a, mid+1, end); else - return |@|prefix|bsearch(str, start, mid); + return |@|prefix|bsearch(str, a, start, mid); } } @@ -33,9 +35,26 @@ var elements = document.getElementsByClassName("Sq"); for (var i = 0; i < elements.length; i++) { var elem = elements[i]; - var n = elem.href.match(/tag=[^&]*/); - if (n) { - var pos = |@|prefix|bsearch(decodeURIComponent(n[0].substring(4)), + var doc = elem.href.match(/doc=[^&]*/); + var rel = elem.href.match(/rel=[^&]*/); + var tag = elem.href.match(/tag=[^&]*/); + if (doc && rel) { + var pos = |@|prefix|bsearch(decodeURIComponent(doc[0].substring(4)), + |@|prefix|link_dirs, + 0, + |@|prefix|link_dirs.length); + if (pos) { + var p = |@|prefix|link_dirs[pos][1]; + if (|@|prefix|link_target_prefix) { + p = |@|prefix|link_target_prefix + p; + } + elem.href = p + "/" + decodeURIComponent(rel[0].substring(4)); + tag = false; + } + } + if (tag) { + var pos = |@|prefix|bsearch(decodeURIComponent(tag[0].substring(4)), + |@|prefix|link_targets, 0, |@|prefix|link_targets.length); if (pos) { @@ -43,7 +62,6 @@ if (|@|prefix|link_target_prefix) { p = |@|prefix|link_target_prefix + p; } - elem.href = p; } } } @@ -82,11 +100,18 @@ #f null (lambda (renderer p ri) + (define doc-dirs (get-rendered-doc-directories (not user?) user?)) (define keys (if (and main-at-user? (not user?)) ;; If there's no installation-scope "doc", then ;; the "main" redirection table is useless. null - (resolve-get-keys #f ri (lambda (v) #t)))) + (resolve-get-keys #f ri + (lambda (v) + ;; Support key-based indirect only on sections + ;; and module names: + (define t (car v)) + (or (eq? t 'part) + (eq? t 'mod-path)))))) (define (target? v) (and (vector? v) (= 5 (vector-length v)))) (define dest-dir (send renderer get-dest-directory #t)) (define (make-dest user?) @@ -124,13 +149,26 @@ (path->directory-path (build-path (find-doc-dir) "local-redirect"))))) (newline o)) + (fprintf o "var ~alink_dirs = [" (if user? "user_" "")) + (define (extract-name e) + (let-values ([(base name dir?) (split-path e)]) + (path->string name))) + (for ([e (in-list (sort doc-dirs stringstring (path->url e)) + (format "../~a" name)))) + (fprintf o "];\n\n") (fprintf o "var ~alink_targets = [" (if user? "user_" "")) (for ([e (in-list db)] [i (in-naturals)]) (fprintf o (if (zero? i) "\n" ",\n")) (fprintf o " [~s, ~s]" (car e) (cadr e))) (fprintf o "];\n\n") - (fprintf o (rewrite-code user?)))) + (fprintf o (rewrite-code user?)) + (newline o))) (unless (file-exists? alt-dest) ;; make empty alternate file; in `user?` mode, this ;; file will get used only when "racket-index" is not diff --git a/pkgs/racket-pkgs/racket-index/setup/xref.rkt b/pkgs/racket-pkgs/racket-index/setup/xref.rkt index 0d20cd6780..4012d4a879 100644 --- a/pkgs/racket-pkgs/racket-index/setup/xref.rkt +++ b/pkgs/racket-pkgs/racket-index/setup/xref.rkt @@ -10,11 +10,16 @@ setup/doc-db) (provide load-collections-xref - make-collections-xref) + make-collections-xref + get-rendered-doc-directories) (define cached-xref #f) -(define (get-dests tag no-user? no-main?) +(define (get-rendered-doc-directories no-user? no-main?) + (append (get-dests 'scribblings no-user? no-main? #f) + (get-dests 'rendered-scribblings no-user? no-main? #f))) + +(define (get-dests tag no-user? no-main? sxrefs?) (define main-dirs (for/hash ([k (in-list (find-relevant-directories (list tag) 'no-user))]) (values k #t))) @@ -42,10 +47,12 @@ (if no-user? 'never 'false-if-missing) #:main? (not no-main?))]) (if d - (for*/list ([i (in-range (add1 out-count))] - [p (in-value (build-path d (format "out~a.sxref" i)))] - #:when (file-exists? p)) - p) + (if sxrefs? + (for*/list ([i (in-range (add1 out-count))] + [p (in-value (build-path d (format "out~a.sxref" i)))] + #:when (file-exists? p)) + p) + (list d)) null)) null))))) @@ -62,11 +69,19 @@ (exn-message exn) (format "~e" exn)))) #f)]) - (make-data+root + (make-data+root+doc-id ;; data to deserialize: (cadr (call-with-input-file* dest fasl->s-exp)) ;; provide a root for deserialization: - (path-only dest)))))) + (path-only dest) + ;; Use the destination directory's name as an identifier, + ;; which allows a faster and more compact indirection + ;; for installation-scaoped documentation: + (let-values ([(base name dir?) (split-path dest)]) + (and (path? base) + (let-values ([(base name dir?) (split-path base)]) + (and (path? name) + (path->string name)))))))))) (define (make-key->source db-path no-user? no-main? quiet-fail? register-shutdown!) (define main-db (and (not no-main?) @@ -132,8 +147,8 @@ (define (get-reader-thunks no-user? no-main? quiet-fail? done-ht) (map (dest->source done-ht quiet-fail?) - (filter values (append (get-dests 'scribblings no-user? no-main?) - (get-dests 'rendered-scribblings no-user? no-main?))))) + (filter values (append (get-dests 'scribblings no-user? no-main? #t) + (get-dests 'rendered-scribblings no-user? no-main? #t))))) (define (load-collections-xref [report-loading void]) (or cached-xref diff --git a/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/core.scrbl b/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/core.scrbl index c7fc5d0efd..96d8f34dc5 100644 --- a/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/core.scrbl +++ b/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/core.scrbl @@ -1354,6 +1354,16 @@ whether the resulting information originated from an external source (i.e., a different document).} +@defproc[(resolve-get/ext-id [p (or/c part? #f)] [ri resolve-info?] [key info-key?]) + (values any/c (or/c boolean? string?))]{ + +Like @racket[render-get/ext?], but the second result can be a string +to indicate the source document's identification as established via +@racket[load-xref] and a @racket[#:doc-id] argument. + +@history[#:added "1.1"]} + + @defproc[(resolve-search [dep-key any/c] [p (or/c part? #f)] [ri resolve-info?] [key info-key?]) void?]{ diff --git a/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/renderer.scrbl b/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/renderer.scrbl index 3822b2901c..2d610f1a14 100644 --- a/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/renderer.scrbl +++ b/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/renderer.scrbl @@ -320,10 +320,16 @@ Specializes a @racket[render<%>] class for generating HTML output. @defmethod[(set-external-tag-path [url string?]) void?]{ -Configures the renderer to redirect links to external via -@racket[url], adding a @racket[tag] query element to the end of the +Configures the renderer to redirect links to external documents via +@racket[url], adding a @tt{tag} query element to the end of the URL that contains the Base64-encoded, @racket[print]ed, serialized -original tag (in the sense of @racket[link-element]) for the link.} +original tag (in the sense of @racket[link-element]) for the link. + +If the link is based on a cross-reference entry that has a +document-identifying string (see @racket[load-xref] and its +@racket[#:doc-id] argument), the document identifier is added as a +@tt{doc} query element, and a path to the target within the +document is added as a @tt{rel} query element.} @defmethod[(set-external-root-url [url string?]) void?]{ diff --git a/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/xref.scrbl b/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/xref.scrbl index 3b0f99cc72..06a55d188b 100644 --- a/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/xref.scrbl +++ b/pkgs/scribble-pkgs/scribble-doc/scribblings/scribble/xref.scrbl @@ -25,7 +25,8 @@ by @racket[load-xref], @racket[#f] otherwise.} (lambda (_tag) #f)] [#:render% using-render% (implementation?/c render<%>) (render-mixin render%)] - [#:root root-path (or/c path-string? false/c) #f]) + [#:root root-path (or/c path-string? false/c) #f] + [#:doc-id doc-id-str (or/c path-string? false/c) #f]) xref?]{ Creates a cross-reference record given a list of functions, @@ -38,9 +39,11 @@ serialize-info]. The result of @racket[_source] can optionally be another function, which is in turn responsible for returning a list of @racket[_info]s. Finally, each @racket[_info] can be either serialized information, a @racket[#f] to be ignored, or a value produced by -@racket[make-data+root] from which @racket[_data] part is used as -serialized information and the @racket[_root] part overrides -@racket[root-path] for deserialization. +@racket[make-data+root] or @racket[make-data+root+doc-id], from which +@racket[_data] part is used as serialized information, the +@racket[_root] part overrides @racket[root-path] for deserialization, +and the @racket[_doc-id] part (if any) overrides +@racket[doc-id-string] to identify the source document. The @racket[demand-source] function can effectively add a new source to @racket[sources] in response to a search for information on the @@ -60,8 +63,16 @@ but a @racket[make-data+root] result for any @racket[_info] supplies an alternate path for deserialization of the @racket[_info]'s @racket[_data]. +If @racket[doc-id-str] is not @racket[#f], it identifies each +cross-reference entry as originating from @racket[doc-id-str]. This +identification is used when a rendering link to the cross-reference +entry as an external query; see the @racket[set-external-tag-path] +method of @racket[render-mixin]. + Use @racket[load-collections-xref] from @racketmodname[setup/xref] to -get all cross-reference information for installed documentation.} +get all cross-reference information for installed documentation. + +@history[#:changed "1.1" @elem{Added the @racket[#:doc-id] argument.}]} @defproc[(xref-binding->definition-tag [xref xref?] @@ -213,3 +224,13 @@ the destination for the index link into the main document.} A value constructed by @racket[make-data+root] can be returned by a source procedure for @racket[load-xref] to specify a path used for deserialization.} + +@deftogether[( +@defproc[(data+root+doc-id? [v any/c]) boolean?] +@defproc[(make-data+root+doc-id [data any/c] [root (or/c #f path-string?)] [doc-id string?]) data+root+doc-id?] +)]{ + +Extends @racket[make-data+root+doc-id] to support an +document-identifying string (see @racket[load-xref]). + +@history[#:added "1.1"]} diff --git a/pkgs/scribble-pkgs/scribble-lib/scribble/base-render.rkt b/pkgs/scribble-pkgs/scribble-lib/scribble/base-render.rkt index 00f15c0a85..2f339b7339 100644 --- a/pkgs/scribble-pkgs/scribble-lib/scribble/base-render.rkt +++ b/pkgs/scribble-pkgs/scribble-lib/scribble/base-render.rkt @@ -353,13 +353,13 @@ (when rp (set-mobile-root-path! root rp)))))) - (define/public (deserialize-info v ci #:root [root-path #f]) + (define/public (deserialize-info v ci #:root [root-path #f] #:doc-id [doc-id #f]) (let ([root+ht (deserialize v)] [in-ht (collect-info-ext-ht ci)]) (when root-path (set-mobile-root-path! (car root+ht) root-path)) (for ([(k v) (cdr root+ht)]) - (hash-set! in-ht k v)))) + (hash-set! in-ht k (if doc-id (known-doc v doc-id) v))))) (define/public (get-defined ci) (hash-map (collect-info-ht ci) (lambda (k v) k))) diff --git a/pkgs/scribble-pkgs/scribble-lib/scribble/base.rkt b/pkgs/scribble-pkgs/scribble-lib/scribble/base.rkt index 639f8a6ebb..61fbb4e6cc 100644 --- a/pkgs/scribble-pkgs/scribble-lib/scribble/base.rkt +++ b/pkgs/scribble-pkgs/scribble-lib/scribble/base.rkt @@ -729,7 +729,8 @@ (collect-info-ext-ht ci)))) (lambda (k v) (when (and (pair? k) (eq? 'index-entry (car k))) - (set! l (cons (cons (cadr k) v) l))))) + (let ([v (if (known-doc? v) (known-doc-v v) v)]) + (set! l (cons (cons (cadr k) v) l)))))) (sort l entry . any)] [resolve-get/tentative ((or/c part? false/c) resolve-info? info-key? . -> . any)] [resolve-get/ext? ((or/c part? false/c) resolve-info? info-key? . -> . any)] + [resolve-get/ext-id ((or/c part? false/c) resolve-info? info-key? . -> . any)] [resolve-search (any/c (or/c part? false/c) resolve-info? info-key? . -> . any)] [resolve-get-keys ((or/c part? false/c) resolve-info? (info-key? . -> . any/c) . -> . any/c)]) diff --git a/pkgs/scribble-pkgs/scribble-lib/scribble/html-render.rkt b/pkgs/scribble-pkgs/scribble-lib/scribble/html-render.rkt index b779cb40fe..d3ebebd3aa 100644 --- a/pkgs/scribble-pkgs/scribble-lib/scribble/html-render.rkt +++ b/pkgs/scribble-pkgs/scribble-lib/scribble/html-render.rkt @@ -508,6 +508,19 @@ (anchor-name (dest-anchor dest)))))) "???")) + (define/private (dest->url-in-doc dest) + (and dest + (not (dest-redirect dest)) + (format "~a~a~a" + (let-values ([(base name dir?) (split-path + (relative->path (dest-path dest)))]) + name) + (if (dest-page? dest) "" "#") + (if (dest-page? dest) + "" + (uri-unreserved-encode + (anchor-name (dest-anchor dest))))))) + (define/public (render-toc-view d ri) (define has-sub-parts? (pair? (part-parts d))) @@ -1239,15 +1252,15 @@ [(and (link-element? e) (not (current-no-links))) (parameterize ([current-no-links #t]) (define indirect-link? (link-element-indirect? e)) - (let-values ([(dest ext?) + (let-values ([(dest ext-id) (if (and indirect-link? external-tag-path) (values #f #f) - (resolve-get/ext? part ri (link-element-tag e)))]) + (resolve-get/ext-id part ri (link-element-tag e)))]) (if (or indirect-link? dest) - `((a [(href + `((a ([href ,(cond - [(and ext? external-root-url + [(and ext-id external-root-url (let ([rel (find-relative-path (find-doc-dir) (relative->path (dest-path dest)))]) @@ -1270,7 +1283,7 @@ (and (not (dest-page? dest)) (anchor-name (dest-anchor dest)))])))] [(or indirect-link? - (and ext? external-tag-path)) + (and ext-id external-tag-path)) ;; Redirected to search: (url->string* (let ([u (string->url (or external-tag-path @@ -1279,16 +1292,20 @@ url u [query - (cons (cons 'tag (tag->query-string (link-element-tag e))) - (url-query u))])))] + (if (string? ext-id) + (list* (cons 'doc ext-id) + (cons 'rel (or (dest->url-in-doc dest) "???")) + (url-query u)) + (cons (cons 'tag (tag->query-string (link-element-tag e))) + (url-query u)))])))] [else ;; Normal link: - (dest->url dest)])) + (dest->url dest)])] ,@(attribs (if (or indirect-link? - (and ext? external-tag-path)) - '((class "Sq")) + (and ext-id external-tag-path)) + '([class "Sq"]) null)) - [data-pltdoc "x"]] + [data-pltdoc "x"]) ,@(if (empty-content? (element-content e)) (render-content (strip-aux (dest-title dest)) part ri) (render-content (element-content e) part ri)))) diff --git a/pkgs/scribble-pkgs/scribble-lib/scribble/xref.rkt b/pkgs/scribble-pkgs/scribble-lib/scribble/xref.rkt index 75c1dfe976..b0ae6fa8f1 100644 --- a/pkgs/scribble-pkgs/scribble-lib/scribble/xref.rkt +++ b/pkgs/scribble-pkgs/scribble-lib/scribble/xref.rkt @@ -1,6 +1,7 @@ #lang scheme/base (require scribble/struct + (only-in scribble/core known-doc? known-doc-v) scribble/base-render scribble/search (prefix-in html: scribble/html-render) @@ -17,7 +18,9 @@ xref-transfer-info (struct-out entry) make-data+root - data+root?) + data+root? + make-data+root+doc-id + data+root+doc-id?) (define-struct entry (words ; list of strings: main term, sub-term, etc. @@ -26,6 +29,7 @@ desc)) ; further info that depends on the kind of index entry (define-struct data+root (data root)) +(define-struct (data+root+doc-id data+root) (doc-id)) ;; Private: (define-struct xrefs (renderer ri)) @@ -40,7 +44,8 @@ (define (load-xref sources #:demand-source [demand-source (lambda (key) #f)] #:render% [render% (html:render-mixin render%)] - #:root [root-path #f]) + #:root [root-path #f] + #:doc-id [doc-id-str #f]) (let* ([renderer (new render% [dest-dir (find-system-path 'temp-dir)])] [fp (send renderer traverse null null)] [load-source (lambda (src ci) @@ -51,7 +56,11 @@ (when v (define data (if (data+root? v) (data+root-data v) v)) (define root (if (data+root? v) (data+root-root v) root-path)) - (send renderer deserialize-info data ci #:root root))))))] + (define doc-id (or (and (data+root+doc-id? v) (data+root+doc-id-doc-id v)) + doc-id-str)) + (send renderer deserialize-info data ci + #:root root + #:doc-id doc-id))))))] [ci (send renderer collect null null fp (lambda (key ci) (define src (demand-source key)) @@ -73,7 +82,10 @@ #:when (and (pair? k) (eq? (car k) 'index-entry))) - (make-entry (car v) (cadr v) (cadr k) (caddr v)))) + (let ([v (if (known-doc? v) + (known-doc-v v) + v)]) + (make-entry (car v) (cadr v) (cadr k) (caddr v))))) ;; dest-file can be #f, which will make it return a string holding the ;; resulting html @@ -151,8 +163,11 @@ (collect-info-ext-ht (resolve-info-ci (xrefs-ri xrefs))) `(index-entry ,tag) #f)]) - (cond [v (make-entry (car v) (cadr v) (cadr tag) (caddr v))] - [(and (pair? tag) (eq? 'form (car tag))) - ;; Try again with 'def: - (xref-tag->index-entry xrefs (cons 'def (cdr tag)))] - [else #f]))) + (let ([v (if (known-doc? v) + (known-doc-v v) + v)]) + (cond [v (make-entry (car v) (cadr v) (cadr tag) (caddr v))] + [(and (pair? tag) (eq? 'form (car tag))) + ;; Try again with 'def: + (xref-tag->index-entry xrefs (cons 'def (cdr tag)))] + [else #f]))))