Sign in

    #cmark

    This package provides a CommonMark parser in MoonBit, with support for tables, task lists, math blocks, and more.

    #Basic Usage

    Let's start with a simple example of parsing Markdown text:

    ///|
    test "basic parsing" {
    let input =
    #|# Hello World
    #|
    #|This is a paragraph.
    let doc = @cmark.Doc::from_string(input)
    // Should contain a heading and a paragraph
    debug_inspect(
    doc,
    content=(
    #|{
    #| nl: "\n",
    #| block: Blocks(
    #| {
    #| v: Seq(
    #| [
    #| Heading(
    #| {
    #| v: {
    #| layout: Atx({ indent: 0, after_opening: "", closing: "" }),
    #| level: 1,
    #| inline: Text(
    #| {
    #| v: "Hello World",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| id: None,
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| BlankLine(
    #| {
    #| v: "",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| Paragraph(
    #| {
    #| v: {
    #| leading_indent: 0,
    #| inline: Text(
    #| {
    #| v: "This is a paragraph.",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| trailing_blanks: "",
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| ],
    #| ),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| defs: {},
    #|}
    ),
    )
    }

    Below are some simple examples showing how to parse Markdown text with cmark and what the resulting syntax tree looks like. For more examples, please checkout the test suite.

    #Working with Inline Elements

    #Emphasis and Strong Emphasis

    ///|
    test "emphasis and strong emphasis" {
    let input =
    #|_Emphasis_ and **strong emphasis**
    let doc = @cmark.Doc::from_string(input)
    debug_inspect(
    doc,
    content=(
    #|{
    #| nl: "\n",
    #| block: Paragraph(
    #| {
    #| v: {
    #| leading_indent: 0,
    #| inline: Inlines(
    #| {
    #| v: Seq(
    #| [
    #| Emphasis(
    #| {
    #| v: {
    #| delim: '_',
    #| inline: Text(
    #| {
    #| v: "Emphasis",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| Text(
    #| {
    #| v: " and ",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| StrongEmphasis(
    #| {
    #| v: {
    #| delim: '*',
    #| inline: Text(
    #| {
    #| v: "strong emphasis",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| ],
    #| ),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| trailing_blanks: "",
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| defs: {},
    #|}
    ),
    )
    }

    #Inline Code and Math

    ///|
    test "inline code and math" {
    let input =
    $|$E = mc^2$ in Python: `E = m * (c ** 2)`
    let doc = @cmark.Doc::from_string(input, strict=false)
    debug_inspect(
    doc,
    content=(
    #|{
    #| nl: "\n",
    #| block: Paragraph(
    #| {
    #| v: {
    #| leading_indent: 0,
    #| inline: Inlines(
    #| {
    #| v: Seq(
    #| [
    #| ExtMathSpan(
    #| {
    #| v: {
    #| display: false,
    #| tex_layout: Seq(
    #| [
    #| {
    #| blanks: "",
    #| node: { v: "E = mc^2", meta: { id: 0, loc: ..., extra: None } },
    #| },
    #| ],
    #| ),
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| Text(
    #| {
    #| v: " in Python: ",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| CodeSpan(
    #| {
    #| v: {
    #| backticks: 1,
    #| code_layout: Seq(
    #| [
    #| {
    #| blanks: "",
    #| node: { v: "E = m * (c ** 2)", meta: { id: 0, loc: ..., extra: None } },
    #| },
    #| ],
    #| ),
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| ],
    #| ),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| trailing_blanks: "",
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| defs: {},
    #|}
    ),
    )
    }

    #Working with Block-Level Elements

    #Headings

    ///|
    test "headings" {
    let input =
    #|# Level 1
    let doc = @cmark.Doc::from_string(input)
    // Should contain a heading and a paragraph
    debug_inspect(
    doc,
    content=(
    #|{
    #| nl: "\n",
    #| block: Heading(
    #| {
    #| v: {
    #| layout: Atx({ indent: 0, after_opening: "", closing: "" }),
    #| level: 1,
    #| inline: Text(
    #| {
    #| v: "Level 1",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| id: None,
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| defs: {},
    #|}
    ),
    )
    }

    #Lists

    ///|
    test "lists" {
    let input =
    #|- First item
    #|- Second item
    #| 1. Nested item
    let doc = @cmark.Doc::from_string(input)
    // Should find an ordered list within an unordered one.
    debug_inspect(
    doc,
    content=(
    #|{
    #| nl: "\n",
    #| block: List(
    #| {
    #| v: {
    #| ty: Unordered('-'),
    #| tight: true,
    #| items: Seq(
    #| [
    #| {
    #| v: {
    #| before_marker: 0,
    #| marker: {
    #| v: "-",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| after_marker: 1,
    #| block: Paragraph(
    #| {
    #| v: {
    #| leading_indent: 0,
    #| inline: Text(
    #| {
    #| v: "First item",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| trailing_blanks: "",
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| ext_task_marker: None,
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| {
    #| v: {
    #| before_marker: 0,
    #| marker: {
    #| v: "-",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| after_marker: 1,
    #| block: Blocks(
    #| {
    #| v: Seq(
    #| [
    #| Paragraph(
    #| {
    #| v: { leading_indent: 0, inline: Text(...), trailing_blanks: "" },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: ...,
    #| last_line: ...,
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| List(
    #| {
    #| v: { ty: Ordered(1, '.'), tight: true, items: Seq(...) },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: ...,
    #| last_line: ...,
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| ],
    #| ),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| ext_task_marker: None,
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ],
    #| ),
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| defs: {},
    #|}
    ),
    )
    }

    #Code Blocks

    ///|
    test "code blocks" {
    let tick3 = "`".repeat(3)
    let input =
    $|\{tick3}moonbit
    $|fn main {
    $| println("Hello")
    $|}
    $|\{tick3}
    let doc = @cmark.Doc::from_string(input)
    debug_inspect(
    doc,
    content=(
    #|{
    #| nl: "\n",
    #| block: CodeBlock(
    #| {
    #| v: {
    #| layout: Fenced(
    #| {
    #| indent: 0,
    #| opening_fence: {
    #| v: "",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| closing_fence: Some(
    #| {
    #| v: "",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| },
    #| ),
    #| info_string: Some(
    #| {
    #| v: "moonbit",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| code: Seq(
    #| [
    #| {
    #| v: "fn main {",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| {
    #| v: " println(\"Hello\")",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| {
    #| v: "}",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ],
    #| ),
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| defs: {},
    #|}
    ),
    )
    }

    #Tables

    ///|
    test "tables" {
    let input =
    #|| Header 1 | Header 2 |
    #||----------|----------|
    #|| Cell 1 | Cell 2 |
    let doc = @cmark.Doc::from_string(input, strict=false)
    debug_inspect(
    doc,
    content=(
    #|{
    #| nl: "\n",
    #| block: ExtTable(
    #| {
    #| v: {
    #| indent: 0,
    #| col_count: 2,
    #| rows: Seq(
    #| [
    #| (
    #| {
    #| v: Header(
    #| Seq(
    #| [
    #| (
    #| Text(
    #| {
    #| v: "Header 1",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: ...,
    #| last_line: ...,
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| TableCellLayout(("", "")),
    #| ),
    #| (
    #| Text(
    #| {
    #| v: "Header 2",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: ...,
    #| last_line: ...,
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| TableCellLayout(("", "")),
    #| ),
    #| ],
    #| ),
    #| ),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| "",
    #| ),
    #| (
    #| {
    #| v: Sep(
    #| Seq(
    #| [
    #| {
    #| v: TableSep((None, 10)),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| {
    #| v: TableSep((None, 10)),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ],
    #| ),
    #| ),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| "",
    #| ),
    #| (
    #| {
    #| v: Data(
    #| Seq(
    #| [
    #| (
    #| Text(
    #| {
    #| v: "Cell 1",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: ...,
    #| last_line: ...,
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| TableCellLayout(("", "")),
    #| ),
    #| (
    #| Text(
    #| {
    #| v: "Cell 2",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: ...,
    #| last_line: ...,
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| TableCellLayout(("", "")),
    #| ),
    #| ],
    #| ),
    #| ),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| "",
    #| ),
    #| ],
    #| ),
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| defs: {},
    #|}
    ),
    )
    }

    #Footnotes

    ///|
    test "footnotes" {
    let input =
    #|Text[^1]
    #|
    #|[^1]: Footnote content
    let doc = @cmark.Doc::from_string(input, strict=false)
    debug_inspect(
    doc,
    content=(
    #|{
    #| nl: "\n",
    #| block: Blocks(
    #| {
    #| v: Seq(
    #| [
    #| Paragraph(
    #| {
    #| v: {
    #| leading_indent: 0,
    #| inline: Inlines(
    #| {
    #| v: Seq(
    #| [
    #| Text(
    #| {
    #| v: "Text",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: ...,
    #| last_line: ...,
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| Link(
    #| {
    #| v: { text: Text(...), reference: Ref(Shortcut, ..., ...) },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: ...,
    #| last_line: ...,
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| ],
    #| ),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| trailing_blanks: "",
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| BlankLine(
    #| {
    #| v: "",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| ExtFootnoteDefinition(
    #| {
    #| v: {
    #| indent: 0,
    #| label: {
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| key: "^1",
    #| text: Seq(
    #| [
    #| {
    #| blanks: "",
    #| node: {
    #| v: "^1",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| },
    #| ],
    #| ),
    #| },
    #| defined_label: Some(
    #| {
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| key: "^1",
    #| text: Seq(
    #| [
    #| {
    #| blanks: "",
    #| node: {
    #| v: "^1",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: ...,
    #| last_line: ...,
    #| },
    #| extra: None,
    #| },
    #| },
    #| },
    #| ],
    #| ),
    #| },
    #| ),
    #| block: Paragraph(
    #| {
    #| v: {
    #| leading_indent: 1,
    #| inline: Text(
    #| {
    #| v: "Footnote content",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| trailing_blanks: "",
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| ],
    #| ),
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| defs: {
    #| "^1": FootnoteDef(
    #| {
    #| v: {
    #| indent: 0,
    #| label: {
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| key: "^1",
    #| text: Seq(
    #| [
    #| {
    #| blanks: "",
    #| node: {
    #| v: "^1",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| },
    #| ],
    #| ),
    #| },
    #| defined_label: Some(
    #| {
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| key: "^1",
    #| text: Seq(
    #| [
    #| {
    #| blanks: "",
    #| node: {
    #| v: "^1",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| },
    #| ],
    #| ),
    #| },
    #| ),
    #| block: Paragraph(
    #| {
    #| v: {
    #| leading_indent: 1,
    #| inline: Text(
    #| {
    #| v: "Footnote content",
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| trailing_blanks: "",
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| },
    #| meta: {
    #| id: 0,
    #| loc: {
    #| file: "-",
    #| first_ccode: -1,
    #| last_ccode: -1,
    #| first_line: LinePos(-1, -1),
    #| last_line: LinePos(-1, -1),
    #| },
    #| extra: None,
    #| },
    #| },
    #| ),
    #| },
    #|}
    ),
    )
    }

    #Document Transformation

    In addition, this package provides powerful APIs for transforming documents using the Mapper and Folder APIs.

    Blanks

    type Blanks = String

    BlanksNode

    type BlanksNode = Node[String]

    BlockBlankLine

    type BlockBlankLine = String

    The type for blank lines. These can be ignored during rendering, they are kept for layout.

    BlockLineBlank

    type BlockLineBlank = Node[String]

    Count

    type Count = Int

    InlineText

    type InlineText = String

    The type for textual content.

    Normally these strings should not contain newlines. This can however happen if the source had newlines as character references.

    LabelDefs

    type LabelDefs = Map[String, LabelDef]

    Mutable linked hash map that maintains the order of insertion, not thread safe.

    Example

    test {
    let map = { 3: "three", 8: "eight", 1: "one" }
    @test.assert_eq(map.get(2), None)
    @test.assert_eq(map.get(3), Some("three"))
    map.set(3, "updated")
    @test.assert_eq(map.get(3), Some("updated"))
    }

    LabelKey

    type LabelKey = String

    The type for label keys. These are link labels normalized for matches.

    StringNode

    type StringNode = Node[String]

    FolderError

    pub(all) suberror FolderError {
    FolderError(String)
    } derive(
    Debug
    )

    impl Show for FolderError

    MapperError

    pub(all) suberror MapperError {
    MapperError(String)
    } derive(
    Debug
    )

    impl Show for MapperError

    Block

    pub(all) enum Block {
    BlankLine(Node[String])
    BlockQuote(Node[BlockQuote])
    Blocks(Node[Seq[Block]])
    CodeBlock(Node[CodeBlock])
    Heading(Node[BlockHeading])
    HtmlBlock(Node[HtmlBlock])
    LinkRefDefinition(Node[LinkDefinition])
    List(Node[BlockList])
    Paragraph(Node[BlockParagraph])
    ThematicBreak(Node[BlockThematicBreak])
    ExtMathBlock(Node[CodeBlock])
    ExtTable(Node[Table])
    ExtFootnoteDefinition(Node[Footnote])
    } derive(
    Debug
    )

    A markdown block.

    Block::defs

    fn Block::defs(self : Block, init? : Map[String, LabelDef]) -> Map[String, LabelDef]

    Collects the given Block's link definitions (e.g. LinkRefDefinition and ExtFootnoteDefinition) and adds them to init (defaults to an empty map).

    Block::empty

    fn Block::empty() -> Block

    Block::normalize

    fn Block::normalize(self : Block) -> Block

    Normalize a block.

    b.normalize() has the same content as b but is such that for any occurrence of Blocks in b the list of blocks bs:
    • bs is not a singleton list.
    • Has no Blocks case. The meta is dropped and the nested blocks are spliced in bs where the case occurs.

    BlockHeading

    pub(all) struct BlockHeading {
    layout : BlockHeadingLayout
    level : Int
    inline : Inline
    id : BlockHeadingId?
    } derive(
    Debug
    )

    The type for ATX and Setext headings.

    BlockHeading::new

    fn BlockHeading::new(id? : BlockHeadingId?, layout? : BlockHeadingLayout, level~ : Int, inline : Inline) -> BlockHeading

    BlockHeadingAtxLayout

    pub(all) struct BlockHeadingAtxLayout {
    indent : Int
    after_opening : String
    closing : String
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    BlockHeadingAtxLayout::default

    BlockHeadingId

    pub(all) enum BlockHeadingId {
    Auto(String)
    Id(String)
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    BlockHeadingLayout

    pub(all) enum BlockHeadingLayout {
    Atx(BlockHeadingAtxLayout)
    Setext(BlockHeadingSetextLayout)
    } derive(
    Debug
    )

    BlockHeadingSetextLayout

    pub(all) struct BlockHeadingSetextLayout {
    leading_indent : Int
    trailing_blanks : String
    underline_indent : Int
    underline_count : Node[Int]
    underline_blanks : String
    } derive(
    Debug
    )

    BlockLine

    pub(all) struct BlockLine(Node[String])

    Block lines.

    In CommonMark blocks, a "line" does not necessarily correspond to a line in the source plain text. For example the lines of a paragraph in a block quote are the lines stripped from the block quote markers. We call the line resulting from stripping the block structure preceeding a given block a {e block line}.

    BlockLine::list_text_loc

    BlockLine::to_string

    fn BlockLine::to_string(self : BlockLine) -> String

    BlockList

    pub(all) struct BlockList {
    ty :
    ListType

    tight : Bool
    items : Seq[Node[ListItem]]
    } derive(
    Debug
    )

    The type for lists.

    BlockList::map_items

    fn BlockList::map_items(self : BlockList, f : (ListItem) -> ListItem) -> BlockList

    BlockList::normalize_items

    fn BlockList::normalize_items(self : BlockList) -> BlockList

    BlockParagraph

    pub(all) struct BlockParagraph {
    leading_indent : Int
    inline : Inline
    trailing_blanks : String
    } derive(
    Debug
    )

    The type for paragraphs.

    BlockParagraph::new

    fn BlockParagraph::new(leading_indent? : Int, trailing_blanks? : String, inline : Inline) -> BlockParagraph

    BlockQuote

    pub(all) struct BlockQuote {
    indent : Int
    block : Block
    } derive(
    Debug
    )

    The type for block quotes.

    BlockQuote::map_block

    fn BlockQuote::map_block(self : BlockQuote, f : (Block) -> Block) -> BlockQuote

    BlockQuote::new

    fn BlockQuote::new(indent? : Int, block : Block) -> BlockQuote

    BlockQuote::normalize_block

    fn BlockQuote::normalize_block(self : BlockQuote) -> BlockQuote

    BlockThematicBreak

    pub(all) struct BlockThematicBreak {
    indent : Int
    layout : String
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    The type for thematic breaks.

    BlockThematicBreak::new

    fn BlockThematicBreak::new(indent? : Int, layout? : String) -> BlockThematicBreak

    CodeBlock

    pub(all) struct CodeBlock {
    layout : CodeBlockLayout
    info_string : Node[String]?
    code : Seq[Node[String]]
    } derive(
    Debug
    )

    The type for indented and fenced code blocks.

    CodeBlock::language_of_info_string

    fn CodeBlock::language_of_info_string(s : String) -> (String, String)?

    [language_of_info_string s] extracts a (non-empty) language, the first word of [s] and a trimmed remainder. Assumes [s] is {!String.trim}ed which is what {!info_string} gives you.

    CodeBlock::make_fence

    fn CodeBlock::make_fence(self : CodeBlock) -> (Char, Int)

    [make_fence cb] is a fence character and count suitable for [cb].

    CodeBlock::new

    fn CodeBlock::new(layout? : CodeBlockLayout, info_string? : Node[String]?, code : Seq[Node[String]]) -> CodeBlock

    CodeBlockFencedLayout

    pub(all) struct CodeBlockFencedLayout {
    indent : Int
    opening_fence : Node[String]
    closing_fence : Node[String]?
    } derive(
    Debug
    )

    CodeBlockFencedLayout::default

    CodeBlockLayout

    pub(all) enum CodeBlockLayout {
    Indented
    Fenced(CodeBlockFencedLayout)
    } derive(
    Debug
    )

    Doc

    pub(all) struct Doc {
    nl : String
    block : Block
    defs : Map[String, LabelDef]
    } derive(
    Debug
    )

    The type for CommonMark documents.

    Doc::empty

    fn Doc::empty() -> Doc

    Doc::from_string

    fn Doc::from_string(defs? : Map[String, LabelDef], resolver? : LabelResolverFn, nested_links? : Bool, heading_auto_ids? : Bool, layout? : Bool, locs? : Bool, file? : String, strict? : Bool, s : String) -> Doc

    Doc::new

    fn Doc::new(nl? : String, defs? : Map[String, LabelDef], block : Block) -> Doc

    FoldFn

    pub(all) struct FoldFn[A, B]((Folder[B], B, A) -> B raise FolderError)

    Folder

    pub(all) struct Folder[A] {
    inline_ext_default : FoldFn[Inline, A]
    block_ext_default : FoldFn[Block, A]
    inline : FolderFn[Inline, A]
    block : FolderFn[Block, A]
    }

    Folder::block_ext_none

    fn[A] Folder::block_ext_none(self : Folder[A], _a : A, _b : Block) -> A raise FolderError

    Folder::fold_block

    fn[A] Folder::fold_block(self : Folder[A], acc : A, b : Block) -> A raise FolderError

    Folder::fold_doc

    fn[A] Folder::fold_doc(self : Folder[A], acc : A, doc : Doc) -> A raise FolderError

    Folder::fold_inline

    fn[A] Folder::fold_inline(self : Folder[A], acc : A, i : Inline) -> A raise FolderError

    Folder::inline_ext_none

    fn[A] Folder::inline_ext_none(self : Folder[A], _a : A, _b : Inline) -> A raise FolderError

    Folder::new

    fn[A] Folder::new(inline_ext_default? : FoldFn[Inline, A], block_ext_default? : FoldFn[Block, A], inline? : FolderFn[Inline, A], block? : FolderFn[Block, A]) -> Folder[A]

    Folder::none

    fn[A, B] Folder::none(self : Folder[A], _a : A, _b : B) -> FolderResult[A]

    Folder::ret

    fn[A] Folder::ret(a : A) -> FolderResult[A]

    FolderFn

    pub(all) struct FolderFn[A, B]((Folder[B], B, A) -> FolderResult[B])

    FolderResult

    pub(all) enum FolderResult[A] {
    Default
    Fold(A)
    } derive(Eq,
    Debug
    )

    The type for folder results. Default means doing the default fold.
    impl Default for FolderResult[A]

    Footnote

    pub(all) struct Footnote {
    indent : Int
    label : Label
    defined_label : Label?
    block : Block
    } derive(
    Debug
    )

    Footnote::map_block

    fn Footnote::map_block(self : Footnote, f : (Block) -> Block) -> Footnote

    Footnote::new

    fn Footnote::new(indent? : Int, defined_label? : Label?, label : Label, block : Block) -> Footnote

    Footnote::normalize_block

    fn Footnote::normalize_block(self : Footnote) -> Footnote

    HtmlBlock

    pub(all) struct HtmlBlock(Seq[Node[String]]) derive(
    Debug
    )

    The type for HTML blocks.

    Inline

    pub(all) enum Inline {
    Autolink(Node[InlineAutolink])
    Break(Node[InlineBreak])
    CodeSpan(Node[InlineCodeSpan])
    Emphasis(Node[InlineEmphasis])
    Image(Node[InlineLink])
    Inlines(Node[Seq[Inline]])
    Link(Node[InlineLink])
    RawHtml(Node[InlineRawHtml])
    StrongEmphasis(Node[InlineEmphasis])
    Text(Node[String])
    ExtStrikethrough(Node[InlineStrikethrough])
    ExtMathSpan(Node[InlineMathSpan])
    } derive(Eq,
    Debug
    )

    Inline::empty

    fn Inline::empty() -> Inline

    Inline::id

    fn Inline::id(self : Inline, buf? : StringBuilder) -> String

    Inline::is_empty

    fn Inline::is_empty(self : Inline) -> Bool

    Inline::meta

    meta(i) is the metadata of i.

    Inline::normalize

    fn Inline::normalize(self : Inline) -> Inline

    normalize(i) has the same content as i but is such that for any occurence of Inlines(is, _) in i the list of inlines is:
    • is is not a singleton list.
    • Has no two consecutive Text _ cases. If that occurs the texts are concatenated, the meta of the first one is kept and its text location extended to include the second one.
    • Has no Inlines _ case. The meta is dropped and the nested inlines are spliced in is where the case occurs.

    Inline::to_plain_text

    fn Inline::to_plain_text(self : Inline, break_on_soft~ : Bool) -> Seq[Seq[String]]

    to_plain_text(i, break_on_soft~, acc~) has the plain text of i as a sequence of lines represented by a list of strings to be concatenated. If break_on_soft is true soft line breaks are turned into hard line breaks.
    pub(all) struct InlineAutolink {
    is_email : Bool
    link : Node[String]
    } derive(Eq,
    Debug
    )

    InlineAutolink::new

    fn InlineAutolink::new(link : Node[String]) -> InlineAutolink

    InlineBreak

    pub(all) struct InlineBreak {
    layout_before : Node[String]
    ty : InlineBreakType
    layout_after : Node[String]
    } derive(Eq,
    Debug
    )

    InlineBreak::new

    fn InlineBreak::new(layout_before? : Node[String], layout_after? : Node[String], ty : InlineBreakType) -> InlineBreak

    InlineBreakType

    pub(all) enum InlineBreakType {
    Hard
    Soft
    } derive(Compare, Eq, ToJson,
    Debug
    ,
    FromJson
    )

    InlineCodeSpan

    pub(all) struct InlineCodeSpan {
    backticks : Int
    code_layout : Seq[Tight]
    } derive(Eq,
    Debug
    )

    The type for code spans.

    InlineCodeSpan::code

    fn InlineCodeSpan::code(self : InlineCodeSpan) -> String

    https://spec.commonmark.org/0.30/#code-spans

    InlineCodeSpan::from_string

    from_string(s) is a code span for s. s can start with or include backticks; the appropriate minimal backtick count and possible needed leading and trailing space are computed accordingly. If s contains newlines, blanks after newlines are treated as layout like during parsing. meta is used for the lines of the resulting code layout (see code_layout). the resulting code layout (see code_layout).

    InlineCodeSpan::new

    fn InlineCodeSpan::new(backticks~ : Int, code_layout : Seq[Tight]) -> InlineCodeSpan

    InlineEmphasis

    pub(all) struct InlineEmphasis {
    delim : Char
    inline : Inline
    } derive(Eq,
    Debug
    )

    The type for emphasis and strong emphasis. https://spec.commonmark.org/0.30/#emphasis-and-strong-emphasis

    InlineEmphasis::new

    fn InlineEmphasis::new(delim? : Char, inline : Inline) -> InlineEmphasis

    pub(all) struct InlineLink {
    text : Inline
    reference : ReferenceKind
    } derive(Eq,
    Debug
    )

    InlineLink::is_unsafe

    fn InlineLink::is_unsafe(l : String) -> Bool

    is_unsafe(l) is true if l is deemed unsafe. This is the case if l starts with a caseless match of javascript:, vbscript:, file: or data: except if data:image/{gif,png,jpeg,webp}. These rules were taken from the C reference implementation of CommonMark and are likely incomplete. If you are trying to prevent XSS you should post-process rendering outputs with a dedicated HTML sanitizer.

    InlineLink::new

    fn InlineLink::new(text : Inline, reference : ReferenceKind) -> InlineLink

    InlineLink::reference_definition

    fn InlineLink::reference_definition(self : InlineLink, defs : Map[String, LabelDef]) -> LabelDef?

    reference_definition(defs, l) is the definition of l's reference. If l is an Inline reference this returns its link definition wrapped in a LinkDef. If l is Ref this looks up the referenced_label in defs.

    InlineLink::referenced_label

    fn InlineLink::referenced_label(self : InlineLink) -> Label?

    referenced_label(l) is the label referenced by the label of l. This is the second label of Ref(_) or None on inline references.

    InlineMathSpan

    pub(all) struct InlineMathSpan {
    display : Bool
    tex_layout : Seq[Tight]
    } derive(Eq,
    Debug
    )

    InlineMathSpan::tex

    fn InlineMathSpan::tex(self : InlineMathSpan) -> String

    InlineRawHtml

    pub(all) struct InlineRawHtml(Seq[Tight]) derive(Eq,
    Debug
    )

    The type for inline raw HTML (can span multiple lines).

    InlineStrikethrough

    pub(all) struct InlineStrikethrough(Inline) derive(Eq,
    Debug
    )

    Label

    pub(all) struct Label {
    meta :
    Meta

    key : String
    text : Seq[Tight]
    } derive(
    Debug
    )
    impl Eq for Label

    Label::compare

    fn Label::compare(self : Label, other : Label) -> Int

    Label::new

    fn Label::new(meta? :
    Meta
    , key~ : String, text : Seq[Tight]) -> Label

    LabelContext

    pub(all) enum LabelContext {
    Def(Label?, Label)
    Ref(LinkKind, Label, Label?)
    }

    context is:
    • Def (prev, current) when we just hit a link reference definition or footnote definition that defines the label current. If there is already a definition for current's key it is provided in prev (whose meta has the location of the definition if you parse with locations). If None is returned the current definition is ignored, and definition prev (if any) is kept for the document. If Some l is returned l's key will be bound to the parsed definition for current in Doc.defs at the end of parsing. The result of the resolver is stored in the abstract syntax tree and available via LinkDefinition.defined_label and BlockFootnote.defined_label.
    • Ref (kind, ref, def) when we just hit a link or image referencing label ref. def is the label defining ref's key in the document (if any). The result of the resolver is the label stored for resolving the reference to its definition in the resulting InlineLink node; None means that label is undefined and the inline becomes InlineText like in CommonMark.

    LabelContext::default_resolver

    fn LabelContext::default_resolver(self : LabelContext) -> Label?

    LabelDef

    pub(all) enum LabelDef {
    LinkDef(Node[LinkDefinition])
    FootnoteDef(Node[Footnote])
    } derive(
    Debug
    )

    LabelResolverFn

    pub(all) struct LabelResolverFn((LabelContext) -> Label?)

    LinkDefinition

    pub(all) struct LinkDefinition {
    layout : LinkDefinitionLayout
    label : Label?
    defined_label : Label?
    dest : Node[String]?
    title : Seq[Tight]?
    } derive(Eq,
    Debug
    )

    The type for representing {{:https://spec.commonmark.org/0.30/#link-reference-definitions} link references definitions} and {{:https://spec.commonmark.org/0.30/#inline-link}inline links}.

    LinkDefinition::new

    fn LinkDefinition::new(layout? : LinkDefinitionLayout, label? : Label?, defined_label? : Label?, dest? : Node[String]?, title? : Seq[Tight]?) -> LinkDefinition

    LinkDefinitionLayout

    pub(all) struct LinkDefinitionLayout {
    indent : Int
    angled_dest : Bool
    before_dest : Seq[Node[String]]
    after_dest : Seq[Node[String]]
    title_open_delim : Char
    after_title : Seq[Node[String]]
    } derive(Eq,
    Debug
    )

    LinkDefinitionLayout::default

    LinkDefinitionLayout::for_dest

    fn LinkDefinitionLayout::for_dest(dest : String) -> LinkDefinitionLayout

    LinkKind

    pub(all) enum LinkKind {
    Link
    Image
    }

    ListItem

    #alias(ListItemBlock)
    pub(all) struct ListItem {
    before_marker : Int
    marker : Node[String]
    after_marker : Int
    block : Block
    ext_task_marker : Node[Char]?
    } derive(
    Debug
    )

    ListItem::map_block

    fn ListItem::map_block(self : ListItem, f : (Block) -> Block) -> ListItem

    ListItem::new

    fn ListItem::new(before_marker? : Int, marker? : Node[String], after_marker? : Int, ext_task_marker~ : Node[Char]?, block : Block) -> ListItem

    ListItem::normalize_block

    fn ListItem::normalize_block(self : ListItem) -> ListItem

    ListTaskStatus

    pub(all) enum ListTaskStatus {
    Cancelled
    Checked
    Unchecked
    Other(Char)
    }

    ListTaskStatus::from_marker

    fn ListTaskStatus::from_marker(marker : Char) -> ListTaskStatus

    MapFn

    pub(all) struct MapFn[A]((Mapper, A) -> A? raise MapperError)

    Mapper

    pub(all) struct Mapper {
    inline_ext_default : MapFn[Inline]
    block_ext_default : MapFn[Block]
    inline : MapperFn[Inline]
    block : MapperFn[Block]
    }

    Abstract syntax tree mappers.

    Mappers help with pushing abstract syntax tree transformations in every node with a minimal amount of code by defaulting the cases you don't handle. The default map maps leaves to themselves and otherwise propagates the map to all children.

    This map has the form of List::filter_map, however it is akin to List.concat_map as it allows:
    • Node deletion by mapping to None
    • Node transformation by mapping to Some _
    • Node expansion by mapping to Some (Inlines _) or Some (Blocks _)

    Mapper::block_ext_none

    fn[A] Mapper::block_ext_none(self : Mapper, _a : A) -> A? raise MapperError

    Mapper::delete

    fn[A] Mapper::delete() -> MapperResult[A]

    Mapper::inline_ext_none

    fn[A] Mapper::inline_ext_none(self : Mapper, _a : A) -> A? raise MapperError

    Mapper::map_block

    fn Mapper::map_block(self : Mapper, b : Block) -> Block?

    Mapper::map_doc

    fn Mapper::map_doc(self : Mapper, d : Doc) -> Doc

    Mapper::map_inline

    fn Mapper::map_inline(self : Mapper, i : Inline) -> Inline?

    Mapper::new

    fn Mapper::new(inline_ext_default? : MapFn[Inline], block_ext_default? : MapFn[Block], inline? : MapperFn[Inline], block? : MapperFn[Block]) -> Mapper

    Mapper::none

    fn[A] Mapper::none(self : Mapper, _a : A) -> MapperResult[A]

    Mapper::ret

    fn[A] Mapper::ret(a : A) -> MapperResult[A]

    MapperFn

    pub(all) struct MapperFn[A]((Mapper, A) -> MapperResult[A])

    MapperResult

    pub(all) enum MapperResult[A] {
    Default
    Map(A?)
    } derive(Eq,
    Debug
    )

    The type for mapper results. Default means doing the default map.
    impl Default for MapperResult[A]

    Node

    Node::empty

    Node::map

    fn[A, B] Node::map(self : Node[A], f : (A) -> B) -> Node[B]

    Node::new

    fn[A] Node::new(v : A, meta? :
    Meta
    ) -> Node[A]

    ReferenceKind

    pub(all) enum ReferenceKind {
    Inline(Node[LinkDefinition])
    Ref(ReferenceLayout, Label, Label)
    } derive(Eq,
    Debug
    )

    ReferenceLayout

    pub(all) enum ReferenceLayout {
    Collapsed
    Full
    Shortcut
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    The type for reference link layouts.

    Seq

    Seq::empty

    fn[A] Seq::empty() -> Seq[A]

    @coverage.skip

    Seq::fold

    fn[A, B] Seq::fold(self : Seq[A], init~ : B, f : (B, A) -> B) -> B

    @coverage.skip

    Seq::from_array

    fn[A] Seq::from_array(arr : Array[A]) -> Seq[A]

    @coverage.skip

    Seq::from_iter

    fn[A] Seq::from_iter(iter : Iter[A]) -> Seq[A]

    @coverage.skip

    Seq::get

    fn[A] Seq::get(self : Seq[A], idx : Int) -> A?

    @coverage.skip

    Seq::is_empty

    fn[A] Seq::is_empty(self : Seq[A]) -> Bool

    @coverage.skip

    Seq::iter

    fn[A] Seq::iter(self : Seq[A]) -> Iter[A]

    @coverage.skip

    Seq::length

    fn[A] Seq::length(self : Seq[A]) -> Int

    @coverage.skip

    Seq::map

    fn[A, B] Seq::map(self : Seq[A], f : (A) -> B) -> Seq[B]

    @coverage.skip

    Seq::rev_fold

    fn[A, B] Seq::rev_fold(self : Seq[A], init~ : B, f : (B, A) -> B) -> B

    @coverage.skip

    Seq::set

    #alias("_[_]=_")
    fn[A] Seq::set(self : Seq[A], idx : Int, val : A) -> Unit

    @coverage.skip

    Seq::to_array

    fn[A] Seq::to_array(self : Seq[A]) -> Array[A]

    @coverage.skip

    Seq::unsafe_get

    #alias("_[_]")
    fn[A] Seq::unsafe_get(self : Seq[A], idx : Int) -> A

    @coverage.skip

    Table

    pub(all) struct Table {
    indent : Int
    col_count : Int
    rows : Seq[(Node[TableRow], String)]
    } derive(
    Debug
    )

    Table::new

    fn Table::new(indent? : Int, rows : Seq[(Node[TableRow], String)]) -> Table

    TableAlign

    pub(all) enum TableAlign {
    Left
    Center
    Right
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    TableCellLayout

    pub(all) struct TableCellLayout((String, String)) derive(Compare, Eq,
    Debug
    )

    TableRow

    pub(all) enum TableRow {
    Header(Seq[(Inline, TableCellLayout)])
    Sep(Seq[Node[TableSep]])
    Data(Seq[(Inline, TableCellLayout)])
    } derive(
    Debug
    )

    TableSep

    pub(all) struct TableSep((TableAlign?, Int)) derive(
    Debug
    )

    Tight

    pub(all) struct Tight {
    blanks : String
    node : Node[String]
    } derive(Eq,
    Debug
    )

    Tight block lines: a block line with its initial blanks trimmed but kept for layout.

    Tight::list_text_loc

    Tight::to_string

    fn Tight::to_string(self : Tight) -> String

    is_punctuation

    fn is_punctuation(c : Char) -> Bool

    Check if a character is a Unicode punctuation

    is_whitespace

    fn is_whitespace(c : Char) -> Bool

    Uses charclass library with full Unicode Whitespace property Check if a character is a Unicode whitespace

    layout_empty

    let layout_empty : Node[String]

    layout_of_string

    fn layout_of_string(meta? :
    Meta
    , s : String) -> Node[String]