Terminal Document Renderer

Introduction

The terminal document renderer writes text::TextDocument and text::TextNode trees as styled terminal text. TerminalDocumentStyle describes text style overlays, paragraph layout, decorations, and list markers with selectors that match text::TextNodeType values directly.

Usage

Use TerminalDocumentRenderer when semantic document nodes should be rendered to a CursorWriter. The renderer keeps the current style by value, so a renderer can be reused for repeated render calls.

The writer width selects the layout width once at the start of a render. Reported widths of 60 columns or more are used directly. Widths from 1 through 59, an unknown width, and zero select an 80-column layout. A narrower destination may wrap these completed lines naturally. There is no separate narrow-output layout.

Use TerminalDocumentStyle to customize the default text style, base block layout, and selector-specific rules. Rules use BlockStyle for terminal text style overlays and ParagraphIndents with block::Margins for layout. Prefixes, suffixes, and markers are exposed as read-only BlockString values.

Selectors may constrain a rule to nodes below an ancestor node type. Ancestor matching considers the complete parent chain, which allows a diagnostic title inside a block quote to differ from the top-level title.

Container Decorations

For structural containers, prefix and suffix are standalone lines outside the content. linePrefix is prepended to each logical content line. Nested containers combine their line prefixes from outermost to innermost, which is useful for reply-style quote borders.

Structured Fields and Inline Paths

FieldList contains FieldItem nodes made from FieldLabel and FieldContent. Labels occupy an aligned column while they fit within one quarter of the available width. Unstacked content begins one column after the widest rendered label, including its trailing colon. If any label exceeds that threshold, the whole list uses stacked values with an eight-column continuation indent. Nested field lists add four columns of indentation.

Separator and EscapeSequence are generic inline semantic nodes. Separators are styled wrapping opportunities that remain on the preceding line; escape sequences remain indivisible. These semantics are represented as source-relative metadata. The renderer does not insert hidden Unicode characters into the text.

Consecutive inline children of a list item form one semantic paragraph. Inline style boundaries such as Strong or Emphasis therefore do not introduce physical line breaks; only block children and explicit line-break nodes do.

The default system-output style renders cause headings and their frame as non-bold bright black. When terminal styling is unavailable, the plain renderer preserves diagnostic structure with Error: on the root, colons on headings, two-space content indentation, nested cause indentation, and code gutters.

Implementation Notes

The renderer has one forward data flow:

TextDocument
    -> semantic inline content
    -> logical blocks
    -> styled physical lines
    -> CursorWriter

The inline builder traverses each text-bearing node in prefix, direct text, children, suffix order. Alongside the styled text it records soft-break boundaries for Separator nodes and indivisible source ranges for EscapeSequence nodes.

The block builder resolves selectors, text styles, margins, paragraph indents, list markers, and the ordered path of active container frames. A logical block stores only these resolved values; it does not retain mutable style rules or output-specific offsets.

The document layout owns width calculation, vertical margin collapse, paragraph wrapping, field columns, and frame composition. Paragraph content is wrapped at the width remaining after frame and child margins. Each finished content line is then composed in this order: outer container spacing, ordered frame prefixes, child margin, paragraph or list indentation, and content. Blank lines inside a frame use the active frame prefix, which keeps borders continuous. Paragraph line construction uses the same internal paragraph Printer as direct cursor paragraph output; buffer painting remains fragment-based because its colors may depend on target cells.

Only after every physical line has been materialized does the writer adapter emit output. It performs no layout and writes exactly one styled line plus one line break per physical line. New block kinds can therefore extend the block builder and physical-line materializer without adding another sink or traversal mode.

The core invariants are:

  • layout width is selected once per render;

  • semantic indices always refer to unmodified inline source text;

  • frames and margins never participate in paragraph tokenization;

  • vertical margin collapse occurs only between logical blocks;

  • output starts only after all physical lines have been built.

Interface

class TerminalDocumentRenderer

Render a text::TextDocument tree as styled terminal output.

A reported writer width of at least 60 cells is used as-is. Widths from 1 through 59, unknown widths, and zero widths use an 80-column layout; a narrow destination may wrap the completed physical lines naturally. The complete document layout is materialized before output begins, so allocation or layout failures do not partially write the document.

Public Functions

TerminalDocumentRenderer()

Create a renderer with the plain default style.

explicit TerminalDocumentRenderer(TerminalDocumentStyle style)

