Skip to content

Commit fda47f7

Browse files
committed
add traverse-part and cond-part
Enable selection of document parts (not just blocks and elements) at traverse time. This adition requires a small extra pass between the traverse pass and collects pass as reflected by the `traversed-parts` method of a renderer. The extra pass is only needed (i.e., things work if it is omitted) if `traverse-part` is not used.
1 parent da31ca8 commit fda47f7

15 files changed

Lines changed: 209 additions & 41 deletions

File tree

scribble-doc/scribblings/scribble/core.scrbl

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ A document is processed in four passes:
3131
document order so that information from one part of a document
3232
can be communicated to other parts of the same document. The
3333
information is transmitted through a symbol-keyed mapping that
34-
can be inspected and extended by @racket[traverse-element]s and
35-
@racket[traverse-block]s in the document. The @tech{traverse
34+
can be inspected and extended by @racket[traverse-element]s,
35+
@racket[traverse-block]s, and @racket[traverse-part]s in the
36+
document. The @tech{traverse
3637
pass} iterates the traversal until it obtains a fixed point
3738
(i.e., the mapping from one iteration is unchanged from the
3839
previous iteration).}
@@ -52,11 +53,12 @@ A document is processed in four passes:
5253
None of the passes mutate the document representation. Instead, the
5354
@tech{traverse pass}, @tech{collect pass}, and @tech{resolve pass}
5455
accumulate information in a side hash table, @racket[collect-info]
55-
table, and @racket[resolve-info] table. The @tech{collect pass} and
56-
@tech{resolve pass} are effectively specialized version of
57-
@tech{traverse pass} that work across separately built documents.
56+
table, and @racket[resolve-info] table, except that
57+
@racket[traverse-part]s are replaced just after the traverse pass
58+
by rebuilding enclosing @racket[part] structures. The collect pass and
59+
resolve pass are effectively specialized version of
60+
traverse pass that work across separately built documents.
5861

59-
6062
@; ------------------------------------------------------------------------
6163

