mdpp

package module
v0.2.5 Latest Latest
Warning

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

Go to latest
Published: Apr 27, 2026 License: MIT Imports: 26 Imported by: 0

README

Markdown++

The only Markdown stack with a real grammar: the LSP, formatter, and linter understand your document instead of pattern-matching at it, and the output is a faithful artifact rather than a best-effort guess.

Markdown++ keeps .md files readable everywhere while adding the authoring tools Markdown has always needed: diagnostics, formatting, semantic highlighting, hover, completions, live preview, HTML rendering, and PDF export. The core is a Go package backed by gotreesitter, so every tool works from the same syntax tree with byte ranges.

Install

For authors, start with the VS Code extension:

VSIX release: https://github.com/odvcencio/mdpp-vscode/releases/tag/v0.1.10
Marketplace item: m31labs.markdown-plus-plus

Extension source lives at https://github.com/odvcencio/mdpp-vscode.

For the CLI and language server:

go install github.com/odvcencio/mdpp/cmd/mdpp@latest
go install github.com/odvcencio/mdpp/cmd/mdpp-lsp@latest

Pre-built binaries are published on GitHub Releases for macOS, Linux, and Windows.

CLI

Render HTML:

mdpp render README.md -o README.html

Export PDF:

mdpp render --format=pdf README.md -o README.pdf

Format and lint:

mdpp fmt --check README.md
mdpp lint README.md

Parse to JSON:

mdpp parse --json README.md

Go API

doc := mdpp.Parse([]byte(source))
html := mdpp.RenderString(source)

The package exposes the parser, renderer, diagnostics, formatter, linter inputs, table of contents, frontmatter, and source ranges under one module:

go get github.com/odvcencio/mdpp

What Ships

Area Support
Markdown compatibility CommonMark-style Markdown plus GFM tables, task lists, autolinks, and strikethrough.
Structured extensions Math, footnotes, admonitions, emoji shortcodes, diagram fences, frontmatter, [[toc]], and [[embed:url]].
Formatter Source-preserving canonical formatting for headings, lists, tables, directives, fences, references, and footnotes.
Linter Built-in Markdown++ rules with source ranges, fixes, and LSP diagnostics.
LSP Hover, definition, document symbols, formatting, completions, semantic tokens, code actions, and live-preview rendering.
Output HTML and PDF.

Why It Is Different

Most Markdown tooling can recognize shapes. Markdown++ works with syntax nodes.

That matters when a document contains nested lists, code fences, math, HTML, footnotes, diagrams, and links that all reuse the same punctuation. A regex can find [ref]; a parsed document can tell whether it is link text, a reference label, a footnote, code, escaped text, or an HTML attribute.

That is what makes these features practical:

  • Rename or navigate headings, footnotes, and references without touching lookalikes in code blocks.
  • Format source while preserving code, math, HTML, YAML values, and diagram bodies byte-for-byte.
  • Highlight meaning, not just punctuation.
  • Render a live preview from the same AST used by diagnostics and formatting.
  • Export HTML and PDF from the same document model.

Example

# Research Notes

[[toc]]

> [!TIP] Keep the source plain. Let the tools handle structure.

```mermaid
flowchart TD
  Hypothesis --> Experiment
  Experiment --> Results
```

See [the appendix][appendix] and note[^1].

[^1]: Footnotes are part of the AST.

[appendix]: https://example.com

Diagram fences are parsed as document structure and rendered safely by default.

License

MIT

Documentation

Overview

Package mdpp parses and renders Markdown++ documents.

MDPP keeps Markdown-compatible prose as the base syntax and adds structured nodes for technical writing features such as frontmatter, admonitions, footnotes, math, emoji shortcodes, task lists, and diagram fences. The parser is backed by gotreesitter and returns an AST that can be used by renderers, editors, formatters, diagnostics, and language service features.

Index

Constants

View Source
const SpecVersion = "0.1"

SpecVersion is the Markdown++ format version implemented by this package.

View Source
const Version = "0.1.10"

Version is the Markdown++ engine version.

Variables

This section is empty.

Functions

func EmojiShortcodes added in v0.1.10

func EmojiShortcodes() []string

EmojiShortcodes returns the known emoji shortcode names in stable order.

func GrammarBlobHandler

func GrammarBlobHandler() http.Handler

GrammarBlobHandler returns an HTTP handler that serves gotreesitter grammar blobs on demand. Mount it at /grammars/ and any browser-side WASM module can fetch language grammars one at a time.

URL pattern: /grammars/{language}.blob (e.g. /grammars/go.blob)

func Render added in v0.1.10

func Render(doc *Document, opts RenderOptions) ([]byte, error)

Render produces HTML for a parsed document.

func RenderPDF added in v0.1.10

func RenderPDF(doc *Document, opts PDFOptions) ([]byte, error)

RenderPDF renders a document to PDF using headless Chrome.

func RenderString

func RenderString(source string) string

RenderString is a package-level convenience that parses and renders Markdown to HTML with default settings.

func Slugify added in v0.1.10

func Slugify(s string) string

Slugify converts heading text into the renderer's auto-generated id.

Types

type Diagnostic added in v0.1.10

type Diagnostic struct {
	Code     string
	Severity Severity
	Message  string
	Range    Range
}

Diagnostic is a recoverable parser finding attached to a source range.

type Document

type Document struct {
	Root   *Node
	Source []byte
	// contains filtered or unexported fields
}

Document is the top-level result of parsing a Markdown source.

func MustParse added in v0.1.10

func MustParse(source []byte) *Document

MustParse parses Markdown source and panics only if Parse returns an error.

func Parse

func Parse(source []byte) (doc *Document, err error)

Parse parses Markdown source into a Document AST.

func ParseIncremental added in v0.2.5

func ParseIncremental(source []byte, prevTree *gotreesitter.Tree, edit gotreesitter.InputEdit) (doc *Document, tree *gotreesitter.Tree, err error)

ParseIncremental re-parses source by applying edit to prevTree and running the tree-sitter incremental parser. Callers must have produced prevTree from a prior ParseWithTree (or prior ParseIncremental) call against the pre-edit source. ParseIncremental takes ownership of prevTree and returns a fresh *Tree that the caller must eventually Release.

If the edit-applied source no longer satisfies the tree-sitter primary path (e.g. it now matches a container/all-indented/deep-nested fallback) this function falls back to a full parse and the returned Tree may be nil.

func ParseWithTree added in v0.2.5

func ParseWithTree(source []byte) (doc *Document, tree *gotreesitter.Tree, err error)

ParseWithTree parses source and also returns the underlying tree-sitter Tree if the tree-sitter path was used. The returned *Tree is non-nil only when the primary tree-sitter path (not a fallback) produced the document. Callers that receive a non-nil Tree must eventually call tree.Release() (or feed it back into ParseIncremental, which takes ownership).

func (*Document) AST added in v0.1.10

func (d *Document) AST() *Node

AST returns the root node.

func (*Document) Diagnostics added in v0.1.10

func (d *Document) Diagnostics() []Diagnostic

Diagnostics returns recoverable parse diagnostics.

func (*Document) FormatVersion added in v0.1.10

func (d *Document) FormatVersion() string

FormatVersion returns the frontmatter mdpp version, if declared.

func (*Document) Frontmatter

func (d *Document) Frontmatter() map[string]any

Frontmatter returns the parsed frontmatter metadata, if any.

func (*Document) Headings

func (d *Document) Headings() []Heading

Headings returns all headings in document order with their level, text, and a slugified ID.

func (*Document) ReadingTime

func (d *Document) ReadingTime() time.Duration

ReadingTime estimates how long it takes to read the document at 200 words per minute. Returns a minimum of 1 minute if the document has any words.

func (*Document) TableOfContents

func (d *Document) TableOfContents() []TOCEntry

TableOfContents returns a table of contents derived from all headings in the document.

func (*Document) WordCount

func (d *Document) WordCount() int

WordCount returns the number of words in the document's prose content, excluding code and diagram blocks.

type HeaderFooterTemplate added in v0.1.10

type HeaderFooterTemplate struct{ HeaderHTML, FooterHTML string }

HeaderFooterTemplate contains chromedp-compatible header/footer HTML.

