Summary
Typing a backslash into projected link source turns the link literal, which is what the specification describes, but the backslash lands in the document as an ordinary character rather than as the escape it spells. The run then saves with three backslashes where one is enough, and the resulting document differs from the same file authored by hand.
This is the gesture #245 read from the specification as the inverse of #255's, and recorded as inferred rather than observed. It is reachable today with no other change in place.
Steps to reproduce
- Open a document containing
[a](b) tail.
- Place the caret in the link so its source projects, and type
\ at the start of [a](b).
- Move the caret out of the run and save.
Expected behavior
The same state a file holding \[a](b) tail produces: the document holds [a](b) tail as literal text with no backslash character in it, and the file saves as \[a](b) tail.
The link must still become literal, which it already does. A backslash the author means as ordinary text must still survive as one.
Actual behavior
Measured against f409cdbf by driving the editor mount used by the plugin tests, comparing the gesture against the same run authored with the escape. The documents are compared as ProseMirror documents rather than as strings, so the comparison sees literalness rather than form.
|
Typing \ into projected source |
Authored \[a](b) tail |
| Links after the caret leaves |
0 |
0 |
| Document text |
\[a](b) tail |
[a](b) tail |
| Saved |
\\\[a](b) tail |
\[a](b) tail |
The same holds on a realistic link. see [test link](./test.html) tail gives document text see \[test link](./test.html) tail and saves as see \\\[test link](./test.html) tail, against see [test link](./test.html) tail and see \[test link](./test.html) tail for the authored form.
The wrong state is stable rather than self-correcting: reopening \\\[a](b) tail returns the document holding the backslash and saves it back unchanged, so nothing later recovers the escape the author meant.
Related context
Done when
Notes, logs, screenshots
Diagnosis
sourceProjectionLinkAdapter.ts falls back to createLiteralSourceProjectionSlice(state, source) when parseLinkSource rejects the edited source, and that slice is built from the raw source string. The backslash the author typed is part of that string, so it enters the document as text like any other character.
The serializer is then correct: a document holding a literal backslash followed by a bracket needs \\ for the backslash and \[ for the bracket, which is where the third one comes from. The defect is in the commit, not in escaping, so it is not one of the classes under #251.
Verified with a throwaway probe under src/features/editor/tests/, run with vitest and deleted. To reproduce: mount the source, enter projection through the rendered a element, place the selection at the start of the source run, type \, move the selection to the document end, then read doc.textContent and getMarkdown() and compare view.state.doc against a mount of the hand-authored escape.
Implementation direction
One constraint and one open decision.
The constraint is that both directions have to keep working. A backslash typed into projected link source has to mean "escape this run", while a backslash the author wants as text still has to reach the document as one, and only the projection context separates them.
The open decision is where the exception lives. docs/specification.md says an invalid edit to projected source becomes exact literal text, and consuming a leading backslash is an exception to that rule rather than an application of it. Whether it belongs in the adapter's parseSource, in the projection commit ahead of the adapter, or in the specification's wording is not settled here.
Out of scope
Summary
Typing a backslash into projected link source turns the link literal, which is what the specification describes, but the backslash lands in the document as an ordinary character rather than as the escape it spells. The run then saves with three backslashes where one is enough, and the resulting document differs from the same file authored by hand.
This is the gesture #245 read from the specification as the inverse of #255's, and recorded as inferred rather than observed. It is reachable today with no other change in place.
Steps to reproduce
[a](b) tail.\at the start of[a](b).Expected behavior
The same state a file holding
\[a](b) tailproduces: the document holds[a](b) tailas literal text with no backslash character in it, and the file saves as\[a](b) tail.The link must still become literal, which it already does. A backslash the author means as ordinary text must still survive as one.
Actual behavior
Measured against
f409cdbfby driving the editor mount used by the plugin tests, comparing the gesture against the same run authored with the escape. The documents are compared as ProseMirror documents rather than as strings, so the comparison sees literalness rather than form.\into projected source\[a](b) tail\[a](b) tail[a](b) tail\\\[a](b) tail\[a](b) tailThe same holds on a realistic link.
see [test link](./test.html) tailgives document textsee \[test link](./test.html) tailand saves assee \\\[test link](./test.html) tail, againstsee [test link](./test.html) tailandsee \[test link](./test.html) tailfor the authored form.The wrong state is stable rather than self-correcting: reopening
\\\[a](b) tailreturns the document holding the backslash and saves it back unchanged, so nothing later recovers the escape the author meant.Related context
Residual uncertainty; Typed link source becomes a link #238 is the parent that scopes reverting an unwanted conversion; Commit typed link source to a link when the caret leaves it #239 and its pull request treat it as the reversal path available after a savedocs/specification.mdInline Content, which states that invalid or incomplete edits to projected source become exact literal text, anddocs/architecture.mdSource ProjectionDone when
Notes, logs, screenshots
Diagnosis
sourceProjectionLinkAdapter.tsfalls back tocreateLiteralSourceProjectionSlice(state, source)whenparseLinkSourcerejects the edited source, and that slice is built from the raw source string. The backslash the author typed is part of that string, so it enters the document as text like any other character.The serializer is then correct: a document holding a literal backslash followed by a bracket needs
\\for the backslash and\[for the bracket, which is where the third one comes from. The defect is in the commit, not in escaping, so it is not one of the classes under #251.Verified with a throwaway probe under
src/features/editor/tests/, run with vitest and deleted. To reproduce: mount the source, enter projection through the renderedaelement, place the selection at the start of the source run, type\, move the selection to the document end, then readdoc.textContentandgetMarkdown()and compareview.state.docagainst a mount of the hand-authored escape.Implementation direction
One constraint and one open decision.
The constraint is that both directions have to keep working. A backslash typed into projected link source has to mean "escape this run", while a backslash the author wants as text still has to reach the document as one, and only the projection context separates them.
The open decision is where the exception lives.
docs/specification.mdsays an invalid edit to projected source becomes exact literal text, and consuming a leading backslash is an exception to that rule rather than an application of it. Whether it belongs in the adapter'sparseSource, in the projection commit ahead of the adapter, or in the specification's wording is not settled here.Out of scope