6264
@section[#:tag "parts"]{Parts, Flows, Blocks, and Paragraphs}
@@ -386,7 +388,7 @@ value that has been accumulated from enclosing parts.
386388
[style style?]
387389
[to-collect list?]
388390
[blocks (listof block?)]
389-
[parts (listof part?)])]{
391+
[parts (listof (or/c part? traverse-part?))])]{
390392

391393
The @racket[tag-prefix] field determines the optional @techlink{tag
392394
prefix} for the part and/or @techlink{part context} accumulation. When
@@ -611,7 +613,9 @@ passes (i.e., it doesn't directly contribute to the output).
611613
The @racket[blocks] field contains the part's initial flow (before
612614
sub-parts).
613615

614-
The @racket[parts] field contains sub-parts.
616+
The @racket[parts] field contains sub-parts. A @racket[traverse-part]
617+
within @racket[parts] is converted to a replacement list of
618+
@racket[part]s just after the @tech{traverse pass}.
615619

616620
@history[#:changed "1.25" @elem{Added @racket['no-index] support.}
617621
#:changed "1.26" @elem{Added @racket[link-render-style] support.}
@@ -621,7 +625,27 @@ The @racket[parts] field contains sub-parts.
621625
#:changed "1.57" @elem{Added @racket['no-header-controls] support.}
622626
#:changed "1.59" @elem{Added @racket['no-navigation],
623627
@racket['family-navigation], and
624-
@racket['show-language-family] support.}]}
628+
@racket['show-language-family] support.}
629+
#:changed "1.67" @elem{Added @racket[traverse-part?] as a possible
630+
contract on an element in @racket[parts] list.}]}
631+
632+
633+
@defstruct[traverse-part ([traverse part-traverse-procedure/c])]{
634+
635+
Produces a replacement list of parts (possibly empty) during the
636+
@tech{traverse pass}, eventually.
637+
638+
The @racket[traverse] procedure is called with @racket[_get] and
639+
@racket[_set] procedures to get and set symbol-keyed information; the
640+
@racket[traverse] procedure should return either a list of
641+
@tech{parts} (which effectively takes the @racket[traverse-part]'s
642+
place) or a procedure like @racket[traverse] to be called in the next
643+
iteration of the @tech{traverse pass}.
644+
645+
See @racket[traverse-block] for more information that applies to
646+
@racket[traverse-part], too.
647+
648+
@history[#:added "1.67"]}
625649

626650

627651
@defstruct[paragraph ([style style?] [content content?])]{
@@ -1809,6 +1833,18 @@ Produces the block that replaces @racket[b].}
18091833
Produces the content that replaces @racket[e].}
18101834

18111835

1836+
@defthing[part-traverse-procedure/c contract?]{
1837+
1838+
Defined as
1839+
1840+
@racketblock[
1841+
(recursive-contract
1842+
((symbol? any/c . -> . any/c)
1843+
(symbol? any/c . -> . any)
1844+
. -> . (or/c part-traverse-procedure/c
1845+
(listof part?))))
1846+
]}
1847+
18121848
@defthing[block-traverse-procedure/c contract?]{
18131849

18141850
Defined as

scribble-doc/scribblings/scribble/decode.scrbl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ by functions like @racket[decode-flow].}
7272

7373
Returns @racket[#t] if @racket[v] is a @deftech{pre-part} value: a
7474
string or other non-list @tech{content}, a @tech{block}, a
75-
@racket[part], a @racket[title-decl], a @racket[part-start], a
75+
@racket[part], a @racket[title-decl], a @racket[part-start], a @racket[traverse-part], a
7676
@racket[part-index-decl], a @racket[part-collect-decl], a
7777
@racket[part-tag-decl], @|void-const|, a list of @tech{pre-part} values, or a @racket[splice] containing
7878
a list of @tech{pre-part} values; otherwise returns @racket[#f].

scribble-doc/scribblings/scribble/renderer.scrbl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,20 @@ A renderer for a specific format is relatively unlikely to override
383383
any of these methods. Each method accepts the information accumulated
384384
so far and returns augmented information as a result.}
385385

386+
@defmethod[(traversed-parts [parts (listof part?)]
387+
[fp (and/c hash? immutable?)])
388+
(listof part?)]
389+
@defmethod[(traversed-parts-part [part part?]
390+
[fp (and/c hash? immutable?)])
391+
part?]{
392+
393+
The @method[render% traversed-parts] method replaces
394+
@racket[traverse-part]s in the sub-parts of @racket[parts]
395+
(transitively) with replacement @racket[part]s. The result list has
396+
the same item as @racket[parts] unmodified for each part that has no
397+
@racket[traverse-part]s.
398+
399+
@history[#:added "1.67"]}
386400

387401
@defmethod[(collect [parts (listof part?)]
388402
[dests (listof path-string?)]

scribble-doc/scriblib/scribblings/render-cond.scrbl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ output, use styles plus ``back end'' configurations for each target
1515
format (see @secref[#:doc scribble-doc "config"] in
1616
@other-manual[scribble-doc]).
1717

18-
As a last resort, the @racket[cond-element] and @racket[cond-block]
18+
As a last resort, the @racket[cond-element], @racket[cond-block], and @racket[cond-part]
1919
forms support varying the document content depending on the target
2020
format. More precisely, they generate parts of a document where
2121
content is delayed until the @tech[#:doc scribble-doc]{traverse pass}
2222
of document rendering. Format detection relies on the
2323
@racket['scribble:current-render-mode] registration that is accessible
2424
through a @racket[traverse-element] or @racket[traverse-block].
2525

26-
The syntax of @racket[cond-element] and @racket[cond-block] is based
26+
The syntax of @racket[cond-element], @racket[cond-block], and @racket[cond-part] is based
2727
on SRFI-0.
2828

2929
@defform*/subs[#:literals (and or not else)
@@ -60,4 +60,11 @@ Like @racket[cond-element], but generates a @racket[traverse-block]
6060
where the selected @racket[body] must produce a block according to
6161
@racket[block?].}
6262

63+
@defform*[[(cond-part [feature-requirement body ...+])
64+
(cond-part [feature-requirement body ...+] [else body ...+])]]{
6365

66+
Like @racket[cond-element], but generates a @racket[traverse-part]
67+
where the selected @racket[body] must produce a list parts according to
68+
@racket[part?].
69+
70+
@history[#:added "1.67"]}

scribble-lib/info.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
(define pkg-authors '(mflatt eli))
2323

24-
(define version "1.66")
24+
(define version "1.67")
2525

2626
(define license
2727
'((Apache-2.0 OR MIT)

scribble-lib/scribble/base-render.rkt

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,15 @@
458458
[fp (traverse-content (part-to-collect d) fp)]
459459
[fp (traverse-flow (part-blocks d) fp)])
460460
(for/fold ([fp fp]) ([p (in-list (part-parts d))])
461-
(traverse-part p fp))))
461+
(cond
462+
[(traverse-part? p)
463+
(traverse-force fp p
464+
(traverse-part-traverse p)
465+
(lambda (ps fp)
466+
(for/fold ([fp fp]) ([p (in-list ps)])
467+
(traverse-part p fp))))]
468+
[else
469+
(traverse-part p fp)]))))
462470

463471
(define/public (traverse-paragraph p fp)
464472
(traverse-content (paragraph-content p) fp))
@@ -527,6 +535,32 @@
527535
(let ([fp (hash-set fp p v2)]) (if (procedure? v2) fp (again v2 fp))))
528536
fp))
529537

538+
;; ----------------------------------------
539+
;; replace `traverse-part`s with `part`s
540+
541+
(define/public (traversed-parts ds fp)
542+
(for/list ([d (in-list ds)])
543+
(traversed-parts-part d fp)))
544+
545+
(define/public (traversed-parts-part d fp)
546+
(define ps
547+
(apply
548+
append
549+
(for/list ([p (in-list (part-parts d))])
550+
(cond
551+
[(traverse-part? p)
552+
(or (hash-ref fp p #f)
553+
(error 'traverse-part-parts "no part computed for traverse-part: ~e" p))]
554+
[else
555+
(list p)]))))
556+
(if (and (= (length ps) (length (part-parts d)))
557+
(ormap eq? ps (part-parts d)))
558+
d
559+
(struct-copy part
560+
d
561+
[parts (for/list ([p (in-list ps)])
562+
(traversed-parts-part p fp))])))
563+
530564
;; ----------------------------------------
531565
;; global-info collection
532566

@@ -727,7 +761,7 @@
727761
(collect-put! ci
728762
t
729763
;; See "INFO SHAPE" above.
730-
(vector (element-content i) (add-current-tag-prefix t))))
764+
(vector (element-content i) (add-current-tag-prefix t) #f)))
731765

732766
(define/public (collect-index-element i ci)
733767
(collect-put! ci

scribble-lib/scribble/core.rkt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@
245245
hash?
246246
. -> . (values part-number-item? hash?))]))
247247

248+
;; Traverse part is not serializable, because it's
249+
;; replaced away after the traverse step
250+
(define-struct traverse-part (traverse))
251+
248252
;; ----------------------------------------
249253

250254
(provide-structs
@@ -254,7 +258,7 @@
254258
[style style?]
255259
[to-collect list?]
256260
[blocks (listof block?)]
257-
[parts (listof part?)])]
261+
[parts (listof (or/c part? traverse-part?))])]
258262
[paragraph ([style style?]
259263
[content content?])]
260264
[table ([style style?]
@@ -369,6 +373,18 @@
369373

370374
;; ----------------------------------------
371375

376+
(define part-traverse-procedure/c
377+
(recursive-contract
378+
((symbol? any/c . -> . any/c)
379+
(symbol? any/c . -> . any)
380+
. -> . (or/c part-traverse-procedure/c
381+
(listof part?)))))
382+
383+
(provide part-traverse-procedure/c)
384+
(provide (contract-out (struct traverse-part ([traverse part-traverse-procedure/c]))))
385+
386+
;; ----------------------------------------
387+
372388
;; Traverse element has special serialization support:
373389
(define-struct traverse-element (traverse)
374390
#:property

scribble-lib/scribble/decode.rkt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
(part-collect-decl? v)
3535
(part-tag-decl? v)
3636
(part? v)
37+
(traverse-part? v)
3738
(and (splice? v)
3839
(andmap pre-part? (splice-run v)))
3940
(and (list? v)
@@ -218,7 +219,8 @@
218219
(part-to-collect part)
219220
(append para (list (car l)) (part-flow part))
220221
(part-parts part)))]
221-
[(part? (car l))
222+
[(or (part? (car l))
223+
(traverse-part? (car l)))
222224
(let ([para (decode-accum-para accum)]
223225
[part (decode-flow* (cdr l) keys colls tag-prefix tags vers style
224226
title index-desc part-depth)])
@@ -242,7 +244,8 @@
242244
(if (or (null? l)
243245
(and (part-start? (car l))
244246
((part-start-depth (car l)) . <= . part-depth))
245-
(part? (car l)))
247+
(part? (car l))
248+
(traverse-part? (car l)))
246249
(let ([para (decode-accum-para accum)]
247250
[s (decode-styled-part (reverse s-accum)
248251
(part-start-tag-prefix s)

scribble-lib/scribble/render.rkt

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,21 @@
7777
fn))))
7878
names))
7979
(define fp (send renderer traverse docs fns))
80-
(define info (send renderer collect docs fns fp))
81-
(for ([file (in-list info-input-files)])
82-
(let ([s (with-input-from-file file read)]) (send renderer deserialize-info s info)))
83-
(for ([xr (in-list xrefs)])
84-
(xref-transfer-info renderer info xr))
85-
(let ([r-info (send renderer resolve docs fns info)])
86-
(send renderer render docs fns r-info)
87-
(when info-output-file
88-
(let ([s (send renderer serialize-info r-info)])
89-
(with-output-to-file info-output-file #:exists 'truncate/replace (lambda () (write s)))))
90-
(when warn-undefined?
91-
(let ([undef (send renderer get-undefined r-info)])
92-
(unless (null? undef)
93-
(eprintf "Warning: some cross references may be broken due to undefined tags:\n")
94-
(for ([t (in-list undef)])
95-
(eprintf " ~s\n" t))))))
80+
(let ([docs (send renderer traversed-parts docs fp)]) ; no `traverse-part`s after this step
81+
(define info (send renderer collect docs fns fp))
82+
(for ([file (in-list info-input-files)])
83+
(let ([s (with-input-from-file file read)]) (send renderer deserialize-info s info)))
84+
(for ([xr (in-list xrefs)])
85+
(xref-transfer-info renderer info xr))
86+
(let ([r-info (send renderer resolve docs fns info)])
87+
(send renderer render docs fns r-info)
88+
(when info-output-file
89+
(let ([s (send renderer serialize-info r-info)])
90+
(with-output-to-file info-output-file #:exists 'truncate/replace (lambda () (write s)))))
91+
(when warn-undefined?
92+
(let ([undef (send renderer get-undefined r-info)])
93+
(unless (null? undef)
94+
(eprintf "Warning: some cross references may be broken due to undefined tags:\n")
95+
(for ([t (in-list undef)])
96+
(eprintf " ~s\n" t)))))))
9697
(void))

scribble-lib/scriblib/render-cond.rkt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
(for-syntax racket/base))
44

55
(provide cond-element
6-
cond-block)
6+
cond-block
7+
cond-part)
78

89
(define-for-syntax (render-cond stx mk check-result no-matching-case)
910
(syntax-case stx ()
@@ -42,12 +43,23 @@
4243
...
4344
[else (no-matching-case)]))))))]))
4445

46+
(define-syntax (cond-part stx)
47+
(render-cond stx #'traverse-part #'check-part #'no-part-case))
48+
4549
(define-syntax (cond-block stx)
4650
(render-cond stx #'traverse-block #'check-block #'no-block-case))
4751

4852
(define-syntax (cond-element stx)
4953
(render-cond stx #'traverse-element #'check-content #'no-element-case))
5054

55+
(define (check-part v)
56+
(unless (and (list? v) (andmap part? v))
57+
(raise-mismatch-error
58+
'cond-part
59+
"clause result is not a list of parts: "
60+
v))
61+
v)
62+
5163
(define (check-block v)
5264
(unless (block? v)
5365
(raise-mismatch-error
@@ -64,9 +76,14 @@
6476
v))
6577
v)
6678

79+
(define (no-part-case)
80+
(raise (make-exn:fail:contract
81+
"cond-part: no clause matched"
82+
(current-continuation-marks))))
83+
6784
(define (no-block-case)
6885
(raise (make-exn:fail:contract
69-
"cond-element: no clause matched"
86+
"cond-block: no clause matched"
7087
(current-continuation-marks))))
7188

7289
(define (no-element-case)

0 commit comments

Comments
 (0)