Create a renderer with the given style sheet.

Parameters:

style – The document style to use.

inline const TerminalDocumentStyle &style() const noexcept

Access the current document style.

Returns:

The current document style.

void setStyle(TerminalDocumentStyle style) noexcept

Replace the current document style.

Parameters:

style – The new document style.

void renderTo(CursorWriter &writer, const text::TextDocument &document) const

Render a document to a cursor writer.

The writer receives one completed styled line followed by one line break for every physical document line.

Parameters:
  • writer – The output writer.

  • document – The document to render.

Throws:

std::bad_alloc – If intermediate layout storage cannot be allocated. The writer remains unchanged.

void renderTo(CursorWriter &writer, const text::TextNode &node) const

Render a node tree to a cursor writer.

The writer receives one completed styled line followed by one line break for every physical document line.

Parameters:
  • writer – The output writer.

  • node – The root node to render.

Throws:

std::bad_alloc – If intermediate layout storage cannot be allocated. The writer remains unchanged.

class TerminalDocumentStyle

A selector-driven terminal document style sheet.

Public Types

enum class Predefined : uint8_t

Shared predefined style variants.

Values:

enumerator Plain

The plain built-in style.

enumerator Simple

A compact colored style.

enumerator Styled

A more decorative colored style.

enumerator SystemOutput

The default style for application system output.

Public Functions

TerminalDocumentStyle()

Create the default plain document style.

inline BlockStyle baseTextStyle() const noexcept

Get the base text style.

void setBaseTextStyle(BlockStyle style)

Replace the base text style.

Parameters:

style – The new base text style.

inline const ParagraphIndents &baseBlockLayout() const noexcept

Get the base block layout.

void setBaseBlockLayout(ParagraphIndents layout)

Replace the base block layout.

Parameters:

layout – The new base block layout.

std::optional<std::reference_wrapper<const TerminalDocumentStyleRule>> definition(const TerminalDocumentStyleSelector &selector) const noexcept

Access one exact stored rule definition.

Parameters:

selector – The selector to query.

Returns:

The stored rule, if present.

TerminalDocumentStyleRule &edit(const TerminalDocumentStyleSelector &selector)

Create or access one exact stored rule definition.

New definitions are initialized from the currently resolved rule for the same selector.

Parameters:

selector – The selector to edit.

Returns:

A mutable reference to the stored rule.

void erase(const TerminalDocumentStyleSelector &selector) noexcept

Remove one exact stored rule definition.

Parameters:

selector – The selector to erase.

auto resolve(const TerminalDocumentStyleSelector &selector, const TerminalDocumentStyleSelector::TokenList &contextTokens = {}, const std::vector<text::TextNodeType> &ancestors = {}) const -> TerminalDocumentStyleRule

Resolve the best matching rule.

Parameters:
  • selector – The requested selector.

  • contextTokens – Additional style tokens from the current node.

  • ancestors – Node types in the parent chain, from root to direct parent.

Returns:

The resolved rule.

Public Static Functions

static const TerminalDocumentStyle &defaultStyle(Predefined predefined = Predefined::Plain) noexcept

Access a predefined default style.

Parameters:

predefined – The predefined style variant.

static const TerminalDocumentStyle &defaultPlain() noexcept

Access the plain default style.

static const TerminalDocumentStyle &defaultSimple() noexcept

Access the simple default style.

static const TerminalDocumentStyle &defaultStyled() noexcept

Access the styled default style.

static const TerminalDocumentStyle &defaultSystemOutput() noexcept

Access the application system-output default style.

class TerminalDocumentStyleMarker

A visible marker used for bullet and numbered terminal document list items.

Public Types

enum class Kind : uint8_t

Marker rendering mode.

Values:

enumerator None

No visible marker.

enumerator Literal

Use the literal marker text.

enumerator Ordered

Render the item number followed by the suffix.

Public Functions

TerminalDocumentStyleMarker() = default

Create an empty marker.

inline Kind kind() const noexcept

Get the marker kind.

inline BlockStyle style() const noexcept

Get the marker style overlay.

inline BlockString literal() const noexcept

Get the literal marker text.

inline BlockString suffix() const noexcept

Get the ordered-list suffix text.

TerminalDocumentStyleMarker &clear() noexcept

Clear the marker.

Returns:

Reference to this marker.

