The library is based on the architecture of markdown-it-py, making it easy to port their plugins.
You can run make install_toolchain (rust+cargo+uv)
Build with:
# Installs toolchain (cargo + uv)
make install_toolchain
# Builds entire project
make build
# OPTIONAL: Install built package in local python env
uv run maturin develop --releaseNOTE: If using conda/mamba on macos, builds can break. Reinstalling mamba helps.
let parser = &mut crate::MarkdownIt::new();
crate::plugins::cmark::add(parser);
crate::plugins::extra::add(parser);
let ast = parser.parse("Hello **world**!");
let html = ast.render();
print!("{html}");
// prints "<p>Hello <strong>world</strong>!</p>"If you built the python package, it should be installed as quickmark in the local python environment. Syntax looks like that of markdown-it-py:
from quickmark import MDParser
md = MDParser("commonmark").enable("table")
md.render("# Hello, world!")
# '<h1>Hello, world!</h1>\n'-
MarkdownIt("zero")will not enable any plugins. -
MarkdownIt("commonmark")for all CommonMark plugins. -
MarkdownIt("gfm")for CommonMark + GitHub Flavoured Markdown plugins.
A cli is in python/quickmark/cli.py, which can be used like this:
# replace - with filename to read from a file
# see `quickmark --help` for more
echo "# Hello, world!" | quickmark html -
# <h1>Hello, world!</h1>
echo "# Hello, world!" | quickmark ast -
# <root>
# <heading>
# <text>markdown-it.rs does not generate a token stream, but instead directly generates a Node tree.
This is similar to the markdown-it-py's SyntaxTreeNode class, although the API is not identical.
(source mapping is also provided by byte-offset, rather than line only)
md = (
MDParser("commonmark")
.enable("table")
.enable_many(["linkify", "strikethrough"])
)
node = md.tree("# Hello, world!")
print(node.walk())
# [Node(root), Node(heading), Node(text)]
print(node.pretty(srcmap=True, meta=True))
# <root srcmap="0:15">
# <heading srcmap="0:15">
# level: 1
# <text srcmap="2:15">
# content: Hello, world!Note: Attributes of the Node class, such as Node.attrs, return a copy of the underlying data, and so mutating it will not affect what is stored on the node, e.g.
from quickmark import Node
node = Node("name")
# don't do this!
node.attrs["key"] = "value"
print(node.attrs) # {}
# do this instead (Python 3.9+)
node.attrs = node.attrs | {"key": "value"}
print(node.attrs) # {"key": "value"}
# Node.children is only a shallow copy though, so this is fine
child = Node("child")
node.children = [child]
node.children[0].name = "other"
print(child.name) # "other"There is a webassembly build in the example demos.
For a guide on how to extend it, see examples folder.
For translating markdown-it plugins to rust, here are some useful notes:
state.bMarks[startLine] + state.tShift[startLine]is equivalent tostate.line_offsets[line].first_nonspacestate.eMarks[startLine]is equivalent tostate.line_offsets[line].line_endstate.sCount[line]is equivalent tostate.line_offsets[line].indent_nonspacestate.sCount[line] - state.blkIndentis equivalent tostate.line_indent(state.line)
All syntax rules in markdown-it.rs are implemented as plugins.
Plugins can be added to the parser by calling enable or enable_many with the name of the plugin.
The following plugins are currently supported:
CommonMark Blocks:
blockquote: Block quotes with>code: Indented code blocksfence: Backtick code blocksheading:#ATX headingshr:---horizontal ruleslheading:---underline setext headingslist:*unordered lists and1.ordered listsparagraph: Paragraphsreference: Link reference definitions[id]: src "title"
CommonMark Inlines:
autolink:<http://example.com>backticks:`code`emphasis:_emphasis_,*emphasis*,**strong**,__strong__entity:&escape: backslash escaping\image:link:[text](src "title"),[text][id],[text]newline: hard line breakshtml_block: HTML blockshtml_inline: HTML inlinesourcepos: Add source mapping to rendered HTML, looks like this:<stuff data-sourcepos="1:1-2:3">, i.e.line:col-line:colreplacements: Typographic replacements, like--to—smartquotes: Smart quotes, like"to“linkify: Automatically linkify URLs with https://crates.io/crates/linkify (note currently this only matches URLs with a scheme, e.g.https://example.com)heading_anchors: Add heading anchors, with defaults like GitHubfront_matter: YAML front matterfootnote: Pandoc-style footnotes (see https://pandoc.org/MANUAL.html#footnotes)
GitHub Flavoured Markdown (https://github.github.com/gfm):
-
table:| foo | bar | | --- | --- | | baz | bim |
-
strikethrough:~~strikethrough~~ -
tasklist:- [x] tasklist item -
autolink_ext: Extended autolink detection with "bare URLs" likehttps://example.comandwww.example.com -
tagfilter: HTML tag filtering, e.g.<script>tags are removed