fix: keep a thematic break in the run it was authored with - #338
Merged
Conversation
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.
Summary
A thematic break is now written with the run it was authored with.
hrSchemacarried no attributes, so***,---,_ _ _, and every other accepted spelling parsed into the same node and came back as***, rewriting every break in a file on its first save.A break holds no children, so the characters it was written with are the slice of the file it was built from and nothing else.
leafdownThematicBreakstamps that slice onto the mdast node the wayleafdownCharacterReferencerecovers a reference, thehrschema carries it as an attribute, and athematicBreakstringify handler writes it. Indentation stands outside the slice and the whitespace closing the line is trimmed off it, so what is kept is the characters and the spacing between them. Tabs survive:positionoffsets index the original source rather than the tab stops the parser expands them to, so this is a serializer difference and not the read-time one #319 asked to have settled.The rendered separator carries the run in
data-marker, so a break keeps its spelling through a copy and a paste as an autolink already does.Two positions read a preserved run back as something other than a break, and both give way to the default rather than write a line the next open would disagree with:
mdast-util-to-markdownjoins a tight list item's children with a single newline, so a run of hyphens written after a paragraph there underlines it into a setext heading. The join is read offstate.joinby the resolutionbetweenuses itself, so the answer cannot drift from the blank line the serializer actually emits.rulecharacter it was configured with, but that character cannot answer for a run the node carries, so the run gives way rather than the bullet.* ---\n Paragraphwas otherwise saving as- ---\n Paragraph, which reopens with the list gone.A break the editor inserts still writes
***, which is also both fallbacks: it is the one spelling no position reads as anything else.Related Issue
Closes #319
Verification
markdownCompatibility.test.tsgainsThematic break form, which pins the written bytes for each accepted spelling across a save: seven runs including_ _ _,- - -,----------, and a tab-separated*\t*\t*; three rows where indentation or trailing whitespace is dropped to the run it spells; three containers that keep the run, including a tight list item; and one row per fallback. The setext row sets the marker attribute on a break already sitting in a tight item, which is the arrangement no source can produce and an edit can.nativeClipboard.test.tsgains two rows carrying---and_ _ _through a copy and a paste, beside the autolink rows that prove the same thing for a mark.blocks.test.tsasserts the run an inserted rule writes, since a break read from a file now writes its own and one the editor creates needs a defined one.markdownCompatibility.test.tsexpected***from the---in its documented CommonMark fixture. That expectation encodes the behavior this replaces and was updated rather than worked around; the comment above it no longer lists thematic breaks among the markers the serializer normalizes.Measured by diffing
corpus/commonmark/blocks.mdagainst its first save: the three breaks underAsterisks, hyphens, and underscores form thematic breaksand the one underAn asterisk thematic break interrupts a paragraph insteadno longer differ. The differences that remain in that file are setext headings, ATX closing sequences, and bullet markers, which belong to other classes. No corpus fixture is added: the round-trip assertions compare a save against the save before it and converge either way, whileThematic break formcompares the written bytes, which is what distinguishes the two behaviors.pnpm check:frontendpasses. The backend is untouched, sopnpm check:backendwas not run.Not verified: the manual pass over
corpus/in the running application, which covers rendering, interaction, and navigator behavior beyond the automated round-trip.Notes
docs/reference.mdlists a deferred Horizontal rule marker setting whose default is---, while the implemented fallback is***. The two are not the same thing — the setting names what an author picks for a break Leafdown must choose a run for, and the fallback is the run no line can misread — but the disagreement is worth settling when the setting lands. Left alone here rather than changed, since the default of a product setting is not this issue's to move.A run opening a list item whose bullet shares its character still normalizes:
* ---saves as- ***. Preserving it means deciding the bullet from the runs the items carry rather than from the configuredrule, which is bullet scope, and a list mixing a***opener with a---one would still need the fallback for one of them.A
---under paragraph text with no blank line between them is a setext underline and belongs to the heading class, as does the--------------------------------inblocks.md. Neither is touched.