Markdown for MoonBit: CommonMark and GFM to a syntax tree, HTML or Markdown, whole or as it streams in.
moon add moonbitstack/moonmdtest {
let document = @moonmd.parse("# Title\n\nA *short* paragraph with a [link](/a).\n")
assert_eq(
document.html(safe=false),
"<h1>Title</h1>\n<p>A <em>short</em> paragraph with a <a href=\"/a\">link</a>.</p>\n",
)
assert_eq(document.markdown(), "# Title\n\nA *short* paragraph with a [link](/a).\n")
}test {
let document = @moonmd.parse("# One\n\nA [link](/a) and a *word*.\n")
let counts = [0, 0]
@moonmd.walk(document, fn(node) {
match node {
@moonmd.Block(@moonmd.Heading(_)) => counts[0] = counts[0] + 1
@moonmd.Inline(@moonmd.Link(_)) => counts[1] = counts[1] + 1
_ => ()
}
})
assert_eq(counts, [1, 1])
}test {
let document = @moonmd.parse("# One\n\ntwo\nthree\n")
let spans = document.lines()
assert_eq((spans[0].first, spans[0].last), (1, 1))
assert_eq((spans[1].first, spans[1].last), (3, 4))
}test {
let document = @moonmd.parse("A claim[^note]\n\n[^note]: the support\n")
let expected =
"<p>A claim<sup class=\"footnote-ref\"><a href=\"#fn-note\" " +
"id=\"fnref-note\" data-footnote-ref>1</a></sup></p>\n" +
"<section class=\"footnotes\" data-footnotes>\n" +
"<ol>\n" +
"<li id=\"fn-note\">\n" +
"<p>the support <a href=\"#fnref-note\" class=\"footnote-backref\" " +
"data-footnote-backref data-footnote-backref-idx=\"1\" " +
"aria-label=\"Back to reference 1\">\u{21A9}</a></p>\n" +
"</li>\n</ol>\n</section>\n"
assert_eq(document.html(safe=false), expected)
assert_eq(document.markdown(), "A claim[^note]\n\n[^note]:\n the support\n")
}test {
assert_eq(@moonmd.parse("~~struck~~\n").html(safe=false), "<p><del>struck</del></p>\n")
assert_eq(
@moonmd.parse("~~struck~~\n", flavor=@moonmd.commonmark).html(safe=false),
"<p>~~struck~~</p>\n",
)
}test {
let typed : @moonmd.Flavor = { ..@moonmd.gfm, smart: true }
assert_eq(
@moonmd.parse("\"quoted\" -- and...\n"[:], flavor=typed).html(safe=false),
"<p>\u{201C}quoted\u{201D} \u{2013} and\u{2026}</p>\n",
)
assert_eq(
@moonmd.parse("`\"--\"`\n"[:], flavor=typed).html(safe=false),
"<p><code>"--"</code></p>\n",
)
}test {
let boxed : @moonmd.Flavor = { ..@moonmd.gfm, breaks: true }
assert_eq(
@moonmd.parse("one\ntwo\n"[:], flavor=boxed).html(safe=false),
"<p>one<br />\ntwo</p>\n",
)
}test {
let stream = @moonmd.Stream::new()
let counts = [
stream.feed("# Hi"[:]).length(),
stream.feed("\n\n"[:]).length(),
stream.feed("a paragraph"[:]).length(),
stream.finish().length(),
]
assert_eq(counts, [0, 1, 0, 1])
}test {
assert_eq(@moonmd.parse("<div>hi</div>\n").html(), "<div>hi</div>\n")
assert_eq(@moonmd.parse("[bad](javascript:alert(1))\n").html(), "<p>bad</p>\n")
}| Corpus | Examples | Passing |
|---|---|---|
| CommonMark 0.31.2 spec.json | 652 | 652 |
| GitHub Flavored Markdown extensions | 29 | 29 |
| Example | What the reference does instead |
|---|---|
| 28 | turns a character reference above U+10FFFF into U+FFFD; 0.31.2 keeps it as text |
| 354 | counts a currency sign as punctuation, so *£* closes emphasis where 0.31.2 leaves it open |
| 389 | collapses __foo, __bar__, baz__ into one strong span |
| 417 | drops the runs of foo******bar*********baz down to one strong span |
| 425 | collapses __foo __bar__ baz__ into a single strong span |
| 426 | collapses ____foo__ bar__ into a single strong span |
| 427 | collapses **foo **bar**** into a single strong span |
| 464 | collapses ****foo**** into a single strong span |
| 465 | collapses ____foo____ into a single strong span |
| 466 | collapses ******foo****** into a single strong span |
| 468 | collapses _____foo_____ into one strong span inside an emphasis |
pub(all) enum Align {
Default
Left
Center
Right
}pub(all) struct Code {
info : String
text : String
fenced : Bool
}pub struct Document {
// private fields
}test {
let document = @moonmd.parse("# One\n\ntwo\n")
let spans = document.lines()
assert_eq((spans[0].first, spans[0].last), (1, 1))
assert_eq((spans[1].first, spans[1].last), (3, 3))
}pub(all) struct Flavor {
tables : Bool
strikethrough : Bool
autolinks : Bool
task_lists : Bool
tag_filter : Bool
footnotes : Bool
smart : Bool
breaks : Bool
}pub(all) struct FootnoteRef {
label : String
number : Int
ref_ix : Int
}pub(all) struct LineSpan {
first : Int
last : Int
}pub(all) enum ListKind {
Bullet(Char)
Ordered(Int, Char)
}pub struct Stream {
// private fields
}pub(all) enum Task {
Plain
Unchecked
Checked
}test {
let document = @moonmd.parse("# Title")
assert_eq(document.html(safe=false), "<h1>Title</h1>\n")
}Install
Download zipMarkdown for MoonBit: CommonMark and GFM to a syntax tree, HTML or Markdown, whole or as it streams in.