diff --git a/spec/flavour.md b/spec/flavour.md index bb4791a..5839b2d 100644 --- a/spec/flavour.md +++ b/spec/flavour.md @@ -263,9 +263,9 @@ The moon, at night. a `mediaSingle` with attrs exactly `{"layout":"center"}` holding an `external` `media` — `url` from the destination, `alt` the description's plain-text content when non-empty — a link or image inside it contributing its own text, a node spelling its text in the content slot contributing -that text, and a break of either kind a space. `adfToMarkdown` -emits the image form for exactly that shape — those attrs and no others, no marks on either -node, no caption, and a `media` carrying nothing beyond `alt`, `type` and `url` — and only where +that text, and a break of either kind a space. `adfToMarkdown` emits the image form for exactly +that shape — those attrs and no others, no marks on either node, no caption, and a `media` +carrying nothing beyond `alt`, `type` and `url` — and only where CommonMark spells the pair: a destination or a description the image form cannot hold, an empty `alt` included, takes the directive form instead. An image amid other text, or one carrying a title, is a named error: `mediaInline` carries a media diff --git a/src/markdown/parse/directive-marks.ts b/src/markdown/parse/directive-marks.ts index bb4a953..b1d7ef2 100644 --- a/src/markdown/parse/directive-marks.ts +++ b/src/markdown/parse/directive-marks.ts @@ -16,7 +16,7 @@ export function readDirectiveMark(name: string, attributes: DirectiveAttributes, return success(Object.keys(attrs.value).length === 0 ? { type: name } : { attrs: attrs.value, type: name }) } -export function markSpellingFault(name: string): ConvertFault | undefined { +export function inlineMarkSpellingFault(name: string): ConvertFault | undefined { const spelling = markSpelling(name) if (spelling === undefined) return undefined return { code: 'unsupported-node-shape', message: `${name} is spelled ${markdownForm(spelling) ?? `:${name}[…]`}, never as a block directive` } diff --git a/src/markdown/parse/directive-nodes.ts b/src/markdown/parse/directive-nodes.ts index cffe314..eb51512 100644 --- a/src/markdown/parse/directive-nodes.ts +++ b/src/markdown/parse/directive-nodes.ts @@ -9,7 +9,7 @@ import { blockDirective } from '../../adf/block-directives.ts' import { carryName } from '../opaque-carry.ts' import { failure, faulted, success, type ConvertErrorPath, type Result } from '../../result.ts' import { inlineDirective } from '../../adf/inline-directives.ts' -import { markSpellingFault } from './directive-marks.ts' +import { inlineMarkSpellingFault } from './directive-marks.ts' import { marksAttribute, readMarkValues } from '../block-directive-marks.ts' import { readVocabulary } from './directive-attributes.ts' import { slotLineEndingFault } from '../directive-syntax.ts' @@ -69,15 +69,17 @@ export function readInlineDirectiveNode( // A name the other position spells names that spelling, never the code a later MINOR may fill (AGENTS.md §8). function inlineSpellingFault(name: string): ConvertFault | undefined { - const mark = markSpellingFault(name) + const mark = inlineMarkSpellingFault(name) if (mark !== undefined) return mark if (inlineDirective(name) === undefined && name !== textDirectiveName) return undefined - return { code: 'unsupported-node-shape', message: `${name} takes the inline form, :${name}` } + return { code: 'unsupported-node-shape', message: `${name} takes the inline form, :${name}{…}` } } function blockSpellingFault(name: string): ConvertFault | undefined { - if (blockDirective(name) === undefined) return undefined - return { code: 'unsupported-node-shape', message: `${name} takes the block form, ::${name}` } + const directive = blockDirective(name) + if (directive === undefined) return undefined + const form = directive.contentModel === 'none' ? `::${name}` : `:::${name}` + return { code: 'unsupported-node-shape', message: `${name} takes the block form, ${form}` } } // spec/flavour.md, Inline nodes: the slot is plain text, its adjacent nodes already merged. diff --git a/src/markdown/parse/markdown-to-adf.test.ts b/src/markdown/parse/markdown-to-adf.test.ts index d510ec6..d7883d7 100644 --- a/src/markdown/parse/markdown-to-adf.test.ts +++ b/src/markdown/parse/markdown-to-adf.test.ts @@ -254,9 +254,10 @@ test('names the directive name no node reads back to', () => { test('names the position a directive name the other one spells belongs to', () => { assert.equal(content(markdownToAdf(':::em\na\n:::\n')), 'unsupported-node-shape: em is spelled _x_, never as a block directive') assert.equal(content(markdownToAdf('::underline\n')), 'unsupported-node-shape: underline is spelled :underline[…], never as a block directive') - assert.equal(content(markdownToAdf('::text {text=" "}\n')), 'unsupported-node-shape: text takes the inline form, :text') - assert.equal(content(markdownToAdf('::date {timestamp=1}\n')), 'unsupported-node-shape: date takes the inline form, :date') - assert.equal(content(markdownToAdf(':paragraph[a]\n')), 'unsupported-node-shape: paragraph takes the block form, ::paragraph') + assert.equal(content(markdownToAdf('::text {text=" "}\n')), 'unsupported-node-shape: text takes the inline form, :text{…}') + assert.equal(content(markdownToAdf('::date {timestamp=1}\n')), 'unsupported-node-shape: date takes the inline form, :date{…}') + assert.equal(content(markdownToAdf(':paragraph[a]\n')), 'unsupported-node-shape: paragraph takes the block form, :::paragraph') + assert.equal(content(markdownToAdf(':rule[a]\n')), 'unsupported-node-shape: rule takes the block form, ::rule') assert.equal(code(markdownToAdf(':::widget\na\n:::\n')), 'unknown-directive-name') assert.equal(code(markdownToAdf(':widget[a]\n')), 'unknown-directive-name') }) diff --git a/src/markdown/text-directive.ts b/src/markdown/text-directive.ts index 8cf4ddf..f3d7eec 100644 --- a/src/markdown/text-directive.ts +++ b/src/markdown/text-directive.ts @@ -2,17 +2,16 @@ import type { ConvertFault } from '../result.ts' import type { DirectiveSpan, Read } from './directive-syntax.ts' import { spellAttributes, spellLeafDirective, spellStringAttribute } from './directive-syntax.ts' -export const textDirectiveName = 'text' - +const name = 'text' const whitespaceRun = /^(?:[ \t]+|\n+)$/ +export const textDirectiveName = name + export function spellTextDirective(text: string): string { - const name = textDirectiveName return spellLeafDirective(name, spellAttributes([[name, spellStringAttribute(text)]])) } export function readTextDirective(span: DirectiveSpan): Read | undefined { - const name = textDirectiveName if (span.name !== name) return undefined if (span.content !== undefined) return { fault: unsupported(`${name} takes no content`) } const spelled = span.attributes.get(name)