TerminalDocumentStyleMarker &setStyle(BlockStyle style) noexcept

Set the marker style overlay.

Parameters:

style – The marker style overlay.

Returns:

Reference to this marker.

TerminalDocumentStyleMarker &setLiteral(BlockString literal, BlockStyle style = {}) noexcept

Use a literal marker string.

Parameters:
  • literal – The literal marker text.

  • style – Optional marker style overlay.

Returns:

Reference to this marker.

TerminalDocumentStyleMarker &setLiteral(const text::U32String &literal, BlockStyle style = {})

Use a literal marker string.

Parameters:
  • literal – The literal marker text.

  • style – Optional marker style overlay.

Returns:

Reference to this marker.

TerminalDocumentStyleMarker &setLiteral(const text::String &literal, BlockStyle style = {})

Use a literal marker string.

Parameters:
  • literal – The literal marker text.

  • style – Optional marker style overlay.

Returns:

Reference to this marker.

TerminalDocumentStyleMarker &setOrdered()

Use an ordered marker with the default suffix and style.

Returns:

Reference to this marker.

TerminalDocumentStyleMarker &setOrdered(BlockString suffix, BlockStyle style = {})

Use an ordered marker with the given suffix.

Parameters:
  • suffix – The suffix appended after the item number.

  • style – Optional marker style overlay.

Returns:

Reference to this marker.

TerminalDocumentStyleMarker &setOrdered(const text::U32String &suffix, BlockStyle style = {})

Use an ordered marker with the given suffix.

Parameters:
  • suffix – The suffix appended after the item number.

  • style – Optional marker style overlay.

Returns:

Reference to this marker.

BlockString render(std::size_t number, BlockStyle baseStyle) const

Render this marker for one list item number.

Parameters:
  • number – The one-based item number for ordered lists.

  • baseStyle – The base text style used for marker text.

Returns:

The rendered marker.

class TerminalDocumentStyleRule

One terminal document style rule with text, layout, and decoration settings.

Public Functions

TerminalDocumentStyleRule() = default

Create an empty style rule.

inline BlockStyle textStyle() const noexcept

Get the text style overlay.

inline const ParagraphIndents &indents() const noexcept

Get the paragraph indents and margins.

inline const block::Margins &margins() const noexcept

Get the margins around the block.

std::optional<BlockString> prefix() const noexcept

Get the optional prefix.

std::optional<BlockString> suffix() const noexcept

Get the optional suffix.

std::optional<BlockString> linePrefix() const noexcept

Get the optional prefix applied to every content line of a container.

inline const std::optional<Block> &lineFill() const noexcept

Get the optional line fill character.

inline TerminalDocumentStyleMarker &marker() noexcept

Get the marker configuration.

inline const TerminalDocumentStyleMarker &marker() const noexcept

Get the marker configuration.

TerminalDocumentStyleRule &setTextStyle(BlockStyle style) noexcept

Replace the text style overlay.

Parameters:

style – The new text style overlay.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setTextStyle(Color color, BlockAttributes attributes = {}) noexcept

Replace the text style overlay.

Parameters:
  • color – The new color overlay.

  • attributes – The new attribute overlay.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setIndents(ParagraphIndents indents) noexcept

Replace the indents and margins.

Parameters:

indents – The new indents and margins.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setMargins(block::Margins margins) noexcept

Replace the margins.

Parameters:

margins – The new margins.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setMargins(int allSides) noexcept

Replace all margins with one value.

Parameters:

allSides – The value for all sides.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setMargins(int horizontal, int vertical) noexcept

Replace horizontal and vertical margins.

Parameters:
  • horizontal – The left and right margins.

  • vertical – The top and bottom margins.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setMargins(int top, int right, int bottom, int left) noexcept

Replace each margin side.

Parameters:
  • top – Top margin.

  • right – Right margin.

  • bottom – Bottom margin.

  • left – Left margin.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setLineIndent(int indent) noexcept

Set the shared line indent.

Parameters:

indent – The new line indent.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setFirstLineIndent(int indent) noexcept

Set the first-line indent.

Parameters:

indent – The new first-line indent.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setWrappedLineIndent(int indent) noexcept

Set the wrapped-line indent.

Parameters:

indent – The new wrapped-line indent.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setPrefix(BlockString prefix) noexcept

Set the optional prefix.

Parameters:

prefix – The prefix text.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setPrefix(const text::U32String &prefix, BlockStyle style = {})

Set the optional prefix.

Parameters:
  • prefix – The prefix text.

  • style – The prefix style.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setPrefix(const text::String &prefix, BlockStyle style = {})

Set the optional prefix.

Parameters:
  • prefix – The prefix text.

  • style – The prefix style.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &clearPrefix() noexcept

Remove the prefix.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setSuffix(BlockString suffix) noexcept

Set the optional suffix.

Parameters:

suffix – The suffix text.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setSuffix(const text::U32String &suffix, BlockStyle style = {})

Set the optional suffix.

Parameters:
  • suffix – The suffix text.

  • style – The suffix style.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setSuffix(const text::String &suffix, BlockStyle style = {})

Set the optional suffix.

Parameters:
  • suffix – The suffix text.

  • style – The suffix style.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &clearSuffix() noexcept

Remove the suffix.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setLinePrefix(BlockString prefix) noexcept

Set the prefix applied to every content line of a container.

TerminalDocumentStyleRule &setLinePrefix(const text::U32String &prefix, BlockStyle style = {})

Set the styled prefix applied to every content line of a container.

TerminalDocumentStyleRule &setLinePrefix(const text::String &prefix, BlockStyle style = {})

Set the styled prefix applied to every content line of a container.

TerminalDocumentStyleRule &clearLinePrefix() noexcept

Remove the per-line prefix.

TerminalDocumentStyleRule &setLineFill(Block fill) noexcept

Set the line fill character.

Parameters:

fill – The fill character.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setLineFill(text::Char codePoint, BlockStyle style = {}) noexcept

Set the line fill character.

Parameters:
  • codePoint – The fill code point.

  • style – The fill style.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &clearLineFill() noexcept

Remove the line fill character.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setMarker(TerminalDocumentStyleMarker marker) noexcept

Replace the marker.

Parameters:

marker – The marker configuration.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setLiteralMarker(BlockString literal, BlockStyle style = {})

Configure a literal marker.

Parameters:
  • literal – The marker text.

  • style – Optional marker style.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setLiteralMarker(const text::U32String &literal, BlockStyle style = {})

Configure a literal marker.

Parameters:
  • literal – The marker text.

  • style – Optional marker style.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setOrderedMarker(BlockString suffix, BlockStyle style = {})

Configure an ordered marker.

Parameters:
  • suffix – The marker suffix.

  • style – Optional marker style.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &setOrderedMarker()

Configure the default ordered marker.

Returns:

Reference to this rule.

TerminalDocumentStyleRule &clearMarker() noexcept

Clear the marker.

Returns:

Reference to this rule.

class TerminalDocumentStyleSelector

A selector used to define or request one terminal document style rule.

Public Types

using TokenList = text::StringList

The normalized token list.

Public Functions

TerminalDocumentStyleSelector() noexcept = default

Create a paragraph selector.

inline explicit TerminalDocumentStyleSelector(text::TextNodeType nodeType) noexcept

Create a selector for the given text node type.

Parameters:

nodeType – The node type to match.

inline TerminalDocumentStyleSelector(text::TextNodeType nodeType, std::optional<int> level) noexcept

Create a selector with an optional level.

Parameters:
  • nodeType – The node type to match.

  • level – The optional heading or list nesting level.

TerminalDocumentStyleSelector(text::TextNodeType nodeType, std::initializer_list<text::String> requiredStyleTokens)

Create a selector with required style tokens.

Parameters:
  • nodeType – The node type to match.

  • requiredStyleTokens – Required style tokens parsed from TextNode::style().

TerminalDocumentStyleSelector(text::TextNodeType nodeType, std::optional<int> level, std::initializer_list<text::String> requiredStyleTokens)

Create a selector with an optional level and required style tokens.

Parameters:
  • nodeType – The node type to match.

  • level – The optional heading or list nesting level.

  • requiredStyleTokens – Required style tokens parsed from TextNode::style().

TerminalDocumentStyleSelector(text::TextNodeType nodeType, std::optional<int> level, std::initializer_list<text::String> requiredStyleTokens, std::optional<text::TextNodeType> ancestorType)

Create a selector with an optional ancestor constraint.

Parameters:
  • nodeType – The node type to match.

  • level – The optional heading or list nesting level.

  • requiredStyleTokens – Required style tokens.

  • ancestorType – A type that must occur in the parent chain.

