fix: project a link label holding a footnote reference - #237
Merged
Conversation
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.
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 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
isSupportedLinkNodewas 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.[**bold** label[^note]](./doc.md)as two links.Related Issue
Closes #236
Verification
src/features/editor/plugins/sourceProjection.test.tsxcovers 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.tsxcovers four round trips through the document serializer, including the mixed-format label that previously saved as two links.src/features/editor/tests/sourceProjectionClipboard.test.tsxcovers a reference pasted into an open label as its Markdown source.src/features/editor/utils/sourceProjectionFootnoteReferenceSyntax.test.tsxcovers 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.