An implementation of the pretty printing algorithm described by Derek C. Oppen.
We also provide an API similar to
ruby/prettyprint
, which we call
Wadler
, in reference to Philip Wadler's paper, A prettier
printer,
the basis for prettyprint
. This can be really helpful if you decide to
transition from ruby/prettyprint
to this gem.
Wadler
is implemented on top of Oppen
, and it provides more options than
ruby/prettyprint
, notably:
- Consistent and inconsistent breaking.
- Explicit breaking, which is achievable in
ruby/prettyprint
with some monkeypatching. - Trimming of trailing whitespaces.
- Display a
String
on line break. - A bunch of helper methods to simplify common patterns like surrounding or separating tokens.
Wadler
calls Oppen
under the hood, so it's not a separate implementation,
and it's not calling ruby's prettyprint
.
Both implementations have their use cases:
- Oppen gives more control over tokens sent to the printer.
- Wadler gives a more "functional" API, which is far nicer to work with.
That being said, both APIs in this gem can achieve the same results, especially on consistent and inconsistent breaking.
tokens = [
Oppen.begin_inconsistent,
Oppen.string('Hello'),
Oppen.break(', '),
Oppen.string('World!'),
Oppen.line_break,
Oppen.string('How are you doing?'),
Oppen.end,
Oppen.eof,
]
puts Oppen.print(tokens:)
# Hello, World!
# How are you doing?
out = Oppen::Wadler.new(width: 20)
out.group(indent: 2) {
out.group {
out.text('def').breakable.text('foo')
}
out.parens_break_none {
out.separate(%w[bar baz bat qux], ',', break_type: :inconsistent) { |param|
out.text(param)
}
}
}
out.group(indent: 2) {
out
.break
.nest(indent: 2) {
out
.text('puts')
.breakable(line_continuation: ' \\')
.text('42')
}
}
out.break.text('end')
puts out.output
# def foo(bar, baz,
# bat, qux)
# puts \
# 42
# end
An easy way to add colors to the output on the terminal is wrap oppen
and expose your own vocabulary:
require 'colored'
class ColoredTty
KW_PALETTE = { Hello: :red, World: :green }.freeze
def initialize(...) = @out = Oppen::Wadler.new(...)
def breakable(...) = @out.breakable(...) && self
def keyword(value, width: value.length) = @out.text(value.send(KW_PALETTE[value.to_sym] || :white), width:) && self
def output = @out.output
def text(...) = @out.text(...) && self
end
out = ColoredTty.new(width: 12)
out.keyword('Hello').breakable.text('World')
puts out.output
# \e[31mHello\e[0m World
The same idea can be applied an adapted to make an HTML printer; all you need to take care of is the correct width of the text to preserve the width of the text and get an output identical to that of the tty colored printer.
Check out the examples/ folder for more details on how to use the Oppen and Wadler APIs.
- We took the liberty to rename functions to make the API more modern and closer to what we expect when writing Ruby code. All correspondences with the algorithm as described in Oppen's paper are noted in the comments of classes and methods.
- We do not raise exceptions when we overflow the margin. The only exceptions that we raise indicate a bug in the implementation. Please report them.
- The stacks described by the algorithm do not have a fixed size in our implementation: we upsize them when they are full.
- We can optionally trim trailing whitespaces (this feature is on by default for the
Wadler
API). - We added support for an additional new line anchors, see examples/configs/indent_anchor.rb.
- We added support for eager printing of
groups
; see examples/configs/eager_print.rb. - We introduced a new token (
Whitespace
) and added more customizations to one of the originals (Break
).
For more insight on how Oppen's algorithm works, check out docs/oppen_algorithm.md.