T-W7D5-rebase-markdown-extract-navigation-on-markdown-contract
Status: closed/done · Impact: low · Complexity: medium
plugin/lib/util/markdown_extract.ts already parses through markdown-contract
(its parseMarkdown wrapper keeps mc’s .mdast), but sectionBody still
hand-iterates parsed.tree.children, matches a heading by plain text, then
scans forward for the next same-or-shallower heading and slices parsed.source
between the two offsets. mc 0.2.0 (already vendored) exposes section-navigation
helpers that return exactly those boundaries, so the bespoke child-iteration
plus source slicing can be deleted in favour of the shared front-end. This is
the read-side follow-on to the T-K1A8 rebase that first routed this module
through mc.
| Location | Role today |
|---|---|
plugin/lib/util/markdown_extract.ts#sectionBody | Hand-iterates parsed.tree.children, matches a depth-N heading by plain text, forward-scans for the next heading of the same or shallower depth, and slices parsed.source between the heading offsets, then trims |
plugin/lib/util/markdown_extract.ts#hasBlockId | Regex-tests parsed.body for a trailing Obsidian ^blockId token |
plugin/lib/util/markdown_extract.ts#extractBlockIdText | Regex-locates the block whose last line carries ^blockId and returns its text |
plugin/lib/util/markdown_extract.ts#parseMarkdown | The mc-backed wrapper: calls mc parse, keeps .mdast as tree, plus source/body/frontmatter |
plugin/lib/util/tests/markdown_extract.test.ts | Behavioural suite pinning sectionBody and block-id output |
Proposed
Section titled “Proposed”sectionBody resolves its section through mc’s navigation helpers rather than a
manual mdast child walk. parseMarkdown (or sectionBody directly) also holds
mc’s section-tree root (SectionNode) alongside the existing .mdast, so
findSection / sectionSpans / sectionForLine / sectionsAt can locate the
target section and its body extent. The returned string is byte-identical to
today’s output for every input, including the trim and the same-or-shallower
heading boundary. No hand-rolled next-sibling offset scan remains in
sectionBody.
Approach
Section titled “Approach”- Expose mc’s section-tree root from the parse wrapper: have
parseMarkdownalso retain theSectionNoderoot that mcparsereturns (today it keeps only.mdast), or call mcparseinsidesectionBody. Add it to theParsedMarkdownshape if callers benefit. - Rewrite
sectionBodyto locate the target withfindSection(root, headingText, { depth })and derive its body extent from the section node (viasectionSpans/ the node’s ownpos). The mc section tree nests by heading depth, so a depth-N section’s extent already ends at the next same-or-shallower heading — this reproduces the current<= depthboundary without a flat same-depth filter. - Slice
parsed.sourceto the resolved extent and.trim(), matching the current return exactly (returnnullwhen no such heading exists). - Delete the manual
parsed.tree.childrenloop and next-sibling offset scan. - Leave
hasBlockId/extractBlockIdTextbespoke: mc 0.2.0 exports no block-id lookup helper (its index exports section, table, block, and code-fence helpers only), and it owns the Obsidian dialect at parse time but surfaces no^blockIdnavigation. Block-id handling stays as-is; note this in the PR so a future mc release can revisit it. - Run the suite; confirm byte-identical
sectionBodyoutput.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/lib/util/markdown_extract.ts#sectionBody | modify | Replace the manual mdast child-iteration + source slicing with mc findSection / sectionSpans navigation; keep byte-identical output |
plugin/lib/util/markdown_extract.ts#parseMarkdown | modify | Retain mc’s SectionNode root (alongside .mdast) so the navigation helpers have a root to query |
plugin/lib/util/tests/markdown_extract.test.ts | modify | Extend or confirm coverage that sectionBody output is unchanged across depths and the shallower-heading boundary |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
sectionBodycontains no hand-rolled iteration overparsed.tree.childrenand no manual next-heading offset scan; it resolves its section through mc’sfindSection/sectionSpans(or the siblingsectionForLine/sectionsAt) navigation helpers. - AC-2:
sectionBodyoutput is byte-identical to the pre-change output for every input the suite exercises, including the trim and the same-or-shallower heading boundary and thenull-on-absent case. - AC-3:
plugin/lib/util/tests/markdown_extract.test.tspasses unchanged (or with additions only, no expectation weakened). - AC-4:
hasBlockId/extractBlockIdTextare either rebased onto an mc block-id helper OR left bespoke with a one-line note in the PR stating mc 0.2.0 exposes no block-id equivalent.
Out of scope
Section titled “Out of scope”- Block-id handling (
hasBlockId/extractBlockIdText) — stays bespoke unless a mc block-id helper exists; see AC-4. - The
services/docscopy of markdown_extract behaviour and its own test file. - Frontmatter reading,
entityFiles/entityDirs, or any non-section helper.
Dependencies
Section titled “Dependencies”- none — enabled by the already-vendored
markdown-contract0.2.0 (vendor/markdown-contract-0.2.0.tgz); no re-vendor needed. The sibling rebasesT-K1A8(this module onto mcparse),T-ZGO4, andT-FN18are already shipped (closed/done), so this is additive read-side cleanup with no blocking predecessor.
Discovery context
Section titled “Discovery context”- Surfaced by a read-side audit of remaining bespoke markdown navigation after
the markdown-contract adoption (milestone M-0009).
sectionBodywas the one navigation pathT-K1A8left hand-rolled when it routed this module’s parse through mc.