richdoc

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2026 License: BSD-3-Clause Imports: 1 Imported by: 0

README

richdoc

A neutral, format-agnostic and widget-agnostic document model for rich text, written in pure Go (CGO-free).

richdoc is the foundation of a multi-format rich-document system: a WYSIWYG toolkit widget and converters for Markdown, LaTeX, ODT and RTF are all built on top of this one model. It intentionally does no rendering, parsing or I/O — it only defines the tree and a few small utilities to traverse, build, extract and copy it.

Model

A Document is an ordered slice of block nodes plus a format-agnostic map[string]string of metadata. Blocks and inlines are closed interface sets: each concrete type carries an unexported marker method, so consumers (converters especially) can type-switch over them exhaustively.

  • Blocks: Heading, Paragraph, List (ListItem), CodeBlock, BlockQuote, Table (Cell, Alignment), ThematicBreak, MathBlock, RawBlock.
  • Inlines: Text, Emph, Strong, Strikethrough, Code, Link, Image, Math, LineBreak, RawInline.

RawBlock/RawInline carry format-specific passthrough text for round-trip fidelity.

Utilities

  • Walk(d *Document, v Visitor) — depth-first traversal (Enter/Leave).
  • New() *Builder — fluent construction with inline/structural constructor helpers.
  • PlainText(d *Document) string — textual content for search and previews.
  • Clone(d *Document) *Document — deep copy for undo snapshots and rewrites.

Example

doc := richdoc.New().
	Meta("title", "Hello").
	H(1, richdoc.Txt("Hello")).
	P(
		richdoc.Bold(richdoc.Txt("bold")),
		richdoc.Txt(" and "),
		richdoc.Italic(richdoc.Txt("italic")),
	).
	UList(true,
		richdoc.Item(richdoc.Paragraph{Inlines: []richdoc.Inline{richdoc.Txt("first")}}),
		richdoc.Item(richdoc.Paragraph{Inlines: []richdoc.Inline{richdoc.Txt("second")}}),
	).
	Doc()

License

BSD-3-Clause. Copyright (c) the go-richdoc authors.

Documentation

Overview

Package richdoc defines a neutral, format-agnostic and widget-agnostic model for rich text documents.

The model is a typed tree, not a generic attribute bag. A Document is an ordered slice of Block nodes; blocks and inlines are closed interface sets (each concrete type carries an unexported marker method), so consumers such as converters and editor widgets can exhaustively type-switch over them.

The package is deliberately small and orthogonal. On top of the model it provides four utilities:

  • Walk with a Visitor performs a depth-first traversal.
  • Builder (via New) offers fluent, ergonomic construction.
  • PlainText extracts the textual content of a document.
  • Clone returns a deep copy.

The package has no dependencies beyond the standard library and is safe to build with CGO disabled.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PlainText

func PlainText(d *Document) string

PlainText returns the textual content of d with block-level nodes separated by newlines. It is intended for search, previews and tests, not for faithful rendering.

It concatenates the values of Text and Code inlines and of CodeBlock blocks, descending through every container (headings, lists, quotes, table cells, and emphasis-like inlines). A Footnote contributes its body text inline at the position it occurs, because footnotes are document text a search should find; an Anchor and a CrossRef contribute their visible inlines but not their identifiers. Nodes that carry no literal text in that sense contribute nothing: ThematicBreak, LineBreak, Image, Math, MathBlock, RawInline and RawBlock. It returns "" for a nil document.

func Walk

func Walk(d *Document, v Visitor)

Walk performs a depth-first traversal of d, driving v. It is a no-op when d is nil.

Types

type Alignment

type Alignment int

Alignment is the horizontal alignment of a table column.

const (
	AlignDefault Alignment = iota
	AlignLeft
	AlignCenter
	AlignRight
)

Column alignments. AlignDefault leaves the choice to the renderer.

type Anchor added in v0.2.0

type Anchor struct {
	ID      string
	Inlines []Inline
}

Anchor is a labeled target attached to inline content: the destination a CrossRef points at (a LaTeX \label, an ODF bookmark, a Markdown heading anchor target). ID is the label; Inlines is the content the label marks and may be empty for a point target that carries no visible text of its own.

func Mark added in v0.2.0

func Mark(id string, inlines ...Inline) Anchor

Mark builds an Anchor labeling the given inlines with id. The inlines may be omitted for a point target.

type Block

type Block interface {
	// contains filtered or unexported methods
}

Block is a top-level or nested block-level node. The set of concrete block types is closed: only types defined in this package satisfy Block, which lets consumers type-switch exhaustively.

type BlockQuote

type BlockQuote struct {
	Blocks []Block
}

BlockQuote is a quotation containing nested blocks.

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

Builder incrementally assembles a Document with a fluent, chainable API. Every method appends a top-level block and returns the receiver, and New starts an empty builder:

doc := richdoc.New().
	H(1, richdoc.Txt("Title")).
	P(richdoc.Bold(richdoc.Txt("bold")), richdoc.Txt(" and "), richdoc.Italic(richdoc.Txt("italic"))).
	Doc()

