Scribble: clean up text-mode output

- line-flow paragraphs to fit in 72 columns
 - better indentation for itemizations
 - format tables with paragraphs and nested tables

original commit: 60c4acb094286e2501bdfc40a6bec2fd86faee4f
This commit is contained in:
Matthew Flatt 2011-03-20 17:19:03 -06:00
parent ce1101a292
commit 80991752ea
7 changed files with 288 additions and 54 deletions

View File

@ -1,9 +1,30 @@
(module text-render mzscheme
(module text-render racket/base
(require "core.ss"
mzlib/class)
racket/class
racket/port)
(provide render-mixin)
(define current-preserve-spaces (make-parameter #f))
(define current-indent (make-parameter 0))
(define (make-indent amt)
(+ amt (current-indent)))
(define (indent)
(let ([i (current-indent)])
(unless (zero? i)
(display (make-string i #\space)))))
(define (indented-newline)
(newline)
(indent))
(define indent-pxs (make-hash))
(define (indent->paragraph-px amt)
(or (hash-ref indent-pxs amt #f)
(let ([px (pregexp (format "^ *(.{1,~a}(?<! ))(?: |$)" (- 72 amt)))])
(hash-set! indent-pxs amt px)
px)))
(define (render-mixin %)
(class %
@ -14,15 +35,10 @@
(#rx"''" "\U201D")
(#rx"'" "\U2019")))
(inherit render-content
render-paragraph
render-block)
(inherit render-block)
(define/override (render-part d ht)
(let ([number (collected-info-number (part-collected-info d ht))])
(when (or (ormap values number)
(part-title-content d))
(newline))
(for-each (lambda (n)
(when n
(printf "~s." n)))
@ -33,15 +49,16 @@
(render-content (part-title-content d) d ht))
(when (or (ormap values number)
(part-title-content d))
(newline)
(newline))
(newline)
(render-flow (part-blocks d) d ht #f)
(let loop ([pos 1]
[secs (part-parts d)])
[secs (part-parts d)]
[need-newline? (pair? (part-blocks d))])
(unless (null? secs)
(newline)
(when need-newline? (newline))
(render-part (car secs) ht)
(loop (add1 pos) (cdr secs))))))
(loop (add1 pos) (cdr secs) #t)))))
(define/override (render-flow f part ht starting-item?)
(if (null? f)
@ -50,26 +67,60 @@
append
(render-block (car f) part ht starting-item?)
(map (lambda (p)
(newline) (newline)
(indented-newline)
(render-block p part ht #f))
(cdr f)))))
(define/override (render-intrapara-block p part ri first? last? starting-item?)
(unless first? (newline) (newline))
(unless first? (indented-newline))
(super render-intrapara-block p part ri first? last? starting-item?))
(define/override (render-table i part ht inline?)
(let ([flowss (table-blockss i)])
(if (null? flowss)
null
(apply
append
(map (lambda (d) (unless (eq? d 'cont) (render-block d part ht #f))) (car flowss))
(map (lambda (flows)
(newline)
(map (lambda (d) (unless (eq? d 'cont) (render-block d part ht #f))) flows))
(cdr flowss))))))
(let* ([strs (map (lambda (flows)
(map (lambda (d)
(if (eq? d 'cont)
d
(let ([o (open-output-string)])
(parameterize ([current-indent 0]
[current-output-port o])
(render-block d part ht #f))
(regexp-split
#rx"\n"
(regexp-replace #rx"\n$" (get-output-string o) "")))))
flows))
flowss)]
[widths (map (lambda (col)
(for/fold ([d 0]) ([i (in-list col)])
(if (eq? i 'cont)
0
(apply max d (map string-length i)))))
(apply map list strs))])
(for/fold ([indent? #f]) ([row (in-list strs)])
(let ([h (apply max 0 (map length row))])
(let ([row* (for/list ([i (in-range h)])
(for/list ([col (in-list row)])
(if (i . < . (length col))
(list-ref col i)
"")))])
(for/fold ([indent? indent?]) ([sub-row (in-list row*)])
(when indent? (indent))
(for/fold ([space? #f]) ([col (in-list sub-row)]
[w (in-list widths)])
(when space? (display " "))
(let ([col (if (eq? col 'cont)
""
col)])
(display col)
(display (make-string (- w (string-length col)) #\space)))
#t)
(newline)
#t)))
#t)
null))))
(define/override (render-itemization i part ht)
(let ([flows (itemization-blockss i)])
@ -78,11 +129,50 @@
(apply append
(begin
(printf "* ")
(render-flow (car flows) part ht #t))
(parameterize ([current-indent (make-indent 2)])
(render-flow (car flows) part ht #t)))
(map (lambda (d)
(printf "\n\n* ")
(render-flow d part ht #f))
(indented-newline)
(printf "* ")
(parameterize ([current-indent (make-indent 2)])
(render-flow d part ht #f)))
(cdr flows))))))
(define/override (render-paragraph p part ri)
(let ([o (open-output-string)])
(parameterize ([current-output-port o])
(super render-paragraph p part ri))
(let ([i (open-input-string
(regexp-replace* #rx"\n" (get-output-string o) " "))]
[px (indent->paragraph-px (current-indent))])
(let loop ([indent? #f])
(cond
[(or (regexp-try-match px i)
(regexp-try-match #px"^ *(.+(?<! ))(?: |$)" i))
=> (lambda (m)
(when indent? (indent))
(write-bytes (cadr m))
(newline)
(loop #t))]
[else
(regexp-try-match "^ +" i)
(let ([b (read-byte i)])
(unless (eof-object? b)
(when indent? (indent))
(write-byte b)
(copy-port i (current-output-port))
(newline)))])))
null))
(define/override (render-content i part ri)
(if (and (element? i)
(let ([s (element-style i)])
(or (eq? 'hspace s)
(and (style? s)
(eq? 'hspace (style-name s))))))
(parameterize ([current-preserve-spaces #t])
(super render-content i part ri))
(super render-content i part ri)))
(define/override (render-other i part ht)
(cond
@ -96,12 +186,14 @@
[(lang) ">"]
[(rang) "<"]
[(rarr) "->"]
[(nbsp) " "]
[(nbsp) "\uA0"]
[(prime) "'"]
[(alpha) "\u03B1"]
[(infin) "\u221E"]
[else (error 'text-render "unknown element symbol: ~e" i)]))]
[(string? i) (display i)]
[(string? i) (if (current-preserve-spaces)
(display (regexp-replace* #rx" " i "\uA0"))
(display i))]
[else (write i)])
null)

View File

@ -1,9 +1,7 @@
(Felleisen et al. 2010)
(Felleisen et al. 2010, part I)
(Felleisen et al. 2010, part II)
(Felleisen et al. 2010)
 (Felleisen et al. 2010)  (Felleisen et al. 2010, part I)  (Felleisen et
al. 2010, part II)  (Felleisen et al. 2010)
Bibliography
Matthias Felleisen, Robert Bruce Findler, and Matthew Flatt. Semantics Engineering with PLT Redex. MIT Press, 2010.
Matthias Felleisen, Robert Bruce Findler, and Matthew Flatt. Semantics
Engineering with PLT Redex. MIT Press, 2010.

View File

@ -1,5 +1,4 @@
quote
@itemlist[#:style 'ordered
@item{Eat cookie.}]
  @itemlist[#:style 'ordered
            @item{Eat cookie.}]

View File

@ -1,4 +1,3 @@
Example:
> #f
#f
  > #f
  #f

View File

@ -1,18 +1,17 @@
Pretty-Print-Handler Bug Example
Example:
> '((x "positional 1")
(rest ("positional 2" "positional 3"))
(a ())
(b ("b-arg"))
(c (("first c1" "second c1") ("first c2" "second c2")))
(d #f)
(e ()))
'((x "positional 1")
(rest ("positional 2" "positional 3"))
(a ())
(b ("b-arg"))
(c (("first c1" "second c1") ("first c2" "second c2")))
(d #f)
(e ()))
Example:
  > '((x "positional 1")
        (rest ("positional 2" "positional 3"))
        (a ())
        (b ("b-arg"))
        (c (("first c1" "second c1") ("first c2" "second c2")))
        (d #f)
        (e ()))
  '((x "positional 1")
    (rest ("positional 2" "positional 3"))
    (a ())
    (b ("b-arg"))
    (c (("first c1" "second c1") ("first c2" "second c2")))
    (d #f)
    (e ()))

View File

@ -0,0 +1,76 @@
#lang scribble/manual
@(require (for-label racket/base))
@title{Document}
This document exercises various constructs to check text output.
@section{Part A}
Scribble is a collection of tools for creating prose documents---papers, books, library documentation, etc.---in HTML or PDF (via Latex)
form. More generally, Scribble helps you
write programs that are rich
in textual content, whether the content is prose to be typeset or any
other form of text to be generated
programmatically.
@subsection{A Subsection}
Here's some Racket code:
@racketblock[
(define half (lambda (x)
(x x)))
(x x)
]
@subsection{Another Subsection}
@defmodule[racket/base]
@defproc[(cons [car (or/c #f
other?)]
[cdr any?])
stuff?]{
Ok?}
@section{B}
@itemlist[
@item{Run
@commandline{scribble --pdf mouse.scrbl}
to generate PDF as @filepath{mouse.pdf}. This will
work only if you have @exec{pdflatex} installed.
If you'd like to see the intermediate Latex, try
@commandline{scribble --latex mouse.scrbl}
to generate @filepath{mouse.tex}.}
@item{Run
@commandline{scribble --html mouse.scrbl}
to generate HTML as @filepath{mouse.html}. You may
notice that the apostrophe in ``he's'' turned into a
curly apostrophe.}
@item{Run
@commandline{scribble --htmls mouse.scrbl}
to generate HTML as @filepath{mouse/index.html}.
Sub-sections (which we add next) will appear as separate
HTML files in the @filepath{mouse} directory.}
]
Run the @exec{scribble} command(s) from the old section
again. You may notice the curly double-quotes in the output, and
the @litchar{---} turned into an em dash.
@section{C}
@subsection{Inside C}
Section C had no text before its subsections.
@subsection{Inside C, Again}
But the subsections have text.

View File

@ -0,0 +1,71 @@
Document
This document exercises various constructs to check text output.
1. Part A
Scribble is a collection of tools for creating prose documents—papers,
books, library documentation, etc.—in HTML or PDF (via Latex) form. More
generally, Scribble helps you write programs that are rich in textual
content, whether the content is prose to be typeset or any other form of
text to be generated programmatically.
1.1. A Subsection
Heres some Racket code:
  (define half (lambda (x)
                 (x x)))
  (x x)
1.2. Another Subsection
 (require racket/base)
(cons car cdr) -> stuff?
   car   :   (or/c #f
      other?)
  cdr : any?
Ok?
2. B
* Run
  scribble --pdf mouse.scrbl
to generate PDF as "mouse.pdf". This will work only if you have
pdflatex installed. If youd like to see the intermediate Latex, try
  scribble --latex mouse.scrbl
to generate "mouse.tex".
* Run
  scribble --html mouse.scrbl
to generate HTML as "mouse.html". You may notice that the apostrophe
in “hes” turned into a curly apostrophe.
* Run
  scribble --htmls mouse.scrbl
to generate HTML as "mouse/index.html". Sub-sections (which we add
next) will appear as separate HTML files in the "mouse" directory.
Run the scribble command(s) from the old section again. You may notice
the curly double-quotes in the output, and the --- turned into an em
dash.
3. C
3.1. Inside C
Section C had no text before its subsections.
3.2. Inside C, Again
But the subsections have text.