Skip to content

fix: project a link label holding a footnote reference - #237

Merged
Azganoth merged 1 commit into
mainfrom
bug/link-label-footnote-reference
Aug 15, 2026
Merged

fix: project a link label holding a footnote reference#237
Azganoth merged 1 commit into
mainfrom
bug/link-label-footnote-reference

Conversation

@Azganoth

Copy link
Copy Markdown
Owner

Summary

A link whose label held a footnote reference exposed no source projection. A caret anywhere in the label, or a selection over its text, left the projection closed, so the link's own punctuation could not be inspected or edited. Only the nested reference projected, on its standalone adapter.

The reference node failing isSupportedLinkNode was the first gate. Behind it, the label's source map parsed with remark, which reads [^label] as a reference only while a matching definition sits in the same document, and a projected label carries the inline run alone. The map therefore measured [^follow-up] as twelve characters of text against one document position, and the size check rejected the target even once the node was admitted.

  • Admit a footnote reference to the label, as a segment standing for one document position, the way an image already does.
  • Move the marked fragment's definition injection to the reference syntax module and apply it to the link's source map and to both parse-back paths. A map now rejects a block that starts inside the source it was given, so a label that carries its own definition stays literal rather than silently dropping it.
  • Serialize a label holding a reference through the logical-link serializer. It skipped such a run for the same reason the projection never opened, saving [**bold** label[^note]](./doc.md) as two links.
  • Treat a reference like an image when a selection covers part of it, in the label and in a marked fragment that wraps one, since the rich payload can carry the whole object or none of it.

Related Issue

Closes #236

Verification

src/features/editor/plugins/sourceProjection.test.tsx covers entry from a caret in the label, from a selection over its text, and from either reference boundary with the mapped source offset; the label and reference presentation ranges; a clean restore of the original document; and a committed edit that round-trips the label, the reference with its link mark and destination, and the definition. It also covers a marked fragment whose link label holds a reference, which is the parse path the mark adapter takes.

src/features/editor/tests/markdownCompatibility.test.tsx covers four round trips through the document serializer, including the mixed-format label that previously saved as two links. src/features/editor/tests/sourceProjectionClipboard.test.tsx covers a reference pasted into an open label as its Markdown source. src/features/editor/utils/sourceProjectionFootnoteReferenceSyntax.test.tsx covers the definition injection: no reference, one reference, a repeated label, an escaped bracket in a label, and an unescaped one.

Measured the candidate scan against the pattern read out of the source file. On realistic projected sources it costs between 0.17 and 0.75 microseconds, against a remark parse that follows it and costs orders of magnitude more. On "[^" repeated 64000 times it took 7431 ms before excluding the unescaped bracket and 0.18 ms after.

Not verified: no manual pass in a desktop build. The behavior is observable through opening a document, reading the projected source, and reading the serialized Markdown back, which the cases above do directly.

Notes

Three existing cases described behavior this change supersedes. Two pinned a footnote reference as a node the projected link source cannot hold; the source holds one now, so they moved to a hard break, which it still cannot hold, and a reference pasted as its source joins the image case beside them. One drove [Text[^note]](https://example.com) into a marked fragment as unsupported content; that shape is supported now, so it uses an image, which a marked fragment still rejects.

The quadratic candidate scan is not introduced here. The pattern already ran on every marked-fragment source, and this change widened its reach to links, which is where the measurement came from. A label cannot hold an unescaped bracket, so excluding it removes an over-match the scan previously accepted, and it bounds each candidate to the next bracket rather than letting a run of unterminated [^ rescan the rest of the source for every one of them.

Ownership precedence is unchanged. A node selection on a reference inside a label still belongs to the standalone adapter, as [**left[^note]**](https://example.com) already did.

A reference in a projected label renders as one content span, like an image, rather than splitting its brackets into markers as a marked fragment does. Presentation only, and left as its own question.

Admitting the reference node to the label was not enough: the label's
source map parsed with remark, which reads `[^label]` as a reference
only while a matching definition is in the same document, and a
projected label carries the inline run alone. The definition injection
the marked fragment already used moves to the reference syntax module
and now covers the link's map and both parse-back paths, which is also
why a map rejects a block starting inside the source it was given.

The logical-link serializer skipped a run whose label held a reference
for the same reason it never projected, saving `[**bold** a[^n]](./x)`
as two links.

The candidate scan excludes an unescaped bracket, which a label cannot
contain and which let a run of unterminated `[^` scan quadratically.
@Azganoth Azganoth added the Bug Something isn't working label Aug 15, 2026
@Azganoth Azganoth self-assigned this Aug 15, 2026
@Azganoth
Azganoth merged commit 3c99531 into main Aug 15, 2026
3 checks passed
@Azganoth
Azganoth deleted the bug/link-label-footnote-reference branch August 15, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A footnote reference in a link label prevents the link source projection from opening

1 participant