kramdown

package module
v0.0.0-...-25a62e4 Latest Latest
Warning

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

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

README

go-ruby-kramdown/kramdown

go-ruby-kramdown/kramdown

Pure-Go (CGO=0), MRI-faithful reimplementation of the Ruby kramdown Markdown-to-HTML converter.

CI Coverage Conformance Go Reference

About

go-ruby-kramdown renders the kramdown dialect of Markdown to HTML. Across the shared kramdown test corpus, 196 of 198 cases (98.99%) render byte-exact against the Ruby kramdown 2.5.2 gem — see Conformance & testing for the measured number and the short list of what is not yet supported. It is a member of the go-ruby-* family of pure-Go Ruby modules that go-embedded-ruby (rbgo) binds as native modules — there is no cgo and no external process: the converter is a self-contained Go package that cross-compiles to every Go target.

Install

go get github.com/go-ruby-kramdown/kramdown

Usage

package main

import (
	"fmt"

	"github.com/go-ruby-kramdown/kramdown"
)

func main() {
	html := kramdown.ToHTML("# Hello *kramdown*\n\nA paragraph with a footnote.[^1]\n\n[^1]: the note.\n", nil)
	fmt.Print(html)
}

For finer control, parse into a *Document and inspect warnings:

doc := kramdown.New(src, &kramdown.Options{AutoIds: true, SmartQuotes: true})
html := doc.ToHTML()
for _, w := range doc.Warnings {
	// e.g. an undefined footnote reference
	fmt.Println("warning:", w)
}

ToHTML(src, nil) uses DefaultOptions(), which mirrors kramdown's own defaults (AutoIds, SmartQuotes, Typographic, HardWrap all on; footnotes numbered from 1).

Options

Field Default Meaning
AutoIds true Assign a generated id="" to headers lacking an explicit {#id}.
AutoIdPrefix "" Prefix prepended to every auto-generated header id.
SmartQuotes true Curly quotes / apostrophes via the SmartQuotes substitution.
Typographic true --→en-dash, ---→em-dash, ...→ellipsis, << >>→guillemets.
HardWrap true Trailing two-spaces → <br />; when off, only \\ forces a break.
FootnoteNr 1 Starting number for footnotes.

Supported syntax

Area Coverage
Headers ATX (#) + Setext (===/---), explicit {#id} and auto-ids
Blocks Paragraphs, blockquotes, horizontal rules, indented + fenced code (language class)
Lists Unordered, ordered, definition lists; lazy and nested
Tables Pipe tables with per-column alignment
Inline *em*/**strong**, `code`, links & images (inline / reference / with attrs), autolinks
Footnotes [^id] references + definitions, with back-links and ordering
Attributes Inline Attribute Lists {:.class #id key="v"}, ALDs, {::comment} / span IALs
Abbreviations *[HTML]: ... definitions applied to matching text
Typography Smart quotes, --/---/.../<< >> substitutions
HTML Raw inline + block HTML passthrough; entity and backslash escapes

Edge cases deliberately outside the common feature set are documented in the tests.

Conformance & testing

A differential oracle compares output to the real kramdown gem where it is installed. Against the shared kramdown test corpus, 196 of 198 cases (98.99%) render byte-for-byte identically to the Ruby kramdown 2.5.2 gem. A shrink-only ratchet locks this in: a case outside the known-failing ledger that stops matching fails CI, and a ledgered case that starts matching must be graduated out. The deterministic, ruby-free tests alone hold 100% statement coverage, so the no-ruby, Windows, and qemu CI lanes stay green. The package is verified on Linux/macOS/Windows and cross-tested on amd64, arm64, riscv64, loong64, ppc64le, and s390x.

Not yet supported (the remaining 2 corpus cases)

go-ruby-kramdown is not spec-complete. The two cases that do not yet match the gem both depend on a Ruby-runtime library the pure-Go port cannot supply:

  • Rouge syntax-highlighted code blocks (2 cases) — the common Rouge paths are wired through the pure-Go highlighter, but rouge/simple needs a PHP lexer (not yet shipped) and rouge/multiple selects a bespoke formatter defined inside kramdown's own Ruby test harness, which no pure-Go wiring can supply.
Already done

The port covers a large body of kramdown behaviour, including a full port of kramdown's Parser::Html raw-HTML front-end and html_to_native conversion, table-of-contents generation, the HTML named-entity table, footnotes with back-links and ordering, smart quotes and typographic substitutions, header options with auto-ids and transliterated IDs, and CJK line-break handling.

License

BSD-3-Clause — see LICENSE. Copyright (c) the go-ruby-kramdown/kramdown authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package kramdown is a pure-Go (CGO-free) reimplementation of Ruby's kramdown Markdown-to-HTML converter — the parser and HTML renderer that back Kramdown::Document.new(src, options).to_html. It parses the kramdown dialect (a superset of Markdown: ATX/Setext headers with inline-attribute lists, blockquotes, fenced and indented code, ordered/unordered/definition lists, tables with alignment, footnotes, abbreviations, smart-quote typography, block and span IALs/ALDs, the {::comment} extension, …) into an element tree and renders the gem's HTML byte-for-byte on the common feature set — with no Ruby runtime.

The value model is deliberately small: a source string in, an HTML string out, plus an options hash. The intermediate element tree (Element) mirrors kramdown's own AST (a type, a value, attributes and children) so a host (such as go-embedded-ruby) can bind Kramdown::Document / Kramdown::Element directly onto it.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ToHTML

func ToHTML(src string, opts *Options) string

ToHTML is the one-shot convenience entry point: it parses src under opts and returns the HTML, equivalent to Kramdown::Document.new(src, options).to_html.

Types

type Attr

type Attr struct {
	Name string
	Val  string
}

Attr is one HTML attribute (name/value), kept ordered as kramdown emits them.

type Document

type Document struct {
	Root     *Element
	Opts     Options
	Warnings []string
	// contains filtered or unexported fields
}

Document is a parsed kramdown source, the analogue of Kramdown::Document. It holds the element [Root], the resolved [Opts], and the [Warnings] accumulated while parsing (e.g. an undefined footnote reference), and renders HTML via ToHTML.

func New

func New(src string, opts *Options) *Document

New parses src under opts (nil selects DefaultOptions) and returns the parsed Document, mirroring Kramdown::Document.new(src, options). Parsing never fails; malformed constructs degrade to literal text exactly as kramdown does.

func (*Document) ToHTML

func (d *Document) ToHTML() string

ToHTML renders the document to HTML, matching Kramdown::Document#to_html. Span parsing happens here, so any warnings it raises (e.g. an undefined footnote reference) are folded into Document.Warnings before returning.

type Element

type Element struct {
	Type     ElementType
	Value    string
	Children []*Element
	Attrs    []Attr
	Options  map[string]any
}

Element is a node in the kramdown element tree. Type selects the node kind, Value carries literal text for leaf nodes, Children holds nested elements, Attrs holds rendered HTML attributes in emission order, and Options carries parser-internal metadata (header level, list tightness, table alignments, …).

type ElementType

type ElementType int

ElementType enumerates the kinds of node in the kramdown element tree. The set mirrors the subset of Kramdown::Element types this converter produces.

const (
	// ElRoot is the document root; its Children are the top-level blocks.
	ElRoot ElementType = iota
	// ElBlank is a run of one or more blank lines between blocks.
	ElBlank
	// ElP is a paragraph; its Children are span elements.
	ElP
	// ElHeader is an ATX or Setext header; Value is unused, Options["level"] is the
	// level (1..6) and Options["raw_text"] the source used for auto-ids.
	ElHeader
	// ElBlockquote is a blockquote; Children are nested blocks.
	ElBlockquote
	// ElCodeblock is a fenced or indented code block; Value holds the literal text.
	ElCodeblock
	// ElHR is a horizontal rule.
	ElHR
	// ElUL / ElOL are unordered / ordered lists; Children are ElLI.
	ElUL
	// ElOL is an ordered list.
	ElOL
	// ElLI is a list item; Children are nested blocks (or a single bare paragraph
	// whose <p> wrapper is elided when the item is "tight").
	ElLI
	// ElDL is a definition list; Children are ElDT / ElDD.
	ElDL
	// ElDT is a definition term.
	ElDT
	// ElDD is a definition description.
	ElDD
	// ElTable is a table; Children are ElThead / ElTbody.
	ElTable
	// ElThead / ElTbody / ElTfoot / ElTr / ElTd structure a table.
	ElThead
	// ElTbody is a table body.
	ElTbody
	// ElTfoot is a table footer section.
	ElTfoot
	// ElTr is a table row.
	ElTr
	// ElTd is a table cell (a <td> or, in a thead, a <th>).
	ElTd
	// ElComment is a {::comment} extension block; Value holds the comment text.
	ElComment
	// ElRaw is a {::nomarkdown} extension block; Value holds the verbatim content
	// and Options["types"] the target-format filter ([] means all formats).
	ElRaw
	// ElFootnoteDef collects a footnote definition's blocks (never rendered inline).
	ElFootnoteDef
	// ElMath is a block-level "$$…$$" math element; Value holds the LaTeX source.
	// With kramdown's default (MathJax) engine it renders as "\[…\]".
	ElMath

	// ElText is literal text; Value holds it.
	ElText
	// ElEm / ElStrong are emphasis / strong emphasis.
	ElEm
	// ElStrong is strong emphasis.
	ElStrong
	// ElCodespan is an inline code span; Value holds the literal text.
	ElCodespan
	// ElA is a hyperlink; Options["href"]/["title"] carry the destination.
	ElA
	// ElImg is an image; Options["src"]/["alt"]/["title"] carry the attributes.
	ElImg
	// ElBr is a hard line break.
	ElBr
	// ElTypographicSym carries a smart-typography substitution; Value is the entity
	// name (e.g. "ldquo", "mdash").
	ElTypographicSym
	// ElSmartQuote is a smart-quote substitution; Value is one of "lsquo", "rsquo",
	// "ldquo" or "rdquo" and is mapped through the :smart_quotes option at render time
	// (kramdown's :smart_quote element / convert_smart_quote).
	ElSmartQuote
	// ElFootnoteRef is a footnote reference; Options["name"] is the id.
	ElFootnoteRef
	// ElAbbr is an expanded abbreviation; Value is the matched text and
	// Options["title"]/["class"] carry the definition.
	ElAbbr
	// ElRawHTMLSpan is raw inline HTML passed through verbatim in Value.
	ElRawHTMLSpan

	// ElHTMLElement is a parsed raw-HTML element (kramdown's :html_element). Value is
	// the tag name, Attrs the parsed HTML attributes, Children the parsed body, and
	// Options carry "content_model" ("raw"/"block"/"span"/"default"), "category"
	// ("block"/"span") and "is_closed" (bool).
	ElHTMLElement
	// ElXMLComment is a parsed HTML comment (kramdown's :xml_comment). Value holds the
	// verbatim "<!--…-->" text; Options["category"] is "block" or "span".
	ElXMLComment
	// ElXMLPI is a parsed processing instruction (kramdown's :xml_pi). Value holds the
	// verbatim "<?…?>" text; Options["category"] is "block" or "span".
	ElXMLPI
)

type LinkDef

type LinkDef struct {
	URL   string
	Title string
}

LinkDef is a predefined link-reference definition supplied via the LinkDefs option (kramdown's :link_defs): a destination URL and an optional title.

type Options

type Options struct {
	// AutoIds, when true (kramdown's default), assigns a generated id="" to every
	// header that lacks an explicit {#id}.
	AutoIds bool
	// AutoIdPrefix is prepended to every auto-generated header id (default "").
	AutoIdPrefix string
	// AutoIdStripping, when true, slugs a header id from the header's parsed plain
	// text (markup/HTML stripped) instead of its literal source. Mirrors kramdown's
	// deprecated :auto_id_stripping option (default false).
	AutoIdStripping bool
	// HeaderOffset shifts every header's output level by this amount, clamped to
	// 1..6 (an h1 with offset 1 renders as <h2>). Mirrors kramdown's :header_offset
	// option (default 0).
	HeaderOffset int
	// HeaderLinks, when true, prepends an empty self-anchor (<a href="#id"></a>) to
	// every header that carries a non-blank id. Mirrors kramdown's :header_links
	// option (default false).
	HeaderLinks bool
	// TransliteratedHeaderIds, when true, transliterates a header's text to ASCII
	// (via the vendored Stringex unidecoder table) before slugging it into an
	// auto-generated id. Mirrors kramdown's :transliterated_header_ids option
	// (default false).
	TransliteratedHeaderIds bool
	// SmartQuotes enables typographic substitution of quotes/dashes/ellipses
	// (kramdown's default).
	SmartQuotes bool
	// SmartQuotesSubst overrides the entity each smart-quote position maps to, in the
	// order [lsquo, rsquo, ldquo, rdquo] (kramdown's :smart_quotes array). A blank
	// entry falls back to that position's default name, so the zero value renders the
	// usual curly quotes. Mirrors e.g. :smart_quotes: apos,apos,quot,quot.
	SmartQuotesSubst [4]string
	// Typographic enables the --, ---, ... and <<>> substitutions (default true).
	Typographic bool
	// EntityOutput selects how a recognised HTML entity is rendered, mirroring
	// kramdown's :entity_output: "as_char" (the default) emits the entity's character,
	// except <, > and & which fall back to their named/numeric form; "as_input" keeps
	// the entity's literal input form; "numeric" emits "&#cp;"; and "symbolic" emits
	// "&name;" when the entity has a name, else "&#cp;". An empty value means as_char.
	EntityOutput string
	// HardWrap, when true, turns every soft newline into a <br />. Independent of
	// this, a line ending in two spaces (or "\\") is always a hard break. kramdown's
	// default is false.
	HardWrap bool
	// FootnoteNr is the starting number for footnotes (default 1).
	FootnoteNr int
	// FootnotePrefix is inserted between the "fn:"/"fnref:" marker and the footnote
	// name in every footnote id (default "").
	FootnotePrefix string
	// FootnoteBacklink is the (HTML-text-escaped) content of each reverse-footnote
	// link; the empty string suppresses back-links entirely (default "&#8617;").
	FootnoteBacklink string
	// FootnoteLinkText is a format string for the footnote reference's link text,
	// with "%s" replaced by the footnote number; empty means the bare number
	// (default "").
	FootnoteLinkText string
	// FootnoteBacklinkInline, when true, places each back-link inside the last
	// paragraph or header of a footnote's content (descending into nested blocks)
	// instead of appending it to (or after) only a top-level trailing paragraph.
	// Mirrors kramdown's :footnote_backlink_inline option (default false).
	FootnoteBacklinkInline bool
	// ParseSpanHTML, when true (kramdown's default), parses the Markdown content of
	// a raw inline HTML element (so "<span>*x*</span>" emphasises its body). Set
	// false via an inline "{::options parse_span_html=\"false\" /}" extension, a raw
	// inline element's body is instead passed through verbatim.
	ParseSpanHTML bool
	// ParseBlockHTML, when true, gives every parsed block-level HTML element its
	// native content model (kramdown's HTML_CONTENT_MODEL): a :block element reparses
	// its body as Markdown blocks, a :span element span-parses its body, and a :raw
	// element keeps its content verbatim. When false (kramdown's default) every block
	// HTML element uses the raw content model. Mirrors kramdown's :parse_block_html.
	ParseBlockHTML bool
	// HtmlToNative, when true, runs kramdown's Parser::Html::ElementConverter over
	// every parsed raw-HTML element, mapping it to the equivalent native element where
	// possible (<b>/<strong> -> :strong, <i>/<em> -> :em, <h1>.. -> :header,
	// <code>/<pre> -> :codespan/:codeblock, a simple <table> -> :table, and the
	// list/paragraph/blockquote containers), converting entities in their text and
	// applying kramdown's whitespace normalisation. When false (the default) parsed
	// HTML elements are serialised verbatim. Mirrors kramdown's :html_to_native.
	HtmlToNative bool
	// SyntaxHighlighter selects the code highlighter. "rouge" (kramdown's default)
	// routes code blocks/spans through the pure-Go go-ruby-rouge lexers; any other
	// value ("", "null", "minted", …) leaves them as plain <pre><code>.
	SyntaxHighlighter string
	// SyntaxHighlighterOpts carries the highlighter's sub-options (default_lang,
	// guess_lang, and the block:/span: disable flags).
	SyntaxHighlighterOpts SyntaxHighlighterOpts
	// LinkDefs supplies predefined link-reference definitions (kramdown's
	// :link_defs): a reference id maps to a URL and an optional title, resolvable by
	// "[text][id]" / "[id]" the same as a definition harvested from the source.
	LinkDefs map[string]LinkDef
	// RemoveLineBreaksForCJK, when true, elides a soft line break that sits between
	// two runs of East-Asian (Han/Hiragana/Katakana) characters, so source wrapped
	// one CJK "word" per line renders as unbroken text. Mirrors kramdown's
	// :remove_line_breaks_for_cjk option (default false).
	RemoveLineBreaksForCJK bool
	// TocLevels lists the header levels included in a {:toc} table of contents
	// (kramdown's :toc_levels). An empty/nil value means kramdown's default of every
	// level 1..6; e.g. []int{2, 3} restricts the TOC to h2 and h3.
	TocLevels []int
	// MathEngine selects how a "$$…$$" math element renders. "mathjax" (kramdown's
	// default) wraps the LaTeX verbatim in MathJax delimiters — "\[…\]" for a block
	// element, "\(…\)" for a span — with the value HTML-escaped; a math element that
	// carries IAL attributes is wrapped in a <div>/<span> instead. An empty value
	// (kramdown's :math_engine nil, the corpus' ":math_engine: ~") disables the engine:
	// the raw LaTeX is emitted inside a <div>/<span class="kdmath"> as "$$…$$"/"$…$".
	MathEngine string
	// TypographicSymbols overrides the replacement string kramdown emits for a named
	// typographic symbol (hellip, mdash, ndash, laquo, raquo, laquo_space,
	// raquo_space, lsquo, rsquo, ldquo, rdquo). A present entry is HTML-escaped and
	// emitted verbatim in place of the default entity; an absent key keeps the
	// default. Mirrors kramdown's :typographic_symbols option (default nil).
	TypographicSymbols map[string]string
}

Options configures a conversion, mirroring the keyword options accepted by Kramdown::Document.new. Only the options that influence the HTML output of the supported feature set are honoured; the rest are tolerated for API parity.

func DefaultOptions

func DefaultOptions() Options

DefaultOptions returns the option set matching kramdown's own defaults, used when New is called with a nil option pointer.

type SyntaxHighlighterOpts

type SyntaxHighlighterOpts struct {
	// DefaultLang is the language assumed for a code block/span that carries none
	// (kramdown's default_lang).
	DefaultLang string
	// GuessLang, when true, asks Rouge to sniff the language of an unlabelled block
	// (kramdown's guess_lang). A failed guess yields Rouge's plaintext lexer, which
	// still produces the highlighter-rouge wrapper with unhighlighted content.
	GuessLang bool
	// BlockDisable suppresses highlighting for code blocks (block: {disable: true}).
	BlockDisable bool
	// SpanDisable suppresses highlighting for code spans (span: {disable: true}).
	SpanDisable bool
}

SyntaxHighlighterOpts mirrors the recognised keys of kramdown's :syntax_highlighter_opts hash that influence HTML output. Nested per-context blocks (block:/span:) collapse to the two Disable flags this port honours.

Jump to

Keyboard shortcuts

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