Expand description
Markdown mode: read a CommonMark file as a tree of heading sections. Read-only — Markdown prose is never written back.
A heading owns everything under it until the next heading of its own level
or shallower, so the document becomes a tree. Each section reports its
heading text and level, its own blocks in source order, the
paragraph subset of those, and its child sections under h2/h3/…
Anything before the first heading is preamble, which carries the same
blocks/paragraph pair.
preamble.blocks.0 → { type: "frontmatter", format: "toml", text: "" }
preamble.paragraph.0 → { type: "paragraph", text: "CI" } (a badge line)
h1.0.text → "Real"
h1.0.paragraph.0 → { type: "paragraph", text: "The lead." }
h1.0.blocks.1 → { type: "code", language: "bash", text: "…" }
h1.0.h2.0.text → "A quick look"Segments are matched by content as well as by index (see
Format::array_rule): h2.look
finds the section headed “A quick look”, and matching several sections is an
error rather than a guess. That is what makes an address survive editing —
h2.3 moves the moment a section is inserted above it, and a top-level
index moves the moment a badge line appears above the title.
Sections are the reason the tree is not flat. A heading level is the one piece of hierarchy CommonMark states outright, and folding on it costs nothing while giving every address a stable frame: content is addressed relative to the heading it lives under, not to the top of the file.
What this backend owns is the specification layer — block identification,
block boundaries, and the heading nesting that follows from levels — and
nothing above it. Where a document’s title lives, whether the first
paragraph is a synopsis, which blockquote carries a prompt: those are one
project’s layout conventions, and they stay with the caller that holds them.
The distinction matters because block boundaries are precisely the part a
hand-rolled line scanner gets wrong: setext headings, the seven HTML-block
start conditions, four-space indented code, and the rule that an ATX heading
interrupts a paragraph are each a rule a ^# regex does not have.
Detection is never automatic: a .md path resolves to no format on its own
and the caller must ask for --input-format markdown. The same file is
legitimately readable as yaml-frontmatter, and a reader that guesses
between two valid readings of one file is the shape-guessing AFDATA avoids.
Deliberately out of scope, so that what this does return is exact:
- No dialects. No GFM tables, footnotes, task lists, or strikethrough.
A table’s rows parse as a paragraph, which is the specification’s answer,
not a defect. (A leading
+++/---metadata block is recognised, as its own block kind — see [options] for why that is not a dialect.) - No parsing or copying of frontmatter fields. It is reported as one
block with
format: "toml"|"yaml"and emptytext;--input-format toml-frontmatteris the reading that turns it into values. Omitting the raw metadata also prevents this structural reading from becoming a way around field-name-based secret redaction. - No recursion into a block’s children. A blockquote or list reports
flattened
text; its inner block structure is not exposed. (Heading sections are nested — that hierarchy comes from the level, not from walking inside a block.) - No byte offsets or columns. Every block does carry 1-based inclusive
source_start_line/source_end_line. Every section carries its whole source range plusheading_end_linefor the heading alone. These are enough to splice whole Markdown blocks without making every consumer implement UTF-8 byte indexing. - No Markdown-to-Markdown transformation. Rewriting a document in place needs source-preserving serialization, which is a separate design, not a rider on a reader.
Functions§
- load
- Parse
contentas CommonMark into the section tree described above.