fmt:   an extensible code formatter for Racket
1 Running raco fmt
2 Examples
3 Unstable concepts
formatter-map/  c
4 Unstable API
program-format
empty-formatter-map
standard-formatter-map
compose-formatter-map
5 Parameters
current-width
current-max-blank-lines
current-indent
6 Related work
8.2

fmt: an extensible code formatter for Racket

Sorawee Porncharoenwase <sorawee.pwase@gmail.com>

 (require fmt) package: fmt

This package provides a tool raco fmt to reformat Racket code.

The package uses pprint-compact, a very expressive pretty printer library, to compute the most optimal layout of the output code, and uses syntax-color/module-lexer to lex the input program.

The interface to allow users to extend formatting style is extremely unstable and is still a work in progress. For now, the only thing that is stable is the command raco fmt.

1 Running raco fmt

raco fmt file.rkt ... reads file.rkts and displays the formatted programs to the standard output. If file.rkts are not given, it accepts an input program from the standard input.

The raco fmt command accepts the following flags:

2 Examples

Given the file "example.rkt" shown on the left, running raco fmt --width 40 example.rkt would output the program on the right:

example.rkt

#lang racket
 
; low :: number? -> listof number?
(define low
(λ (argument)
   (cond   (; this is the base case
        (zero? argument)
            '())
       (else
          (define
          content (* argument (+
          argument 1)
          (+ argument 2) (+ argument
          3)))
       (define
    next
    ( low ...........................
    ...))     ; recursive call
 
(cons  content    next)))))
 
 
 
  ; high :: number? -> listof number?
(  define high ...)

formatted example.rkt

#lang racket
 
; low :: number? -> listof number?
(define low
  (λ (argument)
    (cond
      ; this is the base case
      [(zero? argument) '()]
      [else
       (define content
         (* argument
            (+ argument 1)
            (+ argument 2)
            (+ argument 3)))
       (define next
         (low
          ...........................
          ...)) ; recursive call
 
       (cons content next)])))
 
; high :: number? -> listof number?
(define high ...)

3 Unstable concepts

A formatter is a function that accepts a code fragment and returns a doc?. In principle, you can create your own formatter, but you need to understand many structures that are currently undocumented and unstable. (If you want to implement one, perhaps take a look at this file.)

A formatter map is a function that accepts either a string or #f, and returns either a formatter or #f. Conceptually, when the input is a string s, a formatter map should return a formatter that will format a form named s, and When the input is #f, the formatter map should return a formatter that will format function application. An exception is that the formatter map can also return #f, which means the formatter map wants to let other fallback formatter maps to handle formatting instead.

value

formatter-map/c : (-> (or/c #f string?) (or/c #f formatter))

Recognizes a formatter map.

4 Unstable API

procedure

(program-format s    
  [#:formatter-map formatter-map    
  #:width width    
  #:max-blank-lines max-blank-lines    
  #:indent indent])  string?
  s : string?
  formatter-map : formatter-map/c = empty-formatter-map
  width : (or/c natural-number/c +inf.0) = (current-width)
  max-blank-lines : (or/c natural-number/c +inf.0)
   = (current-max-blank-lines)
  indent : natural-number/c = (current-indent)
Formats string s with formatter-map under various configurations (see Running raco fmt for details).

Examples:
> (define s "(define (foo) (bar baz) food) (define-like (foo) (bar baz) food)")
> (display (program-format s))

(define (foo)

  (bar baz)

  food)

(define-like (foo) (bar baz) food)

> (define (lib-define-formatter-map s)
    (cond
      [(and (string? s) (string-prefix? s "define-"))
       (standard-formatter-map "define")]
      [else #f]))
> (define (lib-bar-formatter-map s)
    (case s
      [("bar") (standard-formatter-map "cond")]
      [else #f]))
> (display (program-format s #:formatter-map lib-define-formatter-map))

(define (foo)

  (bar baz)

  food)

(define-like (foo)

  (bar baz)

  food)

> (display (program-format s #:formatter-map lib-bar-formatter-map))

(define (foo)

  (bar

    baz)

  food)

(define-like (foo)

             (bar

               baz)

             food)

> (display (program-format s #:formatter-map (compose-formatter-map
                                              lib-define-formatter-map
                                              lib-bar-formatter-map)))

(define (foo)

  (bar

    baz)

  food)

(define-like (foo)

  (bar

    baz)

  food)

A formatter map that does not handle any form.

The fallback formatter map. It defines format styles for the following forms:
'lambda 'λ 'case-lambda
'define 'define-values
'define-for-syntax 'define-values-for-syntax
'define-syntax 'define-syntaxes
'define-syntax-parameter
'define-syntax-parse-rule 'define-simple-macro
'define-syntax-parser
'match-define 'match-define-values
'let 'let-values 'let*-values 'letrec 'letrec-values
'let-syntax 'letrec-syntax 'let-syntaxes 'letrec-syntaxes
'letrec-syntaxes+values
'with-syntax 'with-syntax*
'shared
'parameterize 'parameterize* 'syntax-parameterize
'if 'cond 'when 'unless 'case
'match 'match*
'begin 'begin0 'begin-for-syntax
'syntax-parse 'syntax-parser
'syntax-rules 'syntax-case
'define-syntax-rule
'match 'match*
'syntax/loc 'quasisyntax/loc
'module 'module* 'module+
'provide 'require
'for 'for/fold 'for/list 'for/vector 'for/hash 'for/hasheq 'for/hasheqv
'for* 'for*/fold 'for*/list 'for*/vector 'for*/hash 'for*/hasheq 'for*/hasheqv
'struct 'define-struct
'import 'export 'link 'rename
'class 'instantiate 'public 'private 'override 'inherit 'field 'init
For other forms, it uses the function application style.

procedure

(compose-formatter-map f ...)  formatter-map/c

  f : formatter-map/c
Constructs a formatter map that tries the input functions in order. The first function that returns a formatter will be used.

5 Parameters

parameter

(current-width)  (or/c +inf.0 natural-number/c)

(current-width width)  void?
  width : (or/c +inf.0 natural-number/c)
 = 102
See Running raco fmt for details.

parameter

(current-max-blank-lines)  (or/c +inf.0 natural-number/c)

(current-max-blank-lines max-blank-lines)  void?
  max-blank-lines : (or/c +inf.0 natural-number/c)
 = 1
See Running raco fmt for details.

parameter

(current-indent)  natural-number/c

(current-indent indent)  void?
  indent : natural-number/c
 = 0
See Running raco fmt for details.

6 Related work