prettyprinter-1.7.1: A modern, easy to use, well-documented, extensible pretty-printer.

Safe Haskell | Safe-Inferred
Language | Haskell2010

Prettyprinter.Render.Tutorials.TreeRenderingTutorial

Contents

- The type of available markup
  - Convenience definitions
- The rendering algorithm
- Example invocation

Description

This module shows how to write a custom prettyprinter backend, based on a tree representation of a `SimpleDocStream`. For a stack machine approach, which may be more suitable for certain output formats, see Prettyprinter.Render.Tutorials.StackMachineTutorial.

Rendering to HTML, particularly using libraries such as blaze-html or lucid, is one important use case of tree-based rendering.

The module is written to be readable top-to-bottom in both Haddock and raw source form.

Synopsis

- data SimpleHtml
  - = Bold
  - | Italics
  - | Color Color
  - | Paragraph
  - | Headline
- data Color
  - = Red
  - | Green
  - | Blue
- bold :: Doc SimpleHtml -> Doc SimpleHtml
- italics :: Doc SimpleHtml -> Doc SimpleHtml
- paragraph :: Doc SimpleHtml -> Doc SimpleHtml
- headline :: Doc SimpleHtml -> Doc SimpleHtml
- color :: Color -> Doc SimpleHtml -> Doc SimpleHtml
- render :: SimpleDocStream SimpleHtml -> Text
- renderTree :: SimpleDocTree SimpleHtml -> Builder
- encloseInTagFor :: SimpleHtml -> Builder -> Builder

# The type of available markup

First, we define a set of valid annotations must be defined, with the goal of defining a ``Doc` `SimpleHtml``. We will later define how to convert this to the output format (`Text`).

data SimpleHtml #

Constructors

Bold |
Italics |
Color Color |
Paragraph |
Headline |

data Color #

Constructors

Red |
Green |
Blue |

## Convenience definitions

bold :: Doc SimpleHtml -> Doc SimpleHtml #

italics :: Doc SimpleHtml -> Doc SimpleHtml #

paragraph :: Doc SimpleHtml -> Doc SimpleHtml #

headline :: Doc SimpleHtml -> Doc SimpleHtml #

color :: Color -> Doc SimpleHtml -> Doc SimpleHtml #

# The rendering algorithm

With the annotation definitions out of the way, we can now define a conversion function from `SimpleDocStream` (annotated with our `SimpleHtml`) to the tree-shaped `SimpleDocTree`, which is easily convertible to a HTML/`Text` representation.

There are two ways to render this; the simpler one is just using `renderSimplyDecorated`. However, some output formats require more complicated functionality, so we explore this explicitly with a simple example below. An example for something more complicated is e.g. an XHTML renderer, where a newline may not simply be a newline character followed by a certain number of spaces, but e.g. involve adding a `br/` tag.

render :: SimpleDocStream SimpleHtml -> Text #

To render the HTML, we first convert the `SimpleDocStream` to the `SimpleDocTree` format, which makes enveloping sub-documents in markup easier.

This function is the entry main API function of the renderer; as such, it is only glue for the internal functions. This is similar to `render` from the stack machine tutorial in its purpose.

renderTree :: SimpleDocTree SimpleHtml -> Builder #

Render a `SimpleDocTree` to a `Builder`; this is the workhorse of the tree-based rendering approach, and equivalent to `renderStackMachine` in the stack machine rendering tutorial.

encloseInTagFor :: SimpleHtml -> Builder -> Builder #

Convert a `SimpleHtml` to a function that encloses a `Builder` in HTML tags. This is where the translation of style to raw output happens.

# Example invocation

We can now render an example document using our definitions:

    >>> :set -XOverloadedStrings
    >>> import qualified Data.Text.Lazy.IO as TL
    >>> :{
    >>> let go = TL.putStrLn . render . layoutPretty defaultLayoutOptions
    >>> in go (vsep
    >>> [ headline "Example document"
    >>> , paragraph ("This is a" <+> color Red "paragraph" <> comma)
    >>> , paragraph ("and" <+> bold "this text is bold.")
    >>> ])
    >>> :}
    <h1>Example document</h1>
    <p>This is a <span style="color: #f00">paragraph</span>,</p>
    <p>and <strong>this text is bold.</strong></p>