Skip to content

Markdown

Crate carries two text modules that work together: crate.text.markdown turns a block-structured JSON document into Markdown source, and crate.text.markdownVibe turns Markdown source into HTML.

Editor-style rich-text fields are usually stored as a list of typed blocks rather than as prose. toMarkdown flattens such a document:

import crate.text.markdown;
auto article = `{
"blocks": [
{ "type": "header", "data": { "text": "Release notes" } },
{ "type": "paragraph", "data": { "text": "Bulk operations landed." } }
]
}`.parseJsonString;
auto source = article.toMarkdown;
Block typeRendered as
header## <text>
paragraph<text>

Every block is followed by a blank line, and the result is stripped. A document with no blocks array, or one whose blocks are all of unrecognized types, produces an empty string — unknown types are skipped rather than rejected, so a newer editor’s document degrades instead of failing.

toHeader and toParagraph are exposed if you need to render a single block.

A Markdown-to-HTML filter, extended past vanilla Markdown with tables, attributes, figures, and a document outline.

import crate.text.markdownVibe;
auto html = filterMarkdown(source, MarkdownFlags.backtickCodeBlocks | MarkdownFlags.tables);

filterMarkdown has four forms: return a string or write into an output range dst, each taking either a MarkdownFlags value or a MarkdownSettings object.

FieldDefaultPurpose
flagsvanillaMarkdownWhich extensions the parser recognizes
headingBaseLevel1Level the first heading renders at, so embedded content can sit under a page title
urlFilternullstring delegate(string url, bool isImage) — rewrite every link and image target
allowedURISchemas["http", "https", "ftp", "mailto"]Schemas permitted in link and image targets

The schema allow-list is a security control, not a convenience: a javascript: URI in a link is blocked and escaped rather than emitted.

FlagEffect
none / vanillaMarkdownStandard Markdown only
keepLineBreaksSingle newlines become <br>; suits plain-text sources such as email
backtickCodeBlocksFenced code blocks
noInlineHtmlEmbedded HTML is escaped instead of passed through
tablesMarkdown Extra / GitHub table syntax
attributes{ … } after a link or image adds HTML attributes to the element
figures- %%% list syntax produces <figure>, - ### produces <figcaption>
forumDefaultkeepLineBreaks, backtickCodeBlocks, noInlineHtml and tables combined

Flags are a bit set — combine them with |.

getMarkdownOutline returns the heading tree without rendering the document, which is what a table-of-contents component needs:

Section[] outline = getMarkdownOutline(source);
struct Section {
size_t headingLevel;
string caption;
string anchor;
Section[] subSections;
}

anchor is the slug generated for the heading — lowercased, with dashes for spaces — and matches the id filterMarkdown puts on the rendered heading, so outline entries link to the right place.

The parser is not fully CommonMark compliant. vanillaMarkdown is the intent — standard Markdown — rather than a conformance claim.