type Heading

type Heading struct {
	Level int
	Text  string
	ID    string
}

Heading represents a heading found in the document.

type Margins added in v0.1.10

type Margins struct{ Top, Right, Bottom, Left float64 }

Margins holds PDF page margins in inches.

type MathOption added in v0.1.10

type MathOption int

MathOption selects how math nodes render.

const (
	MathServer MathOption = iota
	MathRaw
	MathOmit
)

type Node

type Node struct {
	Type     NodeType
	Children []*Node
	Literal  string
	Attrs    map[string]string
	Range    Range
}

Node is a single element in the Markdown AST.

func (*Node) Attr added in v0.1.10

func (n *Node) Attr(key string) string

Attr returns an attribute value or "" if absent.

func (*Node) Find added in v0.1.10

func (n *Node) Find(typ NodeType) []*Node

Find returns descendants, including this node, with the requested type.

func (*Node) HasAttr added in v0.1.10

func (n *Node) HasAttr(key string) bool

HasAttr reports whether an attribute key is present.

func (*Node) Level added in v0.1.10

func (n *Node) Level() int

Level returns a heading level, or 0 for non-heading nodes.

func (*Node) Text added in v0.1.10

func (n *Node) Text() string

Text returns collected plain text for this node.

func (*Node) Walk added in v0.1.10

func (n *Node) Walk(visit func(n *Node) bool)

Walk visits this node and descendants in pre-order.

type NodeType

type NodeType int

NodeType classifies an AST node.

const (
	NodeDocument NodeType = iota
	NodeHeading
	NodeParagraph
	NodeCodeBlock
	NodeBlockquote
	NodeList
	NodeListItem
	NodeTable
	NodeTableRow
	NodeTableCell
	NodeThematicBreak
	NodeLink
	NodeImage
	NodeEmphasis
	NodeStrong
	NodeStrikethrough
	NodeCodeSpan
	NodeText
	NodeSoftBreak
	NodeHardBreak
	NodeHTMLBlock
	NodeHTMLInline
	NodeFootnoteRef
	NodeFootnoteDef
	NodeMathInline
	NodeMathBlock
	NodeAdmonition
	NodeDefinitionList
	NodeDefinitionTerm
	NodeDefinitionDesc
	NodeSuperscript
	NodeSubscript
	NodeTaskListItem
	NodeFrontmatter
	NodeTableOfContents
	NodeAutoEmbed
	NodeEmoji
	NodeDiagram
	NodeContainerDirective
)

func (NodeType) String added in v0.1.10

func (t NodeType) String() string

String returns the stable public name of a NodeType.

type Option

type Option func(*Renderer)

Option configures a Renderer.

func WithContainerRenderer added in v0.1.10

func WithContainerRenderer(fn func(c *Node, body string) string) Option

WithContainerRenderer sets a custom renderer for container directives.

func WithHardWraps

func WithHardWraps(enabled bool) Option

WithHardWraps makes single newlines render as <br> instead of whitespace.

func WithHeadingIDs

func WithHeadingIDs(enabled bool) Option

WithHeadingIDs enables or disables automatic id attributes on headings.

func WithHighlightCode

func WithHighlightCode(enabled bool) Option

WithHighlightCode enables or disables code highlighting placeholders.

func WithImageResolver

func WithImageResolver(fn func(string) string) Option

WithImageResolver sets a function to resolve image URLs.

func WithSourcePositions added in v0.1.10

func WithSourcePositions(enabled bool) Option

WithSourcePositions emits data-mdpp-source-* attributes on rendered elements.

func WithUnsafeHTML

func WithUnsafeHTML(enabled bool) Option

WithUnsafeHTML enables or disables raw HTML passthrough.

func WithWrapEmoji

func WithWrapEmoji(enabled bool) Option

WithWrapEmoji wraps emoji output in an accessible <span> with role="img" and aria-label.

type PDFOptions added in v0.1.10

type PDFOptions struct {
	PaperSize         PaperSize
	PaperWidthInches  float64
	PaperHeightInches float64
	MarginInches      Margins
	UserCSS           string
	Background        bool
	HeaderFooter      HeaderFooterTemplate
	RenderOptions     RenderOptions
	BrowserURL        string
	Timeout           time.Duration
	SettleDelay       time.Duration
}

