Skip to content

Emphasis wrapping a link is lost when its label nests the same mark type #213

Description

@Azganoth

Summary

A mark that wraps a link is dropped from every inline node that follows a nested mark of the same type inside the link's label. The loss happens while the file is parsed into the editor document, so an ordinary open and save writes the emphasis away. In one shape the emphasis is not merely lost but lands on different words than the author wrote, and in another the loss reaches text that sits outside the link.

The scope is the parse: carry the wrapping mark across the whole run, without changing how redundant same-type nesting collapses.

Steps to reproduce

  1. Create a Markdown file containing **bold [**a** b](./doc.md) tail**.
  2. Open it in Leafdown.
  3. Edit elsewhere in the document and save.
  4. Reopen the file.

Expected behavior

The authored span stays bold end to end: **bold [**a** b](./doc.md) tail**.

Existing behavior stays stable: a label nesting a different mark type is unaffected, and redundant same-type nesting may still collapse, since **[a **b**](./doc.md)** and **[a b](./doc.md)** mean the same thing.

Actual behavior

The file is written as **bold** [**a** b](./doc.md) tail. Only the leading word keeps its bold; the link and the trailing text lose it.

Measured across shapes:

Input Saved as Result
**[**a** b](./doc.md)** [**a** b](./doc.md) the wrapping bold is dropped
**[a **b** c](./doc.md)** [**a b** c](./doc.md) bold covers different words than authored
**bold [**a** b](./doc.md) tail** **bold** [**a** b](./doc.md) tail text outside the link loses its bold
*[*a* b](./doc.md)* [*a* b](./doc.md) same for emphasis
~~[~~a~~ b](./doc.md)~~ [~~a~~ b](./doc.md) same for strikethrough
**[*a* b](./doc.md)** unchanged a different mark type is unaffected
**[a **b**](./doc.md)** **[a b](./doc.md)** acceptable, the nesting is redundant

Related context

  • Related issues or pull requests: feat: project a link label that contains an image #212, where this surfaced while carrying an image through a projected link label. It is independent of images and of source projection.
  • Documentation, decisions, or prior investigation: measured against main at b4d9e0d through the editor test harness, by mounting each input and reading getMarkdown() back. Round-trip coverage for link labels lives in markdownCompatibility.test.tsx.

Done when

  • A mark wrapping a link survives load and save when the label nests the same mark type, including for text outside the link inside the same wrapper.
  • A different nested mark type, and redundant same-type nesting, keep their current behavior.
  • Regression coverage asserts the round trip through the serialized Markdown.
  • Changelog entry, since authored formatting is currently lost on save.

Notes, logs, screenshots

Diagnosis

remark parses the input correctly. For **[**a** b](./doc.md)** the AST is strong(link(strong("a"), text(" b"))), so the wrapping mark is present after parsing. The document produced by the mdast to ProseMirror conversion is not: its inline nodes are "a"[strong,link] + " b"[link]. The mark is therefore already missing from the document model before any serializer runs, so no serializer change can recover it.

Nesting in the final child keeps the wrapper, while nesting in the first or a middle child loses it. That asymmetry points at the mark set being closed once too often when a nested mark of the same type ends, rather than at anything specific to links.

Implementation direction

The approach is intentionally open, with one constraint: the fix has to land at or before the parse, because the document is already wrong by the time serialization runs.

One untested starting point is to flatten redundant same-type nesting in the mdast before conversion, so strong(link(strong(x), y)) becomes strong(link(x, y)), which would remove the case without depending on how the conversion handles overlapping marks. Whether this is better fixed upstream in Milkdown's parser has not been investigated.

Out of scope

  • Images and source projection. Neither is involved; the case reproduces with plain text in a closed document.
  • How redundant same-type nesting collapses. **[a **b**](./doc.md)** saving as **[a b](./doc.md)** is correct and should stay.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BugSomething isn't working

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions