Skip to content

feat: write a list or heading the editor creates in the prevailing form - #405

Merged
Azganoth merged 1 commit into
mainfrom
feature/prevailing-form
Sep 5, 2026
Merged

feat: write a list or heading the editor creates in the prevailing form#405
Azganoth merged 1 commit into
mainfrom
feature/prevailing-form

Conversation

@Azganoth

@Azganoth Azganoth commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Summary

A construct the editor creates has no authored form, so it was written with a fixed default — * for a bullet list, . for an ordered one, and an ATX heading opened by one space and closed by nothing — whatever the document around it was written in. A list inserted into a file whose lists are all - was written *, and a heading added to a file of setext headings ##. Each was correct by the rules and the file ended up mixed, which is the unrequested rewrite #251 exists to prevent, arriving one construct at a time rather than all at once on first save.

Such a construct now takes the form the document prevails in. Prevailing is settled per axis rather than per construct, because a heading holds three that move independently, and over the whole document. Only the constructs an axis reaches answer for it, so a setext heading spells neither of an ATX heading's runs and an empty heading spells neither a separator nor an underline. One answer is agreement however few constructs give it, and two leave the default, because choosing between them would settle for the author a question the document does not. docs/decisions.md and docs/specification.md carry the rule in full.

A run whose length answers to the construct holding it carries only its presence across: a created heading is closed at the length of its own opening sequence and underlined at the width of its own content, because a length carried from another heading would be a form no file wrote. That is also why the underline is recorded as a form without a run — the length belongs to content a created heading does not have yet and an edit changes under it.

The survey settles over the assembled document rather than reaching each command that creates one of these constructs, which is where the issue expected the answer to arrive. The form attributes now default to null, which is what separates a construct the editor made from one a file wrote in the very form the default names, and a construct still carrying no record is settled on the next transaction. One mechanism therefore reaches every site that can open one, whether a formatting command, an input rule, or a structural edit such as Tab-indent. Settling onto the construct rather than at the save is what makes the answer that construct's own form, so a document that later disagrees with itself does not move a construct already written in it.

The classes settled are a list's marker and a heading's three axes, which are the defaults the issue names. A thematic break, a hard break, a table's outer pipes, and a task marker keep fixed defaults, and both documents record that boundary so the general rule does not read as governing the classes stated below it.

Two existing expectations move with the rule rather than against it. Heading 2 over a selection holding a paragraph beside one closed ATX heading now closes the heading the paragraph becomes, and Tab-indenting an item in a - document nests it under a - list rather than a * one.

Related Issue

Closes #360

Verification

pnpm check:frontend passes, the corpus round trip included. Nothing here touches Rust or Tauri code, so pnpm check:backend was not run.

Focused coverage in markdownCompatibility.test.ts gains a Prevailing form group asserting the written bytes for each axis in both directions: agreement carried, disagreement left to the default, and a document that names no form of its own or holds nothing at all. It reaches a formatting command and an input rule, and keyboardBehavior.test.ts reaches the structural edit. The inheritance rows were confirmed to discriminate rather than restate the implementation by running the group against the pre-change source, where they fail while the guard rows still pass. A convergence row asserts each created form reopens as the document that wrote it, which is what covers the heading whose record names no underline length.

Measured by throwaway probe, not kept: undoing a created construct is one step, so the settling transaction does not land in history as an event of its own; and a created setext heading raised past level two writes ATX and returns to setext when lowered, so the record survives a level change on the rule #359 settled.

Not verified: no manual pass of Leafdown in the assembled application, and the desktop E2E suite was not run locally. Its fixtures hold no list or heading command.

Notes

Whether the characters typed into an input rule name a form is left open. Typing + in a document that prevails in * still writes *, as it wrote * before this change. The question is whether what the author types names a form or only a construct, it reaches the ordered delimiter the same way, and this issue does not answer it.

@Azganoth Azganoth added the Feature New feature or request label Sep 5, 2026
@Azganoth Azganoth self-assigned this Sep 5, 2026
@Azganoth
Azganoth merged commit 532f540 into main Sep 5, 2026
3 checks passed
@Azganoth
Azganoth deleted the feature/prevailing-form branch September 5, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A construct the editor creates ignores the form the document is written in

1 participant