inline bool operator==(const TerminalDocumentStyleSelector &other) const

Compare two selectors for exact identity.

inline bool operator!=(const TerminalDocumentStyleSelector &other) const

Compare two selectors for exact inequality.

inline text::TextNodeType nodeType() const noexcept

Get the node type to match.

inline const std::optional<int> &level() const noexcept

Get the optional heading or list nesting level.

inline const TokenList &requiredStyleTokens() const noexcept

Get the required style tokens.

inline const std::optional<text::TextNodeType> &ancestorType() const noexcept

Get the optional required ancestor type.

Public Static Functions

static inline TerminalDocumentStyleSelector node(text::TextNodeType nodeType) noexcept

Create a selector for a node type.

Parameters:

nodeType – The node type to match.

static inline TerminalDocumentStyleSelector document() noexcept

Create a document selector.

static inline TerminalDocumentStyleSelector section() noexcept

Create a section selector.

static inline TerminalDocumentStyleSelector paragraph() noexcept

Create a paragraph selector.

static inline TerminalDocumentStyleSelector heading(int level) noexcept

Create a heading selector.

Parameters:

level – The one-based heading level.

static inline TerminalDocumentStyleSelector bulletList(int level) noexcept

Create a bullet-list selector.

Parameters:

level – The zero-based nesting level.

static inline TerminalDocumentStyleSelector numberedList(int level) noexcept

Create a numbered-list selector.

Parameters:

level – The zero-based nesting level.

static inline TerminalDocumentStyleSelector bulletListItem(int level) noexcept

Create a bullet-list-item selector.

Parameters:

level – The zero-based nesting level.

static inline TerminalDocumentStyleSelector numberedListItem(int level) noexcept

Create a numbered-list-item selector.

Parameters:

level – The zero-based nesting level.

static inline TerminalDocumentStyleSelector definitionList() noexcept

Create a definition-list selector.

static inline TerminalDocumentStyleSelector definitionTerm() noexcept

Create a definition-term selector.

static inline TerminalDocumentStyleSelector definitionDescription() noexcept

Create a definition-description selector.

static inline TerminalDocumentStyleSelector fieldList() noexcept

Create a field-list selector.

static inline TerminalDocumentStyleSelector fieldItem() noexcept

Create a field-item selector.

static inline TerminalDocumentStyleSelector fieldLabel() noexcept

Create a field-label selector.

static inline TerminalDocumentStyleSelector fieldContent() noexcept

Create a field-content selector.

static inline TerminalDocumentStyleSelector blockquote() noexcept

Create a blockquote selector.

static inline TerminalDocumentStyleSelector codeBlock() noexcept

Create a code-block selector.

static inline TerminalDocumentStyleSelector horizontalLine() noexcept

Create a horizontal-line selector.

static inline TerminalDocumentStyleSelector emphasis() noexcept

Create an emphasis selector.

static inline TerminalDocumentStyleSelector strong() noexcept

Create a strong-text selector.

static inline TerminalDocumentStyleSelector underline() noexcept

Create an underline selector.

static inline TerminalDocumentStyleSelector span(std::initializer_list<text::String> requiredStyleTokens)

Create a span selector with required style tokens.

Parameters:

requiredStyleTokens – Required style tokens.

static inline TerminalDocumentStyleSelector link() noexcept

Create a link selector.

static inline TerminalDocumentStyleSelector code() noexcept

Create an inline-code selector.

static inline TerminalDocumentStyleSelector separator() noexcept

Create an inline-separator selector.

static inline TerminalDocumentStyleSelector escapeSequence() noexcept

Create an escape-sequence selector.

static inline auto descendantOf(text::TextNodeType nodeType, text::TextNodeType ancestorType, std::initializer_list<text::String> requiredStyleTokens = {}) -> TerminalDocumentStyleSelector

Create a selector constrained to descendants of the given type.

Parameters:
  • nodeType – The node type to match.

  • ancestorType – A type that must occur in the parent chain.

  • requiredStyleTokens – Required style tokens.

static void normalizeTokens(TokenList &tokens)

Normalize a token list by sorting and removing duplicates.

Parameters:

tokens – The tokens to normalize in-place.

static TokenList splitStyleTokens(const text::String &value)

Split and normalize a TextNode::style() value.

Parameters:

value – The raw style value.

Returns:

The normalized tokens.