5 Printing
| (require toolbox/print) | package: toolbox-lib |
The toolbox/print module provides utilities for printing Racket values. In particular, it is intended to aid in writing custom write procedures for use with prop:custom-write.
5.1 General-purpose custom write helpers
value
custom-write-mode/c : flat-contract? = (or/c #f #t 0 1)
procedure
(write/mode v out mode) → void?
v : any/c out : output-port? mode : custom-write-mode/c
> (struct tuple1 (value) #:property prop:custom-write (λ (self out mode) (write-string "#<tuple1: " out) (write/mode (tuple1-value self) out mode) (write-string ">" out))) > (tuple1 'ok) #<tuple1: 'ok>
procedure
(make-write/mode out mode) → (->* [any/c] [output-port?] void?)
out : output-port? mode : custom-write-mode/c
> (struct tuple2 (a b) #:property prop:custom-write (λ (self out mode) (define recur (make-write/mode out mode)) (write-string "#<tuple2: " out) (recur (tuple2-a self)) (write-string " " out) (recur (tuple2-b self)) (write-string ">" out))) > (tuple2 1 2) #<tuple2: 1 2>
value
procedure
v : any/c
> empty-printing-value > (~a empty-printing-value) ""
> (~s empty-printing-value) ""
> (~v empty-printing-value) ""
procedure
(custom-printing-value proc [ #:quotable quotability]) → any/c proc : (-> output-port? custom-write-mode/c any) quotability : (or/c 'self 'never 'always 'maybe) = 'self
> (list (custom-printing-value #:quotable 'never (λ (out mode) (write/mode "never quoted" out mode)))) (list "never quoted")
procedure
(multiline-printing-string str [ #:indent-blank? indent-blank? #:indent-trailing? indent-trailing?]) → any/c str : string? indent-blank? : any/c = #f indent-trailing? : any/c = indent-blank?
> (list 'prefix: (multiline-printing-string "hello,\nworld!"))
'(prefix: hello,
world!)
The indent-blank? and indent-trailing? arguments control whether empty lines should be indented. If indent-blank? is #f (the default), then spaces are not inserted after newline characters followed immediately by another newline. This is generally the desired behavior, as such lines would otherwise consist entirely of invisible space characters.
Separately, indent-trailing? controls whether spaces should be inserted after a newline if it is the last character in str. By default, the indent-trailing? argument is simply the value of indent-blank?, but it can be configured separately because it can be useful if further printing follows the printed string that should be similarly indented.
> (list 'prefix: (multiline-printing-string "hello,\nworld!\n"))
'(prefix: hello,
world!
)
> (list 'prefix: (multiline-printing-string "hello,\nworld!\n" #:indent-trailing? #t))
'(prefix: hello,
world!
)
Note that, unlike some of the other functions provided by this module, the way a value returned by multiline-printing-string is printed does not depend on the value of the pretty-printing parameter, only on the result of port-counts-lines?. However, when pretty printing, multiline-printing-string will additionally cooperate with the pretty printer by using pretty-print-newline to print newline characters.
procedure
(printing-append v ...) → any/c
v : any/c
> (printing-append '(one two) '(3 4)) '(one two)'(3 4)
procedure
(printing-add-separators vs [ #:trailing trailing-v #:leading leading-v]) → list? vs : list? trailing-v : any/c = empty-printing-value leading-v : any/c = empty-printing-value
> (printing-add-separators '(one two three) #:trailing (unquoted-printing-string "<trailing>") #:leading (unquoted-printing-string "<leading>")) '(one<trailing> <leading>two<trailing> <leading>three)
procedure
(make-constructor-style-printer get-name get-args [ #:expression? expression?]) → (-> any/c output-port? custom-write-mode/c void?) get-name : (-> any/c (or/c symbol? string?)) get-args : (-> any/c list?) expression? : any/c = #t
procedure
(constructor-style-printing-value name args [ #:expression? expression?]) → any/c name : (or/c symbol? string?) args : list? expression? : any/c = #t
5.2 Cooperating with racket/pretty
The bindings in this section assist in writing custom write procedures that automatically adapt when pretty printing via racket/pretty. Specifically, when (pretty-printing) is #t and line location and column location counting has been enabled for an output port, they may break their printed output over multiple lines to avoid exceeding the target column width controlled by pretty-print-columns.
procedure
(printing-sequence vs [ #:space-after space-after #:hang hang-indent]) → any/c vs : list? space-after : exact-nonnegative-integer? = 0 hang-indent : exact-nonnegative-integer? = 0
> (cons 'prefix (printing-sequence '(three short values))) '(prefix . three short values)
> (cons 'prefix (printing-sequence '(some-very-long-values that-will-be-printed over-multiple-lines to-avoid-overflowing the-desired-width)))
'(prefix
.
some-very-long-values
that-will-be-printed
over-multiple-lines
to-avoid-overflowing
the-desired-width)
The space-after argument is useful when the printed sequence will be followed by a closing delimiter such as ) or >, as it ensures the sequence will be broken over multiple lines if the delimiter would not fit.
When printing is broken over multiple lines, the hang-indent argument controls how much additional indentation should be printed after each line break.
> (printing-sequence #:hang 2 '(some-long-values that-are-broken-over-several-lines and-are-indented-after-the-first-line))
'some-long-values
'that-are-broken-over-several-lines
'and-are-indented-after-the-first-line
procedure
(delimited-printing-sequence vs [ #:before before-str #:after after-str #:hang hang-indent]) → any/c vs : list? before-str : string? = "" after-str : string? = "" hang-indent : exact-nonnegative-integer? = 0
> (delimited-printing-sequence #:before "#<" #:after ">" '(my-cool-struct 3)) #<'my-cool-struct 3>
procedure
(printing-hang herald body [ #:indent indent-amount #:space-after space-after]) → any/c herald : any/c body : any/c indent-amount : exact-nonnegative-integer? = 1 space-after : exact-nonnegative-integer? = 0
> (printing-hang #:indent 2 (unquoted-printing-string "here is a long list:") '(it-gets-wrapped-over-several-lines because-it-is-too-long and-the-lines-are-indented))
here is a long list:
'(it-gets-wrapped-over-several-lines
because-it-is-too-long
and-the-lines-are-indented)
procedure
5.2.1 Printing unquoted expressions
syntax
(quasiexpr quasi-term)
term = ,expr | {~seq head-term ...} | {~if expr term term} | (head-term ... . term) | datum head-term = ,@expr | {~@ . term} | {~if expr head-term} | {~if expr head-term head-term} | term
> (quasiexpr (list (+ 1 2))) (list (+ 1 2))
Uses of unquote or unquote-splicing escape as in quasiquote, and any value inserted via an escape is printed via print with a quote depth of 0:
> (quasiexpr (list a ())) (list a ())
> (quasiexpr (list ,'a ,'())) (list 'a '())
Sequences produced by quasiexpr may be broken over multiple lines when pretty printing. Subsequences that should not be split, such as keyword argument pairs, can be grouped with ~seq:
> (quasiexpr (foo #:a 'really-long-arguments #:b 'that-cause-wrapping #:c 'over-multiple-lines #:d 'without-any-explicit-grouping))
(foo
#:a
'really-long-arguments
#:b
'that-cause-wrapping
#:c
'over-multiple-lines
#:d
'without-any-explicit-grouping)
> (quasiexpr (foo {~seq #:a 'really-long-arguments} {~seq #:b 'that-cause-wrapping} {~seq #:c 'over-multiple-lines} {~seq #:d 'with-explicit-grouping}))
(foo
#:a 'really-long-arguments
#:b 'that-cause-wrapping
#:c 'over-multiple-lines
#:d 'with-explicit-grouping)
Like syntax, {~@ . term} can be used within a head-term to splice a list term into the enclosing term. However, as quasiterm does not support repetitions via ..., the main use of ~@ is usually only useful as the empty sequence {~@} in one arm of a use of ~if.
An {~if cond-expr then-term else-term} term is a convenient abbreviation for a conditional subterm. If cond-expr evaluates to #f, it prints like else-term; otherwise, it prints like then-term. Within a head-term, {~if expr term} is equivalent to {~if expr term {~@}}.
The quasiexpr form can be useful when implementing custom write procedures for values that should print as applications of the functions used to create them, such as contracts.
syntax
5.2.2 Installing a custom overflow handler
procedure
(with-printing-overflow-handler out single-line-proc multi-line-proc [ #:width width #:space-after space-after]) → void? out : output-port? single-line-proc : (-> output-port? any) multi-line-proc : (-> (->* [output-port? exact-positive-integer?] void?) any) width : exact-positive-integer? = (pretty-print-columns) space-after : exact-nonnegative-integer? = 0
The procedure supplied to multi-line-proc is like pretty-print-newline, but its arguments default to out and width. Additionally, after writing a newline, it write spaces until the next column (as returned by port-next-location) is the same as it was when with-printing-overflow-handler was called. This has the effect of ensuring all subsequent lines of the print are correctly indented to align with the first line.
Internally, higher-level abstractions like printing-sequence and printing-hang use with-printing-overflow-handler to automatically adapt to the available space when pretty-printing via racket/pretty. In most cases, it is not necessary to use directly, but it can be useful if maximum control is needed.