Safe Haskell | Safe |
---|---|
Language | Haskell2010 |
Deprecated: Writing your own stack machine is probably more efficient and customizable; also consider using »renderSimplyDecorated(A)« instead
This module shows how to write a custom prettyprinter backend, based on
directly converting a SimpleDocStream
to an output format using a stack
machine. For a tree serialization approach, which may be more suitable for
certain output formats, see
Data.Text.Prettyprint.Doc.Render.Tutorials.TreeRenderingTutorial.
Rendering to ANSI terminal with colors is an important use case for stack machine based rendering.
The module is written to be readable top-to-bottom in both Haddock and raw source form.
Synopsis
- data SimpleHtml
- data Color
- 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
- renderStackMachine :: SimpleDocStream SimpleHtml -> StackMachine Builder SimpleHtml ()
- htmlTag :: SimpleHtml -> (Builder, Builder)
- render :: SimpleDocStream SimpleHtml -> Text
The type of available markup
First, we define a set of valid annotations must be defined, with the goal of
defining a
. We will later define how to convert this to
the output format (Doc
SimpleHtml
Text
).
Convenience definitions
bold :: Doc SimpleHtml -> Doc SimpleHtml Source #
italics :: Doc SimpleHtml -> Doc SimpleHtml Source #
paragraph :: Doc SimpleHtml -> Doc SimpleHtml Source #
headline :: Doc SimpleHtml -> Doc SimpleHtml Source #
color :: Color -> Doc SimpleHtml -> Doc SimpleHtml Source #
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
final 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 ANSI terminal
rendering, where on popping we need to regenerate the previous style,
requiring a pop (discard current style) followed by a peek (regenerate
previous style).
renderStackMachine :: SimpleDocStream SimpleHtml -> StackMachine Builder SimpleHtml () Source #
The StackMachine
type defines a stack machine suitable for many rendering
needs. It has two auxiliary parameters: the type of the end result, and the
type of the document’s annotations.
Most StackMachine
creations will look like this definition: a recursive
walk through the SimpleDocStream
, pushing styles on the stack and popping
them off again, and writing raw output.
The equivalent to this in the tree based rendering approach is
renderTree
.
htmlTag :: SimpleHtml -> (Builder, Builder) Source #
Convert a SimpleHtml
annotation to a pair of opening and closing tags.
This is where the translation of style to raw output happens.
render :: SimpleDocStream SimpleHtml -> Text Source #
We can now wrap our stack machine definition from renderStackMachine
in a
nicer interface; on successful conversion, we run the builder to give us the
final Text
, and before we do that we check that the style stack is empty
(i.e. there are no unmatched style applications) after the machine is run.
This function does only a bit of plumbing around renderStackMachine
, and is
the main API function of a stack machine renderer. The tree renderer
equivalent to this is
render
.
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>