Inline and structural values are produced by the constructor helpers in this file (Txt, Bold, Italic, Item, Td, ...); the plain struct literals remain available for anything the helpers do not cover.

A Builder is not safe for concurrent use.

func New

func New() *Builder

New returns an empty Builder.

func (*Builder) Add

func (b *Builder) Add(blocks ...Block) *Builder

Add appends arbitrary pre-built blocks, an escape hatch for constructs the typed methods below do not cover.

func (*Builder) CodeBlock

func (b *Builder) CodeBlock(language, text string) *Builder

CodeBlock appends a CodeBlock with an optional language tag.

func (*Builder) Doc

func (b *Builder) Doc() *Document

Doc finalizes construction and returns the assembled document.

func (*Builder) H

func (b *Builder) H(level int, inlines ...Inline) *Builder

H appends a Heading of the given level (1..6).

func (*Builder) HR

func (b *Builder) HR() *Builder

HR appends a ThematicBreak.

func (*Builder) MathBlock

func (b *Builder) MathBlock(tex string) *Builder

MathBlock appends a display-math MathBlock carrying TeX source.

func (*Builder) Meta

func (b *Builder) Meta(key, value string) *Builder

Meta sets a metadata key/value pair (for example "title" or "author").

func (*Builder) OList

func (b *Builder) OList(start int, tight bool, items ...ListItem) *Builder

OList appends an ordered List starting at start (clamped to a minimum of 1).

func (*Builder) P

func (b *Builder) P(inlines ...Inline) *Builder

P appends a Paragraph.

func (*Builder) Quote

func (b *Builder) Quote(blocks ...Block) *Builder

Quote appends a BlockQuote wrapping the given blocks.

func (*Builder) RawBlock

func (b *Builder) RawBlock(format, text string) *Builder

RawBlock appends a RawBlock passthrough for the named format.

func (*Builder) Table

func (b *Builder) Table(align []Alignment, header []Cell, rows [][]Cell) *Builder

Table appends a Table with the given column alignments, header cells and rows. Any argument may be nil for an unaligned, headerless or empty table.

func (*Builder) UList

func (b *Builder) UList(tight bool, items ...ListItem) *Builder

UList appends an unordered List.

type Cell

type Cell struct {
	Inlines []Inline
	ColSpan int
	RowSpan int
}

Cell is a single table cell holding inline content.

ColSpan and RowSpan are the number of columns/rows this cell occupies. Zero, the default — what an existing Cell{Inlines: ...} literal or a Td call already produces, with no field for either — means the same as 1: an ordinary cell spanning nothing extra. A converter that has no notion of spanning cells at all (a plain CommonMark table, say) never needs to touch these fields to keep working correctly.

func Td

func Td(inlines ...Inline) Cell

Td builds a table Cell holding the given inlines.

type Code

type Code struct {
	Value string
}

Code is an inline code span. Value is the verbatim code.

func Mono

func Mono(value string) Code

Mono builds an inline Code span.

type CodeBlock

type CodeBlock struct {
	Language string
	Text     string
}

CodeBlock is a block of preformatted code. Language is an optional informational language tag (for example "go"); Text is the verbatim source including its internal newlines.

type CrossRef added in v0.2.0

type CrossRef struct {
	Target  string
	Kind    RefKind
	Inlines []Inline
}

CrossRef is a reference to an Anchor/label or a bibliographic citation. Target is the label or citation key it resolves to and Kind selects between the two. Inlines is the visible text; when it is empty the renderer or writer supplies the resolved number or label.

func Cite added in v0.2.0

func Cite(target string, inlines ...Inline) CrossRef

Cite builds a CrossRef of kind RefCite citing target. The inlines are the visible text and may be omitted for the writer to resolve.

func Ref added in v0.2.0

func Ref(target string, inlines ...Inline) CrossRef

Ref builds a CrossRef of kind RefLabel to target. The inlines are the visible text and may be omitted for the writer to resolve.

type Document

type Document struct {
	Blocks []Block
	Meta   map[string]string
}

Document is a rich text document: an ordered sequence of top-level blocks together with format-agnostic metadata (title, author, and similar).

Meta is an optional, unstructured string map; converters decide how to map its keys onto their target format. A nil Meta is valid and means "no metadata".

func Clone

func Clone(d *Document) *Document

Clone returns a deep copy of d: the returned document shares no mutable state (slices or maps) with the original, so either may be mutated independently. It returns nil for a nil document.

Editors use it for undo snapshots and converters for non-destructive rewrites.

type Emph

type Emph struct {
	Inlines []Inline
}

Emph is emphasized (conventionally italic) inline content.

func Italic

func Italic(inlines ...Inline) Emph

Italic builds an Emph (italic) inline.

type Footnote added in v0.2.0

type Footnote struct {
	Blocks []Block
}

Footnote is a footnote placed inline, whose content is block-level (LaTeX \footnote, an ODF footnote, a Markdown [^id] reference with its definition). Blocks holds the note body; it appears at the position the note is referenced, and a writer is free to relocate the body to the page or document end.

