Pretty-printing

Pretty-printing is displaying a data structure as text, by adding line-breaks and indenttaion so that the visual structure of the output corresponds to the logical structure of data structure. This makes it easier to read and understand. Pretty-printing takes into account the column width of the output so as to avoid using more lines than needed.

Pretty-printing of standard sequences types such as lists and vectors is done by default. For example:

#|kawa:11|# (set! *print-right-margin* 50)
#|kawa:12|# '(ABCDEF (aa bb cc dd) (x123456789
#|.....13|# y123456789 z123456789) ABCDEFG HIJKL)
(ABCDEF (aa bb cc dd)
 (x123456789 y123456789 z123456789) ABCDEFG HIJK)

Setting *print-right-margin* to 50 causes output to be limited to 50 columns. Notice the top-level list has to be split, but sub-lists (aa bb cc dd) and (x123456789 y123456789 z123456789) don’t need to be split.

When outputting to a DomTerm REPL, then *print-right-margin* is ignored, and the line-breaking is actually handled by DomTerm. If you change the window width, DomTerm will dynamically re-calculate the line-breaks of previous pretten output. This works even in the case of a session saved to an HTML file, as long as JavaScript is enabled.

The concepts and terminology are based on those of Common Lisp.

Pretty-printing Scheme forms

Scheme and Lisp code is traditionally pretty-printed slightly differently than plain lists. The pprint procedure assumes the argument is a Scheme form, and prints its accordingly. For example the special form (let ...) is printed differently from a regular function call (list ...).

Procedure: pprint obj [out]

Assume obj is a Scheme form, and pretty-print it in traditional Scheme format. For example:

#|kawa:1|# (import (kawa pprint))
#|kawa:2|# (define fib-form
#|.....3|#   '(define (fibonacci n)
#|.....4|#      (let loop ((i0 0) (i1 1) (n n))
#|.....5|#        (if (<= n 0) i0
#|.....6|#            (loop i1 (+ i0 i1) (- n 1))))))
#|kawa:7|# (set! *print-right-margin* 80)
#|kawa:8|# (pprint fib-form)
(define (fibonacci n)
  (let loop ((i0 0) (i1 1) (n n)) (if (<= n 0) i0 (loop i1 (+ i0 i1) (- n 1)))))
#|kawa:9|# (set! *print-right-margin* 40)
#|kawa:10|# (pprint fib-form)
(define (fibonacci n)
  (let loop ((i0 0) (i1 1) (n n))
    (if (<= n 0)
        i0
        (loop i1 (+ i0 i1) (- n 1)))))

The pprint special-cases forms that start with define, if, lambda, let, and a few more, and formats them with “traditional” indentation. However, it is not as complete or polished as it should be. (It should also use a programmable dispatch table, rather than having these special cases hard-wired. That is an improvemet for another day.)

Generic pretty-printing functions

The following procedures are used to indicate logical blocks, and optional newlines.

To access them do:

(import (kawa pprint))

In the following, out is the output port, which defaults to (current-output-port).

Syntax: pprint-logical-block options statement*

Evaluate the statements within the context of a new “logical block”.

The options are one or more of the following:

prefix: prefix
per-line: per-line-prefix

Emit prefix or per-line-prefix (only one of them can be specified) before the start of the logical block. If per-line-prefix is provided, it is also print for each line within the logical block, indented the same. These are strings and default to "".

suffix: suffix

Emit suffix after the end of the logical block.

out: out

The output file.

For example to print a list you might do:

(pprint-logical-block prefix: "(" suffix: ")"
   print contents of list)

This macro is equivalent to:

(pprint-start-logical-block prefix is-per-line suffix out)
(try-finally
  (begin statement*)
  (pprint-end-logical-block suffix out))

Procedure: pprint-start-logical-block prefix is-per-line suffix out

Start a logical block. The is-per-line argument is a boolean to specifiy of prefix is a per-line-prefix or a plain prefix.

Procedure: pprint-end-logical-block suffix out

End a logical block.

Procedure: pprint-newline kind [out]

Print a conditional newline, where kind is one of the symbols 'fill, 'linear, 'mandatory, or 'miser. Usually follows printing of a space, as nothing is printed if the line is not broken here.

Procedure: pprint-ident mode amount [out]

Change how much following lines are indented (with the current logical block). The amount is the size of the indentation, in characters. The mode is either 'current (if the amount is relative to the current position), or 'block (if the amount is relative to the start (after any prefix) of the current logical block).