PDFOptions configures RenderPDF.

type PaperSize added in v0.1.10

type PaperSize int

PaperSize is a built-in PDF paper size.

const (
	PaperLetter PaperSize = iota
	PaperA4
	PaperLegal
	PaperCustom
)

type Parser added in v0.2.5

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

Parser holds per-document state that survives across ParseIncremental calls, enabling reuse of expensive work (paragraph inline reparse, container body chunk parses) for unchanged regions.

A Parser is safe for sequential use by a single caller; concurrent Parse calls on the same Parser are not supported. Typical usage is to retain one Parser per open document in an LSP-like context.

func NewParser added in v0.2.5

func NewParser() *Parser

NewParser returns a fresh Parser with an empty cache and no retained tree.

func (*Parser) ApplyEdit added in v0.2.5

func (p *Parser) ApplyEdit(edit gotreesitter.InputEdit)

ApplyEdit applies an InputEdit to the retained tree without reparsing. Use this when multiple edits must be applied before a single incremental parse. ParseIncremental already applies the passed edit internally, so use this only for secondary edits that precede a ParseIncremental call.

func (*Parser) Close added in v0.2.5

func (p *Parser) Close()

Close releases any retained tree-sitter state.

func (*Parser) LastStats added in v0.2.5

func (p *Parser) LastStats() (int, int)

LastStats returns (cacheHits, cacheMisses) recorded by the most recent Parse or ParseIncremental call. Useful for observability and tests.

func (*Parser) Parse added in v0.2.5

func (p *Parser) Parse(source []byte) (*Document, error)

Parse runs a full parse, replacing any retained incremental state.

func (*Parser) ParseIncremental added in v0.2.5

func (p *Parser) ParseIncremental(source []byte, edit gotreesitter.InputEdit) (*Document, error)

ParseIncremental applies edit to the retained tree (if any) and runs an incremental parse, reusing cached AST subtrees for unchanged content. If no tree is retained (first parse or prior fallback), it runs a full parse.

type Range added in v0.1.10

type Range struct {
	StartByte int
	EndByte   int
	StartLine int
	StartCol  int
	EndLine   int
	EndCol    int
}

Range identifies a node's byte span in Document.Source. Lines and columns are 1-indexed; columns are byte columns.

type RenderOptions added in v0.1.10

type RenderOptions struct {
	HighlightCode     bool
	HeadingIDs        bool
	UnsafeHTML        bool
	HardWraps         bool
	WrapEmoji         bool
	ImageResolver     func(src string) string
	ContainerRenderer func(c *Node, body string) string
	SourcePositions   bool
	Math              MathOption
	Sanitize          bool
}

RenderOptions is the value-typed rendering API used by the CLI and tools.

type Renderer

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

Renderer converts Markdown source into HTML.

func NewRenderer

func NewRenderer(opts ...Option) *Renderer

NewRenderer creates a Renderer with the given options.

func (*Renderer) Parse

func (r *Renderer) Parse(source []byte) *Document

Parse parses Markdown source into a Document using the package-level parser.

func (*Renderer) Render

func (r *Renderer) Render(doc *Document) string

Render converts a parsed Document AST into an HTML string.

func (*Renderer) RenderString

func (r *Renderer) RenderString(source string) string

RenderString parses and renders a Markdown string to HTML.

type Severity added in v0.1.10

type Severity int

Severity classifies parse diagnostics.

const (
	SeverityInfo Severity = iota
	SeverityWarning
	SeverityError
)

type TOCEntry

type TOCEntry struct {
	Level int
	ID    string
	Text  string
}

TOCEntry represents a single table-of-contents heading.

Directories

Path Synopsis
cmd
mdpp command
mdpp-lsp command
Package fmt provides canonical formatting for Markdown++ source.
Package fmt provides canonical formatting for Markdown++ source.
internal
Package lint provides diagnostics over Markdown++ documents.
Package lint provides diagnostics over Markdown++ documents.

Jump to

Keyboard shortcuts

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