collapsing

main
Matthew Butterick 6 years ago
parent ed82c7455c
commit df871eb5d7

@ -37,7 +37,13 @@
(txexpr 'q (append '((font "fira-mono")(fontsize "11")(bg "aliceblue")) attrs) exprs))
(define-tag-function (pre attrs exprs)
(txexpr 'q attrs exprs))
;; pre needs to convert white space to equivalent layout elements
(define new-exprs (add-between
(for*/list ([expr (in-list exprs)]
[str (in-list (string-split (car (get-elements expr)) "\n"))])
`(,(get-tag expr) ,(get-attrs expr) ,str))
'(q "")))
(txexpr 'q attrs new-exprs))
(define q:string (q #:in 'bi
#:out 'bo ;; align to baseline
@ -139,14 +145,24 @@
(values (cons new-run runs) rest)))
(struct line-break quad ())
(define lbr (q #:type line-break))
(define (line-wrap xs size)
(wrap xs size
#:hard-break (λ (q) (equal? "" (car (quad-elems q))))
#:hard-break (λ (q) (match (quad-elems q)
[(list (or "¶¶" "")) #t]
[_ #f]))
#:soft-break soft-break-for-line?
#:finish-wrap (λ (pcs q idx)
#R pcs
#R q
#R (= idx 1)
(define new-elems (consolidate-runs pcs))
(append
(if (= idx 1) (list q:line-spacer) null)
(if (and (= idx 1) #R (equal? (quad-elems q) '("¶¶")))
(list q:line-spacer)
null)
(list (struct-copy quad q:line
[attrs (let ([attrs (hash-copy (quad-attrs q:line))])
(define container-val (hash-ref (quad-attrs (car new-elems)) 'container #f))
@ -263,24 +279,17 @@
(define-syntax (mb stx)
(syntax-case stx ()
[(_ . STRS)
(with-syntax ([PS (syntax-property #'STRS 'ps)])
#'(#%module-begin
(define qx `(q ((font "Charter") (fontsize "12")) ,@(list . STRS)))
(run qx PS)))]))
[(_ PDF-PATH . STRS)
#'(#%module-begin
(define qx `(q ((font "Charter") (fontsize "12")) ,@(list . STRS)))
(run qx PDF-PATH))]))
(module+ reader
(require scribble/reader syntax/strip-context (only-in markdown parse-markdown)
racket/match txexpr)
(provide (rename-out [quad-read-syntax read-syntax]))
(module reader syntax/module-reader
qtest/markdown
#:read quad-read
#:read-syntax quad-read-syntax
#:whole-body-readers? #t ;; need this to make at-reader work
(require scribble/reader (only-in markdown parse-markdown) racket/list quad)
(define (quad-read p) (syntax->datum (quad-read-syntax (object-name p) p)))
(require racket/match txexpr)
(define (xexpr->parse-tree x)
;; an ordinary txexpr can't serve as a parse tree because of the attrs list fails when passed to #%app.
;; so stick an `attr-list` identifier on it which can hook into the expander.
@ -297,5 +306,10 @@
#:inside? #t
#:command-char #\◊))
(define stx (quad-at-reader path-string p))
(define parsed-stx (datum->syntax stx (xexpr->parse-tree (add-between (parse-markdown (apply string-append (syntax->datum stx))) '(q "")))))
(syntax-property parsed-stx 'ps (path-replace-extension path-string #".pdf"))))
(define parsed-stx (datum->syntax stx (xexpr->parse-tree (add-between (parse-markdown (apply string-append (syntax->datum stx))) '(q "¶¶")))))
(strip-context
(with-syntax ([PT parsed-stx]
[PDF-PATH (path-replace-extension path-string #".pdf")])
#'(module _ qtest/markdown
PDF-PATH
. PT)))))

@ -8,34 +8,6 @@
[(_ EXPR ...) (with-syntax ([debug (datum->syntax stx 'debug)])
#'(when debug (report EXPR ...)))]))
(define (wrap xs
[target-size (current-wrap-distance)]
[debug #f]
#:hard-break [hard-break? (λ (x) #f)]
#:soft-break [soft-break? (λ (x) #f)]
#:wrap-anywhere? [wrap-anywhere? #f]
#:finish-wrap [finish-wrap-proc (λ (xs q idx) (list xs))])
#;((listof quad?)
(real?
any/c
#:hard-break (quad? . -> . any/c)
#:soft-break (quad? . -> . any/c)
#:finish-wrap ((listof any/c) quad? natural? . -> . (listof any/c))) . ->* . (listof any/c))
;; the hard breaks are used to divide the wrap territory into smaller chunks
;; that can be cached, parallelized, etc.
(let loop ([wraps null][qs xs])
(match qs
;; ignore a trailing hard break
[(or (? null?) (list (? hard-break?))) (append* (reverse wraps))]
[(or (cons (? hard-break?) rest) rest)
(define-values (head tail) (splitf-at rest (λ (x) (not (hard-break? x)))))
;; head will be empty (intentionally) if qs starts with two hard breaks
;; because there should be a blank wrap in between
(define next-wrap (wrap-soft-breaks head target-size debug soft-break? wrap-anywhere? finish-wrap-proc))
(debug-report next-wrap)
(loop (cons next-wrap wraps) tail)])))
(define (nonprinting-at-start? x) (not (printable? x 'start)))
(define (nonprinting-at-end? x) (not (printable? x 'end)))
(define (nonprinting-soft-break-in-middle? x) (and (not (printable? x)) (soft-break? x)))
@ -45,12 +17,14 @@
;; thus beginning of list represents the end of the wrap
(append partial (dropf wrap nonprinting-soft-break-in-middle?)))
(define (wrap-soft-breaks qs
target-size
debug
soft-break?
wrap-anywhere?
finish-wrap-proc) ; takes quads in wrap, triggering quad, and wrap idx; returns list containing wrap (and maybe other things)
(define (wrap qs
[target-size (current-wrap-distance)]
[debug #f]
#:hard-break [hard-break? (λ (x) #f)]
#:soft-break [soft-break? (λ (x) #f)]
#:wrap-anywhere? [wrap-anywhere? #f]
#:finish-wrap [finish-wrap-proc (λ (xs q idx) (list xs))])
; takes quads in wrap, triggering quad, and wrap idx; returns list containing wrap (and maybe other things)
(define (finish-wrap qs wrap-idx [wrap-triggering-q (car qs)])
;; reverse because quads accumulated in reverse
;; wrap-triggering-q is ordinarily the last accumulated q
@ -64,10 +38,23 @@
[current-dist #false] ; #false (to indicate start) or integer
[qs qs]) ; list of quads
(match qs
[(== empty) (define last-wrap (finish-wrap (wrap-append next-wrap-tail next-wrap-head) wrap-idx #f))
; append* because `finish-wrap-proc` returns a spliceable list
; reverse because wraps accumulated in reverse
(append* (reverse (cons last-wrap wraps)))]
[(or (list (? hard-break?)) (== empty))
(define last-wrap (finish-wrap (wrap-append next-wrap-tail next-wrap-head) wrap-idx #f))
; append* because `finish-wrap-proc` returns a spliceable list
; reverse because wraps accumulated in reverse
(match (append* (reverse (cons last-wrap wraps)))
[(list (list)) (list)]
[val val])]
[(cons (? hard-break? hard-break-q) other-qs)
(debug-report 'hard-break)
;; a break is inevitable but we want to wait to finish the wrap until we see a hard quad
;; but we can move the current-partial into the current-wrap
(loop wraps
wrap-idx
(wrap-append (cons hard-break-q next-wrap-tail) next-wrap-head)
null
current-dist
other-qs)]
[(cons q other-qs)
(debug-report q 'next-q)
(debug-report (quad-elems q) 'next-q-elems)
@ -243,9 +230,9 @@
(module+ test
(test-case
"soft hyphens"
(check-equal? (linewrap (list shy) 1) (list (list)))
(check-equal? (linewrap (list shy shy) 2) (list (list)))
(check-equal? (linewrap (list shy shy shy) 2) (list (list)))
(check-equal? (linewrap (list shy) 1) (list))
(check-equal? (linewrap (list shy shy) 2) (list))
(check-equal? (linewrap (list shy shy shy) 2) (list))
(check-equal? (linewrap (list x shy) 1) (list (list x)))
(check-equal? (linewrap (list x shy shy shy shy) 1) (list (list x)))
;; todo: degenerate cases that don't work without continuations
@ -350,14 +337,14 @@
(check-equal? (pagewrap (list x pbr pbr x x) 2) (list (list x) pbr (list) pbr (list x x)))
(check-equal? (pagewrap (list lbr x lbr lbr pbr lbr x x lbr) 2) (list (list x) pbr (list x x)))))
(module+ test
(test-case
"composed line breaks and page breaks"
(check-equal? (pagewrap (linewrap null 1) 2) (list))
(check-equal? (pagewrap (linewrap (list x) 1) 2) (list (list x)))
(check-equal? (pagewrap (linewrap (list x x x) 1) 2) (list (list x lbr x) pbr (list x)))
(check-equal? (pagewrap (linewrap (list x x x) 2) 2) (list (list x x) pbr (list x)))
(check-equal? (pagewrap (linewrap (list x x x) 2) 1) (list (list x) pbr (list x) pbr (list x)))))
#;(module+ test
(test-case
"composed line breaks and page breaks"
(check-equal? (pagewrap (linewrap null 1) 2) (list))
(check-equal? (pagewrap (linewrap (list x) 1) 2) (list (list x)))
(check-equal? (pagewrap (linewrap (list x x x) 1) 2) (list (list x lbr x) pbr (list x)))
(check-equal? (pagewrap (linewrap (list x x x) 2) 2) (list (list x x) pbr (list x)))
(check-equal? (pagewrap (linewrap (list x x x) 2) 1) (list (list x) pbr (list x) pbr (list x)))))
(define (linewrap2 xs size [debug #f])
(add-between

Loading…
Cancel
Save