Built on rushdown — forked & extended

Markdown in.
HTML out — fast.

A fast CommonMark + GFM parser and renderer — a Rust core on crates.io with Python bindings on PyPI and full AST access.

mordant parses CommonMark 0.31.2 + GFM (tables, task lists, strikethrough) into an arena-allocated AST, then renders HTML. One engine in both languages, lockstep versioned. The GIL is released for multi-threaded throughput — ~4.0× across 4 threads, up to 55× faster than python-markdown on large documents. Fenced code highlights through the bundled syntect engine — one theme= for code and Mermaid alike.

One call for HTML, full tree when you need it
render.py Python surface
import mordant

html = mordant.markdown_to_html("# Hello\n\n**World**")
# '<h1>Hello</h1>\n<p><strong>World</strong></p>\n'

doc = mordant.parse("# Title\n\nBody")
doc.kind        # "Document"
doc.metadata    # {} — or frontmatter, when present
Single call for HTMLparse → walk → render
The rule the renderer keeps“Never lose the source structure.”

Every node keeps its kind, position, and text — headings, tables, diagrams, footnotes included.
Extensions hook the same tree.

Illustrative session · GFM tables on by default, linkify off unless asked.

Keep the Markdown.
Drop the slowdown.

Markdown tooling splits between fast renderers with no tree and slow parsers with no GFM. mordant keeps both: its core is rushdown by Yusuke Inuzuka — forked and extended as mordant — with a priority-based parser over an arena-allocated AST, CommonMark 0.31.2 plus tables, task lists, and strikethrough out of the box.

The Python package is a thin PyO3 shim over the same mordant Rust crate — Document, Node, and Walker share one source buffer by refcount instead of copying it. Parse and render run detached from the GIL, so threads scale instead of serialising.

  1. SourceMarkdown + GFM

    Tables, task lists, strikethrough

  2. ParseArena AST

    Full tree, positions kept

  3. ExtendEngines per feature

    Frontmatter, math, diagrams, highlighting

  4. EmitHTML or chunks

    Render, lint, or slice

From pip install
to trusted HTML.

One install covers papers, docs, and notes. The example below follows the standard path: install the wheel, render in one call, keep GFM on with linkify off by default, read frontmatter and the AST, then reach for math, diagrams, highlighting, and footnotes.

Rust users add the same crate with the features they need — no Python involved. Python needs 3.9+, Rust needs 1.87+.

Install with one line

Wheels for Linux, macOS, and Windows. The Rust crate and the Python package share the version number — 0.10.1 on both indexes.

pip install mordant

# Rust library (no Python involved):
# cargo add mordant

Parser defaults

# GFM: tables + strikethrough + task lists ON
# linkify (autolink) OFF — enable with GfmOptions.all()
gfm = mordant.GfmOptions()          # defaults
gfm = mordant.GfmOptions.all()      # + linkify

html = mordant.markdown_to_html(
  "https://example.com", gfm_opts=gfm)

Frontmatter, emoji, footnotes, math, and diagrams work alongside GFM with no extra wiring.

1

Render in one call.

The common case stays one function. GFM tables, strikethrough, and task lists render by default; raw HTML is escaped unless explicitly allowed.

Single-call render
html = mordant.markdown_to_html("# Hello\n\n**World**")
# '<h1>Hello</h1>\n<p><strong>World</strong></p>\n'

html = mordant.markdown_to_html("~~deleted~~")
# '<p><del>deleted</del></p>\n'
2

Read frontmatter and the tree.

parse() returns a Document with full traversal — parent, children, siblings, every node kind. YAML frontmatter arrives typed (null, bool, int, float, str, list, dict); a lone --- stays a thematic break.

AST and metadata
doc = mordant.parse("---\ntitle: Doc\n---\n\nBody")
doc.metadata   # {'title': 'Doc'}

heading = mordant.parse("# Title").children[0]
heading.level  # 1
heading.text   # "Title"

links = [n for n in doc.walk("depth") if n.kind == "Link"]
3

Extend with math, diagrams, extras.

KaTeX fenced blocks plus inline $…$ / $$…$$, Mermaid blocks rendered server-side as inline SVG by default, PHP-style footnotes, and shortcode emoji — all on the same tree.

Math, diagrams, footnotes
html = mordant.markdown_to_html("```math\n\\int_0^\\infty e^{-x^2} dx\n```")
# '<span class="katex katex-display">…'

html = mordant.markdown_to_html("```mermaid\ngraph LR\n A --- B\n```")
# server-rendered inline SVG by default

html = mordant.markdown_to_html("Text[^1]\n\n[^1]: The footnote.")
4

Highlight code, theme everything.

Fenced code highlights through the bundled syntect engine — no opt-in needed. Theme per call with highlighting_theme, pick inline style or CSS class output, or set one theme= for code blocks and Mermaid SVG together. Custom VSCode and .tmTheme themes load from ~/.mordant/themes/ or register at runtime.

Themed highlighting
# themed code block (inline styles by default,
# or highlighting_mode="Class" for CSS classes)
html = mordant.markdown_to_html(
  "```python\ndef hi(): return 1\n```",
  highlighting_theme="Dracula")

# single theme= themes BOTH code and Mermaid SVG
html = mordant.markdown_to_html(
  "# T\n```mermaid\ngraph LR\n A---B\n```\n```python\nx=1\n```",
  theme="Dracula")

# standalone spans for your own container
spans = mordant.Highlighter(theme="Dracula").highlight(
  "python", "def hi(): pass", bare=True)
bg = mordant.theme_background("Dracula")  # '#282a36'
5

Slice documents for retrieval.

MarkdownChunker yields bare chunks — raw block content with no heading prefix — while tracking the current header for embed-time injection. GFM tables and Mermaid diagrams classify correctly instead of collapsing to code.

Chunking
chunker = mordant.MarkdownChunker("# S\n\nPara one\n\n## Sub\n\nPara two")
list(chunker)  # ['Para one', 'Para two'] — bare, no prefix

for c in chunker.get_chunks():
    print(c.block_type, c.text)

payloads = chunker.compute_overlap_payloads(2)
6

Lint, fix, and scale.

25 markdownlint-style rules with diagnostics, a fix engine, batch lint_many() / fix_many() over rayon, and a python -m mordant CLI. Parse and render release the GIL — expect ~4.0× across 4 threads.

Lint and threads
from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=4) as pool:
    out = list(pool.map(mordant.markdown_to_html, docs))
# ~4.0x linear scaling vs single-threaded

Useful at every stage
of the Markdown loop.

mordant owns the whole loop — parse, extend, render, check, slice — in one versioned core. The quick reference covers every option; the architecture record explains the arena, the dispatch, and the binding model.

Blazing fast

Up to 55× faster than python-markdown on large documents — 2.763 ms vs 621 ms on a 202 KB fixture. Single-threaded wins hold across mistune and markdown-it-py too.

Full AST access

parse() returns a Document with parent, children, siblings, positions, and all node kinds — tables, diagrams, footnotes, and custom extension nodes included.

CommonMark + GFM

CommonMark 0.31.2 plus tables, task lists, and strikethrough. Autolink stays off by default and enables explicitly via GfmOptions.all().

YAML frontmatter

Typed metadata extraction with thematic-break lookahead — --- alone is a rule, ---\ntitle: … is frontmatter. Errors surface on access, not on parse.

Emoji, math, diagrams, footnotes

Shortcode emoji with blacklist and templates, KaTeX display + inline math, Mermaid as server-side SVG, PHP-style footnotes with backlinks — composable, not competing.

Lint engine

25 rules (MD001, MD009, MD012, MD013, MD024, MD040, MD045, MD049, …) with diagnostics, auto-fix, inline suppressions, frontmatter awareness, and a CLI with human / JSON / GitHub output.

Document chunking

Lazy AST chunk iterator with heading-context propagation, from_file and from_file_mmap constructors, overlap payloads for embeddings, and delimiter-aware reconstruction.

Multi-threaded + tested

GIL released across parse, render, math, and batch lint/fix. 141 Rust unit tests, the full CommonMark spec suite, and 1247 Python integration tests.

Integrated code highlighting

Fenced code highlights through the bundled syntect engine (Sublime Text syntax definitions, bat's theme set via syntect-assets) — no opt-in needed. highlighting_theme picks the theme, highlighting_mode picks inline style vs CSS class, and one theme= kwarg themes code blocks and Mermaid SVG together.

Custom themes, your way

Bring any VSCode JSON or Sublime .tmTheme — add_custom_theme(name, content) at runtime, or drop files in ~/.mordant/themes/ for auto-load. Custom .sublime-syntax grammars register via add_custom_syntax(), with list_themes(), list_syntaxes(), detect_language(), and theme_background() to build on.

Measured speed,
per fixture.

Single-threaded, 50 iterations per fixture. mordant leads at every size — the gap widens as documents grow. Multi-threaded, parse + render scale ~4.0× across 4 threads.

Run it yourself: python benchmarks.py in mordant-py, or cargo bench at the repo root.

Single-threaded parse + render (50 iterations)
Fixturemordantmistunemarkdown-it-pypython-markdown
Small (400B)0.039 ms0.430 ms0.475 ms2.301 ms
Medium (5.4KB)0.155 ms2.448 ms3.940 ms6.455 ms
Large (26.7KB)0.410 ms8.611 ms16.743 ms31.304 ms
Data (202KB)2.763 ms38.152 ms65.736 ms621.295 ms

Rust for pipelines.

The same engine ships as a native library with no Python dependency. Everything beyond the default parser/renderer is a Cargo feature — add only the engines you need.

// Cargo.toml
// dependencies = { mordant = "0.10" }

use mordant::markdown_to_html_string;

let mut html = String::new();
markdown_to_html_string(&mut html, "# Hello\n\n**World**").unwrap();

One engine.
Two languages.

Python covers documents end to end; Rust exposes the same engine plus the feature flags other pipelines build on. The architecture record explains the arena, the dispatch, and the binding model.

Python for documents

Single-call render, full AST, frontmatter, GFM options, emoji, math, diagrams, footnotes, chunking, lint, and highlighting — behind a PyO3 shim that never copies the source.

import mordant

html = mordant.markdown_to_html(md)
doc = mordant.parse(md)
chunker = mordant.MarkdownChunker(md)
Quick reference

Rust for embedding

Add the crate, pick features, render natively. The spec suite runs against the core crate itself — Python adds integration depth, not correctness.

cargo add mordant

# features: meta, emoji, footnotes,
# linter, diagram, chunker,
# math, highlighter
API docs

Pipelines for graphs

Chunk bare blocks with offsets, lint and fix at scale, highlight standalone spans — the shapes OKFgraph consumes (it depends on mordant>=0.9 for chunk + lint).

mordant = "0.10"  # crates.io
# pip install mordant  # PyPI, Python 3.9+
# 141 Rust tests + spec suite
# 1247 Python integration tests
Architecture

Proven downstream,
usable alone.

mordant ships independently on PyPI and crates.io with no model weights and no runtime service. Its main consumer is OKFgraph, the author's local knowledge graph — which depends on mordant for chunking and lint.

All engines beyond parse/render live in the mordant core crate behind granular features — enable only what you need.

OKFgraph graph + memory

Local knowledge graph + agent memory

Website Repository

Depends on mordant (mordant>=0.9) for chunking and lint — MarkdownChunker bare chunks with offsets plus lint / fix before import. The downstream that wants deterministic HTML and graph-ready slices.

Where mordant lives
SurfacePackageVersion
Python wheelsmordant0.10.1 · Python 3.9+
Rust librarymordant0.10.1 · Rust 1.87+
API referencedocs.rs/mordantCommonMark 0.31.2 + GFM
BindingsPyO30.29 · GIL released

Start with the document
you already have.

Install the wheel, render one string, walk the tree when you need it. Then hand the HTML to a page — or the chunks to a graph.