3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-12 11:48:29 +01:00

Answer architecture round 1: 1 🔴, 2 🟡; read a node's taken mark types once, let the highlight ask takesMark, correct the README's code-span bullet

This commit is contained in:
2026-10-14 20:42:33 +02:00
committed by Lillem4n
parent 31f01f2fc0
commit c9f9276df9
7 changed files with 20 additions and 14 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ In `losslessMarkdownToAdf`, a mark spelling skips a node Atlassian's schema with
| Input | `0.2.0` | `0.3.0` |
| --- | --- | --- |
| a `date`, `emoji`, `inlineCard`, `mention`, `status` or `mediaInline` inside a mark's spelling (`**!adf:mention[@A]{id=x}**`) | the node holds the mark | the node holds only marks its type takes; text beside it keeps the mark |
| a link or directive mark around only nodes that cannot take it, hard breaks under a link aside (`[!adf:status[Done]{color=green}](/u)`) | the nodes hold the mark | `unsupported-node-shape`; write the mark around text beside them |
| a link or directive mark around only nodes that cannot take it, a code span included, hard breaks under a link aside (`[!adf:status[Done]{color=green}](/u)`) | the nodes hold the mark | `unsupported-node-shape`; write the mark around text beside them |
| a code span inside any mark's spelling but a link's (``**`npm ci`**``), in every reader | the code text holds the mark | the code text holds only `code` and the link |
`0.2.0` wrote these spellings for a node holding such a mark; `0.3.0` writes the node as
+2 -2
View File
@@ -536,8 +536,8 @@ Serves [Goals](#goals) 1, 4 and 5.
alt text linked to its URL: `See ![the moon](moon.png).` reads as
`See [the moon](moon.png).`. To keep an image an image, give it a paragraph of its own: a
blank line between it and any other line.
- A code span inside emphasis, strikethrough or a directive mark reads as code alone, since ADF
gives code text no mark but a link: ``**`npm ci`**`` reads as `` `npm ci` ``.
- A code span inside emphasis reads as code alone, since ADF gives code text no 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. A link alone in its paragraph
whose whole text is one image reads as that image, with the link marking it.
- An image's title becomes its caption when the image stays an image, and its link's title when
+2 -2
View File
@@ -277,7 +277,7 @@
"divergence": "a 1/5 em 1/3",
"example": 516,
"kind": "mark-model",
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element, and skips the code text, which takes no em."
},
{
"check": "count",
@@ -291,7 +291,7 @@
"divergence": "a 1/5 em 1/3",
"example": 530,
"kind": "mark-model",
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element, and skips the code text, which takes no em."
},
{
"check": "count",
+2 -1
View File
@@ -259,7 +259,8 @@ Their CommonMark suite exceptions are `node-model` (§The CommonMark suite check
## A mark skips a node that cannot hold it, and a mark wrapping only such nodes is refused, but for a link around hard breaks alone
2026-10-14, two reader panels of the viewer/editor app, the LLM pipeline and a support agent, 3 of
3 each. Goals 1, 4 and 6. Valid while Atlassian's schema withholds marks from inline nodes.
3 each. Goals 1, 4 and 6. Valid while Atlassian's schema withholds marks from inline nodes. Code
text follows by this entry's rule, unpanelled, 2026-10-14.
A mark spelling skips a node that takes none of its type, code text included, which takes only
`link` and `annotation`. So `**Ping !adf:mention[@A]{id=x} now**` reads as bold text around a plain
+8 -4
View File
@@ -37,8 +37,12 @@ export function inlineNodeModel(type: string): InlineNodeModel | undefined {
return Object.hasOwn(inlineNodes, type) ? inlineNodes[type] : undefined
}
// The table withholds nothing from a type it does not list, such as text; Atlassian's schema withholds from code text all but `codeTextMarkTypes`.
export function takesMark(node: AdfNode, markType: string): boolean {
if (node.type === 'text' && nodeMarks(node).some((mark) => mark.type === 'code')) return codeTextMarkTypes.includes(markType)
return inlineNodeModel(node.type)?.markTypes.includes(markType) ?? true
// Undefined where the schema withholds no mark: the table lists no such type, text among them, but code text takes only `codeTextMarkTypes`.
export function takenMarkTypes(node: AdfNode): readonly string[] | undefined {
if (node.type === 'text') return nodeMarks(node).some((mark) => mark.type === 'code') ? codeTextMarkTypes : undefined
return inlineNodeModel(node.type)?.markTypes
}
export function takesMark(node: AdfNode, markType: string): boolean {
return takenMarkTypes(node)?.includes(markType) ?? true
}
+3 -2
View File
@@ -11,7 +11,7 @@ import { failure, success, type ConvertErrorPath, type Result } from '../../resu
import { flavourClaims, highlightDelimiter } from '../portable/conventions.ts'
import { holdsNullCharacter, trimTrailingSpace } from '../commonmark/grammar.ts'
import { identicalMark, isSpellableText, nodeAttrs, nodeContent, nodeMarks } from '../../adf/document.ts'
import { inlineNodeModel, takesMark } from '../../adf/inline-nodes.ts'
import { inlineNodeModel, takenMarkTypes } from '../../adf/inline-nodes.ts'
import { joinsWhenRead, textBreakSpelling } from '../adjacent-text.ts'
import { largestNesting } from '../../nesting.ts'
import { longestBacktickRun } from '../commonmark/backtick-runs.ts'
@@ -223,7 +223,8 @@ function nodePath(context: InlineContext, index: number): ConvertErrorPath {
// Decides the text-node, withheld-mark and slot-fault carries up front, so a line holding many of them is emitted once.
function carries(node: AdfNode, carried: ReadonlySet<number>, index: number): boolean {
if (carried.has(index) || nodeMarks(node).some((mark) => !takesMark(node, mark.type))) return true
const taken = takenMarkTypes(node)
if (carried.has(index) || (taken !== undefined && nodeMarks(node).some((mark) => !taken.includes(mark.type)))) return true
if (node.type === 'text') return !isSpellableText(node) || typeof node.text !== 'string' || node.text === ''
const model = inlineNodeModel(node.type)
if (model === undefined) return true
+2 -2
View File
@@ -569,7 +569,7 @@ function pairedHighlights(pieces: readonly Piece[], nodes: readonly Inline[][]):
return paired
}
// Atlassian's schema refuses a highlight on code, a node holds one highlight, and a rebuilt node would lose its attributes.
// A node holds one highlight, and a rebuilt node would lose its attributes.
function markHighlights(pieces: readonly Piece[], nodes: Inline[][], paired: readonly { closer: number; opener: number }[]): void {
for (const [index, piece] of pieces.entries()) {
if (piece.kind === 'highlight') nodes[index] = adfNodes(nodes[index] ?? []).map((holder) => ({ ...holder, text: highlightDelimiter }))
@@ -584,7 +584,7 @@ function markHighlights(pieces: readonly Piece[], nodes: Inline[][], paired: rea
function highlighted(node: Inline): Inline {
if (!isNode(node)) return node
const marks = nodeMarks(node)
if (node.type !== 'text' || Object.keys(nodeAttrs(node)).length > 0 || marks.some((mark) => mark.type === 'code' || mark.type === 'backgroundColor')) return node
if (node.type !== 'text' || Object.keys(nodeAttrs(node)).length > 0 || !marksItem(node, editorHighlight.type) || marks.some((mark) => mark.type === editorHighlight.type)) return node
return { ...node, marks: [editorHighlight, ...marks] }
}