func Note added in v0.2.0

func Note(blocks ...Block) Footnote

Note builds a Footnote whose body is the given blocks.

type Heading

type Heading struct {
	Level   int
	ID      string
	Inlines []Inline
}

Heading is a section heading. Level is 1..6, following the common HTML/ Markdown convention (1 is the most prominent).

ID is an optional anchor identifier for the heading (a Markdown heading anchor, a LaTeX \section immediately followed by \label). An empty ID means the heading carries no explicit anchor.

type Image

type Image struct {
	URL   string
	Alt   string
	Title string
}

Image is an inline image reference. Alt is the textual alternative and Title an optional advisory title.

func Img

func Img(url, alt, title string) Image

Img builds an Image inline.

type Inline

type Inline interface {
	// contains filtered or unexported methods
}

Inline is an inline-level node. Like Block, the set of concrete inline types is closed, so consumers can type-switch exhaustively.

type LineBreak

type LineBreak struct{}

LineBreak is a hard line break within a block.

func Br

func Br() LineBreak

Br builds a hard LineBreak inline.

type Link struct {
	URL     string
	Title   string
	Inlines []Inline
}

Link is a hyperlink wrapping inline content. Title is an optional advisory title (for example a tooltip).

func Href

func Href(url, title string, inlines ...Inline) Link

Href builds a Link inline.

type List

type List struct {
	Ordered bool
	Start   int
	Tight   bool
	Items   []ListItem
}

List is an ordered or unordered list.

When Ordered is true, Start is the number of the first item (1 when unset). Tight indicates a list whose items should render without inter-item spacing, mirroring the CommonMark tight/loose distinction.

type ListItem

type ListItem struct {
	Blocks []Block
}

ListItem is a single entry of a List. Items hold blocks, which makes arbitrary nesting (paragraphs, sub-lists, quotes, ...) possible.

func Item

func Item(blocks ...Block) ListItem

Item builds a ListItem holding the given blocks.

type Math

type Math struct {
	TeX string
}

Math is inline mathematics, carrying its TeX source.

func InlineMath

func InlineMath(tex string) Math

InlineMath builds a Math inline carrying TeX source.

type MathBlock

type MathBlock struct {
	TeX string
}

MathBlock is display (block-level) mathematics, carrying its TeX source.

type Paragraph

type Paragraph struct {
	Inlines []Inline
}

Paragraph is a run of inline content forming a single logical paragraph.

type RawBlock

type RawBlock struct {
	Format string
	Text   string
}

RawBlock is a verbatim, format-specific block passthrough used to preserve round-trip fidelity for constructs the model does not represent natively. Format names the target format the Text belongs to (for example "latex" or "html"); a converter for a different format is free to drop it.

type RawInline

type RawInline struct {
	Format string
	Text   string
}

RawInline is a verbatim, format-specific inline passthrough used to preserve round-trip fidelity, analogous to RawBlock. Format names the target format the Text belongs to.

func RawI

func RawI(format, text string) RawInline

RawI builds a RawInline passthrough for the named format.

type RefKind added in v0.2.0

type RefKind int

RefKind distinguishes the two kinds of reference a CrossRef can be.

const (
	RefLabel RefKind = iota
	RefCite
)

Reference kinds. RefLabel is a cross-reference to a labeled target (LaTeX \ref/\eqref, a Markdown link to an internal id); RefCite is a citation of a bibliographic key (LaTeX \cite).

type Strikethrough

type Strikethrough struct {
	Inlines []Inline
}

Strikethrough is struck-out inline content.

func Strike

func Strike(inlines ...Inline) Strikethrough

Strike builds a Strikethrough inline.

type Strong

type Strong struct {
	Inlines []Inline
}

Strong is strongly emphasized (conventionally bold) inline content.

func Bold

func Bold(inlines ...Inline) Strong

Bold builds a Strong (bold) inline.

type Table

type Table struct {
	Align  []Alignment
	Header []Cell
	Rows   [][]Cell
}

Table is a simple grid with an optional header row.

Align gives the per-column alignment; a shorter Align slice leaves the remaining columns at AlignDefault. Header may be empty for a headerless table. Rows is a list of rows, each a slice of cells.

type Text

type Text struct {
	Value string
}

Text is a run of literal text.

func Txt

func Txt(value string) Text

Txt builds a Text inline.

type ThematicBreak

type ThematicBreak struct{}

ThematicBreak is a horizontal rule separating content.

type Visitor

type Visitor interface {
	Enter(node any) (descend bool)
	Leave(node any)
}

Visitor observes a depth-first traversal driven by Walk.

For every node Walk calls Enter before descending into that node's children and Leave once its subtree has been fully visited. Returning false from Enter skips the node's children (Leave is still called), which lets a visitor prune whole subtrees.

Nodes are passed as any. The concrete dynamic types are the Document (passed as *Document), every Block and Inline, and the structural ListItem and Cell containers, so a visitor can recover the full tree structure by type-switching.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL