mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 19:58:28 +01:00
Answer prose round 1: 0 🔴, 8 🟡; split the image-in-link ruling out, place the (image) plainly, credit only the same-URL rule to the portable writer
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@
|
|||||||
around only code spans is `unsupported-node-shape`. `adfToLosslessMarkdown` writes code text
|
around only code spans is `unsupported-node-shape`. `adfToLosslessMarkdown` writes code text
|
||||||
holding another mark as `!adf:carry{json="…"}`, and `adfToPortableMarkdown` drops that mark.
|
holding another mark as `!adf:carry{json="…"}`, and `adfToPortableMarkdown` drops that mark.
|
||||||
- Every reader keeps the URL and title of an image inside a link's text, where it dropped them:
|
- Every reader keeps the URL and title of an image inside a link's text, where it dropped them:
|
||||||
`(image)` follows the link, linked to the image, so
|
after the link comes `(image)`, which links to the image, so
|
||||||
`[look at  tonight](/moon)` reads as
|
`[look at  tonight](/moon)` reads as
|
||||||
`[look at the moon tonight](/moon) ([image](moon.png "Full"))`. A link to the image's own URL
|
`[look at the moon tonight](/moon) ([image](moon.png "Full"))`. A link to the image's own URL
|
||||||
takes no `(image)`, unless the image's title is one the link lacks.
|
takes no `(image)`, unless the image's title is one the link lacks.
|
||||||
|
|||||||
@@ -554,8 +554,8 @@ Serves [Goals](#goals) 1, 4 and 5.
|
|||||||
blank line between it and any other line.
|
blank line between it and any other line.
|
||||||
- A code span inside emphasis reads as code without the emphasis, since ADF gives code text no
|
- A code span inside emphasis reads as code without the emphasis, since ADF gives code text no
|
||||||
mark but a link or an annotation: ``**`npm ci`**`` reads as `` `npm ci` ``.
|
mark but a link or an annotation: ``**`npm ci`**`` reads as `` `npm ci` ``.
|
||||||
- Inside a link, an image reads as plain text the link marks, and `(image)` follows the link,
|
- Inside a link, an image reads as plain text the link marks. After the link comes `(image)`,
|
||||||
linked to the image: `[look at  tonight](/moon)` reads as
|
which links to the image: `[look at  tonight](/moon)` reads as
|
||||||
`[look at the moon tonight](/moon) ([image](moon.png))`. A link to the image's own URL takes
|
`[look at the moon tonight](/moon) ([image](moon.png))`. A link to the image's own URL takes
|
||||||
no `(image)`, unless the image's title is one the link lacks. A link alone in its paragraph
|
no `(image)`, unless the image's title is one the link lacks. A link alone in its paragraph
|
||||||
whose whole text is one image reads as that image, with the link marking it.
|
whose whole text is one image reads as that image, with the link marking it.
|
||||||
|
|||||||
+13
-11
@@ -243,15 +243,17 @@ reads as the URL, and an empty link text as its destination. A link text holding
|
|||||||
reads as its destination, then those breaks: four of seven readers expected that over dropping them.
|
reads as its destination, then those breaks: four of seven readers expected that over dropping them.
|
||||||
`[]()` reads as nothing: it renders nothing, so Goal 7 loses no content. A link alone in its
|
`[]()` reads as nothing: it renders nothing, so Goal 7 loses no content. A link alone in its
|
||||||
paragraph whose whole text is one image reads as that image, with the link marking its `media`.
|
paragraph whose whole text is one image reads as that image, with the link marking its `media`.
|
||||||
Inside any other link, the alt text is plain text the link marks, and ` (image)` follows the link,
|
|
||||||
`image` linked to the image's URL and titled by its title, so Goal 7 keeps both. For a link whose
|
Inside any other link, the alt text is plain text the link marks. After the link comes ` (image)`,
|
||||||
text is the image alone, the bot, the LLM pipeline and a support agent picked this 3 of 3. For a
|
where `image` links to the image's URL and carries its title, so Goal 7 keeps both. Three readers,
|
||||||
link holding words beside the image they split on where `(image)` stands, and four more readers, the
|
the bot, the LLM pipeline and a support agent, picked this 3 of 3 for a link whose text is the image
|
||||||
bot, the LLM pipeline, a product manager and an engineer, made it 5 of 7 for after the whole link
|
alone. For a link with words beside the image, the three split on where `(image)` goes. Four fresh
|
||||||
over right after the alt text. Every reader rejected linking the alt text to the image: the words
|
readers joined: a second bot, a second LLM pipeline, a product manager and an engineer. Five of the
|
||||||
lose the link they were written in. A link to the image's own URL takes no `(image)`, unless the
|
seven put `(image)` after the whole link rather than right after the alt text. Every reader rejected
|
||||||
image holds a title the link does not: it would repeat the link, as `adfToPortableMarkdown` never
|
linking the alt text to the image, since the words would lose the link they were written in.
|
||||||
writes it (§A linked image standing inline in portable markdown).
|
|
||||||
|
A link to the image's own URL takes no `(image)`, since the link already reaches the image. The
|
||||||
|
exception is an image whose title the link lacks: there `(image)` keeps the title.
|
||||||
|
|
||||||
In `portableMarkdownToAdf`, a `[!WORD]` marker with no `+` or `-` stands apart from the lines after
|
In `portableMarkdownToAdf`, a `[!WORD]` marker with no `+` or `-` stands apart from the lines after
|
||||||
it, and so does a `+` or `-` callout's title line, so an image alone on the marker line or after the
|
it, and so does a `+` or `-` callout's title line, so an image alone on the marker line or after the
|
||||||
@@ -315,8 +317,8 @@ linked text.
|
|||||||
Where `adfToPortableMarkdown` writes an external image inline, as in a table cell, and a link to
|
Where `adfToPortableMarkdown` writes an external image inline, as in a table cell, and a link to
|
||||||
another target marks it, the link keeps its target and the image follows as a link reading `image`.
|
another target marks it, the link keeps its target and the image follows as a link reading `image`.
|
||||||
With no alt text, the link text is the link's target, which is how the reader reads `[](href)`, so
|
With no alt text, the link text is the link's target, which is how the reader reads `[](href)`, so
|
||||||
the rewrite holds. Every seat first wrote `[](href)`, which reads back as linked text
|
the rewrite holds. Every seat first wrote `[](href)`, but the reader reads that as
|
||||||
written in another spelling, and so breaks the byte-for-byte rewrite. Each then picked this over
|
`[alt](href) ([image](url))`, so writing it breaks the byte-for-byte rewrite. Each then picked this over
|
||||||
`[alt](url)` and `[alt](href)`, which drop a target.
|
`[alt](url)` and `[alt](href)`, which drop a target.
|
||||||
|
|
||||||
## Ids stay site-local
|
## Ids stay site-local
|
||||||
|
|||||||
+7
-6
@@ -328,12 +328,13 @@ its alt text linked to its destination, the link titled by the image's title: be
|
|||||||
content, in a heading, a table cell or an `!adf:paragraph`, and in the paragraph a portable task
|
content, in a heading, a table cell or an `!adf:paragraph`, and in the paragraph a portable task
|
||||||
marker opens. A `[!WORD]` marker with no `+` or `-` stands apart from the lines after it, and so
|
marker opens. A `[!WORD]` marker with no `+` or `-` stands apart from the lines after it, and so
|
||||||
does a `+` or `-` callout's title line, so an image alone on the marker line or after the title
|
does a `+` or `-` callout's title line, so an image alone on the marker line or after the title
|
||||||
reads as the image. An empty alt text reads as the destination, as an empty link text does. Inside
|
reads as the image. An empty alt text reads as the destination, as an empty link text does.
|
||||||
a link's text, an `!adf:link`'s included, the alt text is plain text the link marks, and after the
|
|
||||||
link stands ` (image)`, `image` linked to the image's destination and titled by its title, one for
|
Inside a link's text, the alt text is plain text the link marks. This holds for an `!adf:link`'s
|
||||||
each image in the order they stand. An image whose destination is the link's own takes none, unless
|
text too. After the link comes ` (image)` for each image, in the order the images stand. Each
|
||||||
it holds a non-empty title the link does not (`docs/decisions.md` §An image reads as an image only
|
`image` links to its image's destination and carries the image's title. An image whose destination
|
||||||
alone in its paragraph).
|
is the link's own takes no ` (image)`, unless the image has a non-empty title the link lacks
|
||||||
|
(`docs/decisions.md` §An image reads as an image only alone in its paragraph).
|
||||||
|
|
||||||
### Tables
|
### Tables
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function externalMedia(alt: string, url: string, link?: LinkDefinition):
|
|||||||
return link === undefined ? media : { ...media, marks: [linkMark(link)] }
|
return link === undefined ? media : { ...media, marks: [linkMark(link)] }
|
||||||
}
|
}
|
||||||
|
|
||||||
// The ` (image)` after a link for an image inside it, as the readers build it (spec/flavour.md, The CommonMark image) and `adfToPortableMarkdown` writes it; `link` builds `image`.
|
// The ` (image)` after a link for an image inside it, as the readers build it (spec/flavour.md, The CommonMark image) and `adfToPortableMarkdown` writes it; `link` builds the linked word `image`.
|
||||||
export function imageAside(link: (text: string) => AdfNode[]): AdfNode[] {
|
export function imageAside(link: (text: string) => AdfNode[]): AdfNode[] {
|
||||||
return [{ text: ' (', type: 'text' }, ...link('image'), { text: ')', type: 'text' }]
|
return [{ text: ' (', type: 'text' }, ...link('image'), { text: ')', type: 'text' }]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ type OwnMarks = { kind: 'ownMarks'; node: AdfNode }
|
|||||||
|
|
||||||
type Inline = AdfNode | OwnMarks | TextBreak
|
type Inline = AdfNode | OwnMarks | TextBreak
|
||||||
|
|
||||||
// `imageLinks` are the (image)s an `!adf:link` slot's link stands after itself.
|
// `imageLinks` are the (image)s an `!adf:link` slot holds, which the link places after itself.
|
||||||
type Scanned = { image?: AdfNode; imageLinks: ImageLink[]; pieces: Resolved[] }
|
type Scanned = { image?: AdfNode; imageLinks: ImageLink[]; pieces: Resolved[] }
|
||||||
|
|
||||||
export type LinkDefinitions = ReadonlyMap<string, LinkDefinition>
|
export type LinkDefinitions = ReadonlyMap<string, LinkDefinition>
|
||||||
@@ -44,7 +44,7 @@ type Bracket = { active: boolean; image: boolean; kind: 'open'; start: number }
|
|||||||
// `link` is the link whose text is this image alone.
|
// `link` is the link whose text is this image alone.
|
||||||
type Image = { alt: string; definition: LinkDefinition; kind: 'image'; link?: LinkDefinition }
|
type Image = { alt: string; definition: LinkDefinition; kind: 'image'; link?: LinkDefinition }
|
||||||
|
|
||||||
// The (image) after a link for one image its text holds: running text reads it, alt text drops it.
|
// The (image) after a link for one image its text holds: running text keeps it, alt text drops it.
|
||||||
type ImageLink = { image: LinkDefinition; kind: 'imageLink'; nodes: Inline[] }
|
type ImageLink = { image: LinkDefinition; kind: 'imageLink'; nodes: Inline[] }
|
||||||
|
|
||||||
type Nodes = { kind: 'nodes'; nodes: Inline[] }
|
type Nodes = { kind: 'nodes'; nodes: Inline[] }
|
||||||
@@ -74,7 +74,7 @@ type Scan = {
|
|||||||
// Pieces below this have been walked for openers to deactivate: an image close folds the link-marked piece into alt text, leaving this the only record that the brackets around it are doomed.
|
// Pieces below this have been walked for openers to deactivate: an image close folds the link-marked piece into alt text, leaving this the only record that the brackets around it are doomed.
|
||||||
deactivatedBefore: number
|
deactivatedBefore: number
|
||||||
definitions: LinkDefinitions
|
definitions: LinkDefinitions
|
||||||
// Whether an `!adf:link` slot holds this content through mark slots alone, where an image reads as its plain alt text and the link stands its (image) after itself.
|
// Whether an `!adf:link` slot holds this content through mark slots alone, where an image reads as its plain alt text and the link places its (image) after itself.
|
||||||
inLink: boolean
|
inLink: boolean
|
||||||
openingSpellableLink: boolean
|
openingSpellableLink: boolean
|
||||||
path: ConvertErrorPath
|
path: ConvertErrorPath
|
||||||
@@ -270,7 +270,7 @@ function assemble(scan: Scan): Result<Scanned> {
|
|||||||
return success(scan.pieces.length === 1 && only?.kind === 'image' ? { ...resolved, image: mediaSingle(only) } : resolved)
|
return success(scan.pieces.length === 1 && only?.kind === 'image' ? { ...resolved, image: mediaSingle(only) } : resolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A content slot is read before the scan holding it knows whether an image description holds it, so each (image) stays a piece of its own.
|
// A slot is read before its holder knows whether it sits in an image description, which drops every (image), so each (image) stays a piece of its own.
|
||||||
function resolvedPieces(pieces: readonly Piece[], nodes: readonly Inline[][]): Resolved[] {
|
function resolvedPieces(pieces: readonly Piece[], nodes: readonly Inline[][]): Resolved[] {
|
||||||
const resolved: Resolved[] = []
|
const resolved: Resolved[] = []
|
||||||
let run: Inline[] = []
|
let run: Inline[] = []
|
||||||
@@ -292,7 +292,7 @@ function contentNodes(pieces: readonly Resolved[]): Inline[] {
|
|||||||
return mergeReadText(pieces.flatMap((piece) => piece.nodes))
|
return mergeReadText(pieces.flatMap((piece) => piece.nodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// spec/flavour.md, The CommonMark image: an image no paragraph holds alone reads as its alt text, linked, and inside a link as the link's text, then (image) linked to it.
|
// spec/flavour.md, The CommonMark image: an image no paragraph holds alone reads as its alt text, linked, and inside a link as the link's text, then (image) linked to the image.
|
||||||
function linkedAlt(piece: Piece, inLink: boolean): Piece[] {
|
function linkedAlt(piece: Piece, inLink: boolean): Piece[] {
|
||||||
if (inLink && piece.kind === 'imageLink') return []
|
if (inLink && piece.kind === 'imageLink') return []
|
||||||
if (piece.kind !== 'image' || (inLink && piece.link === undefined)) return [piece]
|
if (piece.kind !== 'image' || (inLink && piece.link === undefined)) return [piece]
|
||||||
@@ -300,7 +300,7 @@ function linkedAlt(piece: Piece, inLink: boolean): Piece[] {
|
|||||||
return piece.link === undefined ? [alt] : [alt, ...besideLink([imageLink(piece.definition)], piece.link)]
|
return piece.link === undefined ? [alt] : [alt, ...besideLink([imageLink(piece.definition)], piece.link)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// As adfToPortableMarkdown writes it: a link to the image itself needs no (image), unless the image holds a title the link does not.
|
// A link to the image itself needs no (image), as `adfToPortableMarkdown` writes none; one stays to keep an image title the link lacks.
|
||||||
function besideLink(imageLinks: readonly ImageLink[], link: LinkDefinition): ImageLink[] {
|
function besideLink(imageLinks: readonly ImageLink[], link: LinkDefinition): ImageLink[] {
|
||||||
return imageLinks.filter(({ image }) => image.destination !== link.destination || (image.title !== undefined && image.title !== '' && image.title !== link.title))
|
return imageLinks.filter(({ image }) => image.destination !== link.destination || (image.title !== undefined && image.title !== '' && image.title !== link.title))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,12 +64,11 @@ operations (§Cost fixes are measured, never timed), 4× per doubling: about 255
|
|||||||
- `markPairings`, called through `markedNodes`, re-marks every piece each emphasis pairing spans:
|
- `markPairings`, called through `markedNodes`, re-marks every piece each emphasis pairing spans:
|
||||||
`'*'.repeat(n) + '[b a](/v)'.repeat(n) + '*'.repeat(n)`. Pair by range instead, outermost
|
`'*'.repeat(n) + '[b a](/v)'.repeat(n) + '*'.repeat(n)`. Pair by range instead, outermost
|
||||||
pairing first, marking each piece once.
|
pairing first, marking each piece once.
|
||||||
- `lastBracket`, called by `closeBracket`, walks back over every piece a bracket that went literal
|
- `lastBracket`, called by `closeBracket`, walks back over the pieces each literal bracket leaves:
|
||||||
left: `'['.repeat(n) + '[b a](/v)' + '](/w)'.repeat(n)`. Drop such a bracket from the search, as
|
`'['.repeat(n) + '[b a](/v)' + '](/w)'.repeat(n)`. Drop a bracket from the search once it goes
|
||||||
CommonMark's bracket stack does.
|
literal, as CommonMark's bracket stack does.
|
||||||
|
|
||||||
The seats found `closeBracket` and `resolveNodes` hard to read; leave both easier to read. Found by
|
The seats found `closeBracket` and `resolveNodes` hard to read; leave both easier to read.
|
||||||
the stability review of item 122, 2026-10-23.
|
|
||||||
|
|
||||||
### 95. Find the code-span closers an escape forms, the mark runs a line cannot spell and the text `[` that opens link text by reading the line back, in place of `escapeClosedRuns`, `unspellableRuns` and `lastLinkClose` predicting them.
|
### 95. Find the code-span closers an escape forms, the mark runs a line cannot spell and the text `[` that opens link text by reading the line back, in place of `escapeClosedRuns`, `unspellableRuns` and `lastLinkClose` predicting them.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user