📖 [Docs]: Markdown object model documents the section shape, its depth limits, and the full node schema - #37
Draft
Marius Storhaug (MariusStorhaug) wants to merge 10 commits into
Conversation
A section now carries its heading level, title as inline nodes, and heading style, instead of containing a separate heading node. FR2, FR4, FR6, FR7, FR8 and FR10 are reworded in place, and the acceptance criteria gain a scenario for inline markup in a title surviving a parse and render cycle. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
FR14 states both bounds the model has always had but never wrote down: a chain of nested sections stops at six, because sections nest only on strictly increasing levels and an ATX heading carries one to six hash characters, while total tree depth is unbounded because sectioning restarts inside every block container. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
FR15 to FR20 state what a comment is in the model: exact-block recognition with the trailing-content rule the CommonMark HTML block end condition implies, inner text and a terminated indicator, unterminated comments running to the end of the document, the degenerate forms, preservation of delimiters and inner spacing, and comment-looking text in code excluded. Acceptance criteria gain a matching feature. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The at a glance tree still showed a Heading child under Section. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The section carries Level, Title as inline nodes, and Style, so there is no heading node left in the model. Updates the approach prose, the tree and containment diagrams, the node members table, rendering, and section addressing, and records the two rejected shapes in the alternatives table: the earlier Section wrapping a heading node, and the string-title shape prototyped in #18. Traversal now yields a section's title inlines before its children, which keeps FR8 true and keeps heading links reachable from Descendants(). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The pseudocode covered three; the fourth was only implicit in the function returning. All four are now stated as prose beside it, and the pseudocode is updated onto the merged section shape. Adds the note that the pass runs before inline parsing, and the observation that the six-level nesting bound falls out of rule 1 rather than being enforced. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The comment-plus-trailing-content case is example 177. Example 172 is the style element case. Verified against spec.txt for 0.31.2. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The complete inventory - every class, property and CommonMark section reference - existed only in the body of #8, so it would have become a closed issue body once that Epic closed. It now lives in the design: type hierarchy, worked example, shared members, document, blocks, inlines, enums, and the constructs that are deliberately not nodes. Adapted to evergreen present tense, with the merged section shape applied as it moves: MarkdownHeading is gone, MarkdownSection carries Level, Title and Style, and headings join the list of constructs that are not nodes. Two CommonMark section numbers are corrected on the way in - textual content is 6.9 and entity references are 2.5. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comments occur at block and inline level, and a PowerShell class has a single base, so one type cannot derive from both MarkdownBlock and MarkdownInline. The model carries MarkdownCommentBlock and MarkdownComment, both reporting a Type of Comment so one query finds every comment. Records the rejected single type outside the block and inline split, and the rejected IsComment flag on the existing HTML nodes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CommonMark 4.6 ends an HTML block at the last line of the document OR the last line of the container block holding it, whichever comes first. FR17 stated only the first, which is wrong for a comment inside a block quote or a list item - content after the container would have been swallowed. Adds an acceptance scenario for the container case and updates the IsTerminated note. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Markdown object model documentation now describes the section shape the model actually has, states the depth limits it works within, and carries the complete node schema — every class, every property, and the CommonMark section each one derives from — in one place instead of in an issue body.
Changed: A section carries its heading instead of containing one
A section exposes the heading that opens it directly, as a level, a title, and a heading style, so
$section.Leveland$section.Titleare one access rather than two. There is no separate heading node, which meansDescendants('Section')is how headings are found and there is no traversal rule to remember about where a heading is yielded.A title holds inline nodes rather than a string, so
# Release **1.2** noteskeeps its emphasis instead of flattening to text that cannot be restored.GetTitleText()reads a title as plain text when that is all that is wanted.New: The depth limits are written down
A chain of nested sections stops at six, because sections nest only where heading levels rise and an ATX heading carries one to six
#characters. Total depth of a document is unlimited, because sectioning starts over inside every block quote and list item — a block quote inside a level 6 section may open its own level 1 section. Both facts are stated as requirements rather than left to be rediscovered.New: All four rules for where content lands
The rules that decide which section a block belongs to are stated in full: which sections a heading closes, where the new section attaches, where a block that is not a heading attaches, and what happens at the end of a container. The fourth was previously only implied by the algorithm ending.
New: The complete node schema, next to the specification
Every node type — the document, the blocks, the inlines, the enums, and the constructs that are deliberately not nodes — is documented in the design, with a worked example showing what a small document parses into. It was previously available only in the body of the epic issue that tracks the work, so it would have become unreachable as a source of truth the moment that issue closed.
New: Comments are a node type
A comment is its own kind of node at block level and at inline level, so finding a generated-region marker or a linter directive no longer means scanning raw HTML for text that starts with
<!--. A comment exposes its inner text without delimiters and reports whether it was terminated, and it re-renders exactly as written. A block that carries a comment followed by other content on the same line stays an HTML block, because the specification makes the whole line one block. An unterminated comment ends with the block quote or list item holding it, and reaches the end of the document only when nothing encloses it.Technical details
Documentation only. No classes, parser, renderer, or tests — those remain to be delivered against these documents.
docs/markdown-object-model/spec.md— FR2, FR4, FR6, FR7, FR8 and FR10 reworded in place forMarkdownSection { Level, Title: MarkdownInline[], Style, Children }; FR14 adds the depth bounds; FR15 to FR20 add the comment model. Identifiers are append-only, so nothing was renumbered. Two acceptance scenarios added for section behaviour and one new gherkin feature added for comments.docs/markdown-object-model/design.md— approach prose, the text tree, the containment diagram and the class diagram updated;MarkdownHeadingremoved from the inventory and recorded under the constructs that are deliberately not nodes; the alternatives table gains the rejectedSection { Heading, Children }split and the rejected string-title shape prototyped in 🚀 [Minor]: Markdown documents can now be parsed into objects and regenerated #18; thesectionizepseudocode updated and the four closure rules stated beside it; the node schema moved in from Add a specification-compliant markdown object hierarchy with ConvertFrom-Markdown and ConvertTo-Markdown #8; the two-comment-type decision recorded with its rejected alternatives.docs/markdown-object-model/index.md— the at-a-glance tree still showed aHeadingchild.### The object hierarchyblock replaced by a pointer to the design, and the remaining superseded-shape references corrected. Recorded in a comment on that issue.Decisions taken that the source issues left open or did not cover:
Title. Removing the heading node removes the rule that yielded it before the section's children, which would have left the title inlines unreachable fromDescendants()— breakingFR8and silently dropping every link written inside a heading from$doc.Descendants('Link').Descendants()therefore yields a section's title inlines before its children: the same single node-aware step, moved fromHeadingtoTitle.FR17states both, with an acceptance scenario for the container case.MarkdownCommentBlock : MarkdownBlockandMarkdownComment : MarkdownInline, mirroring the existingMarkdownHtmlBlockandMarkdownRawHtmlpair. Model comments as their own node type rather than raw HTML #36 settles the count but names neither.<style type="text/css">case. Verified againstspec.txtfor 0.31.2, which also confirms the ATX opening sequence of 1–6#characters, the HTML block end condition, and<!-->and<!--->as comments. Model comments as their own node type rather than raw HTML #36 carries the same incorrect number in its body.Reported and not changed:
spec.mdpredates the current Spec-Driven Development convention of per-requirement#### Behavioral scenariosandAC-labelled cross-cutting criteria, and still uses one consolidated gherkin block. Restructuring it is a separate change.Implementation plan progress: completes the two specification tasks in #36 and the first two documentation tasks arising from #32. The model, parser, renderer, and test tasks in both remain open.
Standards and framework alignment:
docs/**(Markdown)Issue convergence sweep: scoped to the three linked issues. None is fully satisfied by this diff — each retains open model, parser, renderer, and test work — so no closing keyword is used.
Linters, run against
.github/linters/configs:markdownlint-cli20 issues,textlintwith the terminology rule 0 issues,codespell0 issues.The
Process-PSModule / Get-Settingscheck is red here for a reason unrelated to this diff: the pinned reusable workflow referencespsmodule/get-psmodulesettings, which no longer exists, so every downstream job is skipped. The last four scheduled runs onmainfailed the same way. Filed as #38.Relevant issues (or links)
Related work