//
// Copyright (c) 2025, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 21 Apr 2025 Matthew Giannini Creation
//
**
** Renders a tree of nodes to plain text
**
@Js
const class TextRenderer : Renderer
{
//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////
** Obtain a builder for configuring the renderer
static TextRendererBuilder builder() { TextRendererBuilder() }
** Get a renderer with all the default configuration
static new make() { builder.build }
internal new makeBuilder(TextRendererBuilder builder)
{
this.lineBreakRendering = builder.lineBreakRendering
factories := builder.nodeRendererFactories
factories.add(|cx->NodeRenderer| { CoreTextNodeRenderer(cx) })
this.nodeRendererFactories = factories
}
internal const LineBreakRendering lineBreakRendering
internal const |TextContext->NodeRenderer|[] nodeRendererFactories
//////////////////////////////////////////////////////////////////////////
// Render
//////////////////////////////////////////////////////////////////////////
override Void renderTo(OutStream out, Node node)
{
cx := TextContext(this, TextWriter(out, this.lineBreakRendering))
cx.render(node)
}
}
**************************************************************************
** TextRendererBuilder
**************************************************************************
**
** Builder for configuring a `TextRenderer`
**
@Js
final class TextRendererBuilder
{
internal new make() { }
internal |TextContext->NodeRenderer|[] nodeRendererFactories := [,]
internal LineBreakRendering lineBreakRendering := LineBreakRendering.compact
** Get the configured `TextRenderer`
TextRenderer build() { TextRenderer(this) }
** Configure how line breaks (newlines) are rendered. The default is
** `LineBreakRendering.compact`
This withLineBreakRendering(LineBreakRendering lineBreakRendering)
{
this.lineBreakRendering = lineBreakRendering
return this
}
** Add a factory for instantiating a node renderer (done when rendering). This allows
** This allows to override the rendering of node types or define rendering for
** custom node types.
**
** If multiple node renderers for the same node type are created, the one from the
** factory that was added first "wins". (This is how the rendering for core node types
** can be overridden; the default rendering comes last).
This nodeRendererFactory(|TextContext->NodeRenderer| factory)
{
nodeRendererFactories.add(factory)
return this
}
** Enable the given extensions on this renderer.
This extensions(MarkdownExt[] exts)
{
exts.each |ext| { ext.extendText(this) }
return this
}
}
**************************************************************************
** LineBreakRendering
**************************************************************************
@Js
enum class LineBreakRendering
{
** Strip all line breaks within blocks and between blocks, resulting in all the
** text on a single line.
strip,
** Use single line breaks between blocks, not a blank line.
** Also renderes all lists as tight
compact,
** Separate blocks by a blank line (and respect tight vs loose lists)
separate_blocks
}