refactor `merge-newlines` & add docs

pull/108/merge
Matthew Butterick 9 years ago
parent b728c96166
commit 27b783e6ba

@ -313,17 +313,17 @@
(define not-block? (λ(i) (not (block-txexpr? i))))
(filter-not empty?
(for/list ([i (in-range (len xc))])
(let ([item (get xc i)])
(cond
;; skip first and last
[(or (= i 0) (= i (sub1 (len xc)))) item]
[(equal? item newline)
(match (get xc (- i 1) (+ i 2)) ; a three-element slice with x[i] in the middle
;; only convert if neither adjacent tag is a block
;; (because blocks automatically force a newline before & after)
[(list (? not-block?) newline (? not-block?)) linebreak]
[else empty])] ; otherwise delete
[else item])))))
(let ([item (get xc i)])
(cond
;; skip first and last
[(or (= i 0) (= i (sub1 (len xc)))) item]
[(equal? item newline)
(match (get xc (- i 1) (+ i 2)) ; a three-element slice with x[i] in the middle
;; only convert if neither adjacent tag is a block
;; (because blocks automatically force a newline before & after)
[(list (? not-block?) newline (? not-block?)) linebreak]
[else empty])] ; otherwise delete
[else item])))))
(module-test-external
(check-equal? (detect-linebreaks '("foo" "\n" "bar")) '("foo" (br) "bar"))
@ -366,39 +366,30 @@
(and (string? x) (regexp-match paragraph-pattern x)))
(define (newline? x)
(and (string? x) (equal? (world:current-newline) x)))
(define (not-newline? x)
(not (newline? x)))
(define (do-merge xs [acc '()])
(if (empty? xs)
acc
;; Try to peel the newlines off the front.
(let-values ([(leading-newlines remainder) (splitf-at xs newline?)])
(if (not (empty? leading-newlines)) ; if you got newlines ...
;; combine them into a string and append them to the accumulator,
;; and recurse on the rest
(do-merge remainder (append acc (list (apply string-append leading-newlines))))
;; otherwise peel off elements up to the next newline, append them to accumulator,
;; and recurse on the rest
(do-merge (dropf remainder not-newline?)
(append acc (takef remainder not-newline?)))))))
;; Find adjacent newline characters in a list and merge them into one item
;; Scribble, by default, makes each newline a separate list item
;; In practice, this is worthless.
;; Scribble, by default, makes each newline a separate list item.
(define+provide/contract (merge-newlines x)
(txexpr-elements? . -> . txexpr-elements?)
(cond
[(list? x) (do-merge (map merge-newlines x))]
[else x]))
(txexpr-elements? . -> . txexpr-elements?)
(define (newlines? x)
(and (string? x)
(let ([newline-pat (regexp (format "^~a+$" (world:current-newline)))])
(regexp-match newline-pat x))))
(define (merge-if-newlines xs)
(if (newlines? (car xs))
(list (string-append* xs))
xs))
(let loop ([x x])
(if (list? x)
(let ([xs (map loop x)])
(append-map merge-if-newlines (slicef xs newlines?)))
x)))
(module-test-external
(check-equal? (merge-newlines '(p "\n" "foo" "\n" "\n" "bar" (em "\n" "\n" "\n")))
'(p "\n" "foo" "\n\n" "bar" (em "\n\n\n"))))
(check-equal? (merge-newlines '(p "\n" "\n" "foo" "\n" "\n\n" "bar" (em "\n" "\n" "\n")))
'(p "\n\n" "foo" "\n\n\n" "bar" (em "\n\n\n"))))

@ -350,12 +350,23 @@ In @racket[_str], convert three hyphens to an em dash, and two hyphens to an en
]
@defproc[
(merge-newlines
[elements (listof xexpr?)])
(listof xexpr?)]
Within @racket[_elements], merge sequential newline characters (@racket["\n"]) into a single whitespace element. Helper function used by @racket[detect-paragraphs].
@examples[#:eval my-eval
(merge-newlines '(p "\n" "\n" "foo" "\n" "\n\n" "bar"
(em "\n" "\n" "\n")))]
@defproc[
(detect-linebreaks
[tagged-xexpr-elements txexpr-elements?]
[tagged-xexpr-elements (listof xexpr?)]
[#:separator linebreak-sep string? (world:current-linebreak-separator)]
[#:insert linebreak xexpr? '(br)])
txexpr-elements?]
(listof xexpr?)]
Within @racket[_tagged-xexpr-elements], convert occurrences of @racket[_linebreak-sep] (@racket["\n"] by default) to @racket[_linebreak], but only if @racket[_linebreak-sep] does not occur between blocks (see @racket[block-txexpr?]). Why? Because block-level elements automatically display on a new line, so adding @racket[_linebreak] would be superfluous. In that case, @racket[_linebreak-sep] just disappears.
@examples[#:eval my-eval
@ -365,12 +376,12 @@ Within @racket[_tagged-xexpr-elements], convert occurrences of @racket[_linebrea
@defproc[
(detect-paragraphs
[elements txexpr-elements?]
[elements (listof xexpr?)]
[#:separator paragraph-sep string? (world:current-paragraph-separator)]
[#:tag paragraph-tag symbol? 'p]
[#:linebreak-proc linebreak-proc (txexpr-elements? . -> . txexpr-elements?) detect-linebreaks]
[#:linebreak-proc linebreak-proc ((listof xexpr?) . -> . (listof xexpr?)) detect-linebreaks]
[#:force? force-paragraph? boolean? #f])
txexpr-elements?]
(listof xexpr?)]
Find paragraphs within @racket[_elements] and wrap them with @racket[_paragraph-tag]. Also handle linebreaks using @racket[detect-linebreaks].
What counts as a paragraph? Any @racket[_elements] that are either a) explicitly set apart with @racket[_paragraph-sep], or b) adjacent to a @racket[block-txexpr?] (in which case the paragraph-ness is implied).

Loading…
Cancel
Save