From df871eb5d7004ba2bc99250c04bb6308885f100c Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Tue, 15 Jan 2019 13:26:14 -0800 Subject: [PATCH] collapsing --- quad/qtest/markdown.rkt | 56 +++++++++++++++++---------- quad/quad/wrap.rkt | 85 +++++++++++++++++------------------------ 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/quad/qtest/markdown.rkt b/quad/qtest/markdown.rkt index 86c34da1..2a891fa4 100644 --- a/quad/qtest/markdown.rkt +++ b/quad/qtest/markdown.rkt @@ -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")))) \ No newline at end of file + (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))))) diff --git a/quad/quad/wrap.rkt b/quad/quad/wrap.rkt index 16b4676e..d2c421c7 100644 --- a/quad/quad/wrap.rkt +++ b/quad/quad/wrap.rkt @@ -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