mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-13 04:08:29 +01:00
Answer architecture round 1: 2 🔴, 5 🟡; nesting counts the JSON fallback alone, one mark attrs read and spell, one mark table, a malformed mark is unsupported-node-shape, the old colwidth names the comma list, README and notes name the attrs fallback
This commit is contained in:
@@ -24,7 +24,7 @@ export function readVocabulary(
|
||||
if (kind === undefined) return failure('unsupported-node-shape', `${type} holds no ${key} attribute: this one spells it`, path)
|
||||
const read = attributeValue(spelled.decoded, kind)
|
||||
if (read.refusal === 'nesting') return failure('unsupported-nesting-depth', attributeNestingMessage(key, type), path)
|
||||
if (read.value === undefined) return failure('unsupported-node-shape', `the ${key} attribute of ${type} is no ${kind}`, path)
|
||||
if (read.value === undefined) return failure('unsupported-node-shape', `the ${key} attribute of ${type} is no ${kind === 'numbers' ? 'comma list of numbers' : kind}`, path)
|
||||
const spelling = spellAttributeValue(read.value)
|
||||
if (spelling !== spelled.spelling) return failure('unsupported-node-shape', `${type} spells its ${key} attribute as ${key}=${spelling}`, path)
|
||||
attrs[key] = read.value.value
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { AdfMark } from '../../adf/document.ts'
|
||||
import type { AttributeVocabulary } from '../../adf/attribute-vocabulary.ts'
|
||||
import type { ConvertFault } from '../../result.ts'
|
||||
import type { DirectiveAttributes } from '../directive-syntax.ts'
|
||||
import type { MarkSpelling } from '../mark-spellings.ts'
|
||||
@@ -13,12 +14,16 @@ export function readDirectiveMark(name: string, attributes: DirectiveAttributes,
|
||||
if (spelling === undefined) return undefined
|
||||
const markdown = markdownForm(spelling)
|
||||
if (markdown !== undefined) return failure('unsupported-node-shape', `${name} is spelled ${markdown}, never as a directive`, path)
|
||||
const empty = readEmptyKeys(name, attributes, ['attrs'], false)
|
||||
return readMarkAttributes(name, attributes, spelling.attributes, path)
|
||||
}
|
||||
|
||||
export function readMarkAttributes(type: string, attributes: DirectiveAttributes, vocabulary: AttributeVocabulary, path: ConvertErrorPath): Result<AdfMark> {
|
||||
const empty = readEmptyKeys(type, attributes, ['attrs'], false)
|
||||
if (empty.fault !== undefined) return faulted(empty.fault, path)
|
||||
const attrs = readVocabulary(name, empty.value.rest, spelling.attributes, undefined, path)
|
||||
const attrs = readVocabulary(type, empty.value.rest, vocabulary, undefined, path)
|
||||
if (!attrs.ok) return attrs
|
||||
if (empty.value.empty.has('attrs')) return success({ attrs: {}, type: name })
|
||||
return success(Object.keys(attrs.value).length === 0 ? { type: name } : { attrs: attrs.value, type: name })
|
||||
if (empty.value.empty.has('attrs')) return success({ attrs: {}, type })
|
||||
return success(Object.keys(attrs.value).length === 0 ? { type } : { attrs: attrs.value, type })
|
||||
}
|
||||
|
||||
export function inlineMarkSpellingFault(name: string): ConvertFault | undefined {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { AdfAttributes, AdfMark, AdfNode, EmptyKey } from '../../adf/document.ts'
|
||||
import type { AttributeVocabulary } from '../../adf/attribute-vocabulary.ts'
|
||||
import type { BlockNodeModel } from '../../adf/block-nodes.ts'
|
||||
import type { ConvertFault } from '../../result.ts'
|
||||
import type { DirectiveAttributes, DirectiveValue } from '../directive-syntax.ts'
|
||||
@@ -10,9 +9,9 @@ import { blockArgument, blockDirectiveForm, marksAttribute, readMarkValues, spel
|
||||
import { blockNodeModel } from '../../adf/block-nodes.ts'
|
||||
import { carryFencePrefix, carryName } from '../opaque-carry.ts'
|
||||
import { failure, faulted, success, type ConvertErrorPath, type Result } from '../../result.ts'
|
||||
import { inlineMarkSpellingFault } from './directive-marks.ts'
|
||||
import { inlineMarkSpellingFault, readMarkAttributes } from './directive-marks.ts'
|
||||
import { inlineNodeModel } from '../../adf/inline-nodes.ts'
|
||||
import { markVocabulary } from '../../adf/mark-attributes.ts'
|
||||
import { markVocabularies } from '../../adf/mark-attributes.ts'
|
||||
import { readEmptyKeys, spellsEmpty } from '../empty-keys.ts'
|
||||
import { readVocabulary } from './directive-attributes.ts'
|
||||
import { slotFault } from '../directive-syntax.ts'
|
||||
@@ -119,33 +118,30 @@ function readNamedMarks(type: string, text: string, path: ConvertErrorPath): Res
|
||||
const marks: AdfMark[] = []
|
||||
let cursor = 0
|
||||
for (;;) {
|
||||
const name = readDirectiveName(text, cursor)
|
||||
if (name === undefined) return failure('unsupported-node-shape', unnamedMarks(type), path)
|
||||
const vocabulary = markVocabulary(name)
|
||||
if (vocabulary === undefined) return failure('unsupported-node-shape', `the ${marksAttribute} attribute of ${type} names ${name}, a mark it spells only in its JSON array`, path)
|
||||
cursor += name.length
|
||||
const attributes = text.charAt(cursor) === '{' ? readAttributes(text, cursor) : { value: { attributes: new Map<string, DirectiveValue>(), end: cursor } }
|
||||
if (attributes.fault !== undefined) return faulted(attributes.fault, path)
|
||||
const mark = readNamedMark(name, attributes.value.attributes, vocabulary, path)
|
||||
const mark = readNamedMark(type, text, cursor, path)
|
||||
if (!mark.ok) return mark
|
||||
marks.push(mark.value)
|
||||
cursor = attributes.value.end
|
||||
marks.push(mark.value.mark)
|
||||
cursor = mark.value.end
|
||||
if (cursor === text.length) return success(marks)
|
||||
if (text.charAt(cursor) !== ' ') return failure('unsupported-node-shape', unnamedMarks(type), path)
|
||||
cursor += 1
|
||||
}
|
||||
}
|
||||
|
||||
function unnamedMarks(type: string): string {
|
||||
return `the ${marksAttribute} attribute of ${type} names each mark with its {attrs}, one space apart: this one does not`
|
||||
function readNamedMark(type: string, text: string, index: number, path: ConvertErrorPath): Result<{ end: number; mark: AdfMark }> {
|
||||
const name = readDirectiveName(text, index)
|
||||
if (name === undefined) return failure('unsupported-node-shape', unnamedMarks(type), path)
|
||||
const vocabulary = markVocabularies.get(name)
|
||||
if (vocabulary === undefined) return failure('unsupported-node-shape', `the ${marksAttribute} attribute of ${type} names ${name}, a mark it spells only in its JSON array`, path)
|
||||
const start = index + name.length
|
||||
const attributes = text.charAt(start) === '{' ? readAttributes(text, start) : { value: { attributes: new Map<string, DirectiveValue>(), end: start } }
|
||||
if (attributes.fault !== undefined) return failure('unsupported-node-shape', unnamedMarks(type), path)
|
||||
const mark = readMarkAttributes(name, attributes.value.attributes, vocabulary, path)
|
||||
return mark.ok ? success({ end: attributes.value.end, mark: mark.value }) : mark
|
||||
}
|
||||
|
||||
function readNamedMark(type: string, attributes: DirectiveAttributes, vocabulary: AttributeVocabulary, path: ConvertErrorPath): Result<AdfMark> {
|
||||
const empty = readEmptyKeys(type, attributes, ['attrs'], false)
|
||||
if (empty.fault !== undefined) return faulted(empty.fault, path)
|
||||
const attrs = readVocabulary(type, empty.value.rest, vocabulary, undefined, path)
|
||||
if (!attrs.ok) return attrs
|
||||
return success(Object.keys(attrs.value).length > 0 || empty.value.empty.has('attrs') ? { attrs: attrs.value, type } : { type })
|
||||
function unnamedMarks(type: string): string {
|
||||
return `the ${marksAttribute} attribute of ${type} names each mark with its {attrs}, one space apart: this one does not`
|
||||
}
|
||||
|
||||
function namedNode(type: string, attrs: AdfAttributes, marks: readonly AdfMark[] | undefined, empty: ReadonlySet<EmptyKey>): AdfNode {
|
||||
|
||||
@@ -556,6 +556,10 @@ test('names the marks key no marks read back from', () => {
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks="{}"}\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks="em strong"}\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks="em "}\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks="breakout{width=1 mode=wide}"}\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks="breakout{mode}"}\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks="breakout{mode=wide"}\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks="em{attrs=empty}strong"}\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks="em{attrs=x}"}\n')), 'unsupported-node-shape: the reserved key attrs takes only the value empty: this one spells attrs=x')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks=x}\n')), 'unsupported-node-shape: the marks attribute of rule names x, a mark it spells only in its JSON array')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:rule {marks="breakout{width=wide}"}\n')), 'unsupported-node-shape: the width attribute of breakout is no number')
|
||||
@@ -577,9 +581,10 @@ test('names the attribute a node holds no reading for', () => {
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:media {width=wide}\n')), 'unsupported-node-shape: the width attribute of media is no number')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:table {isNumberColumnEnabled=yes}\n!adf:/table\n')), 'unsupported-node-shape: the isNumberColumnEnabled attribute of table is no boolean')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:media {width=true}\n')), 'unsupported-node-shape: the width attribute of media is no number')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth="340,"}\n!adf:/tableCell\n')), 'unsupported-node-shape: the colwidth attribute of tableCell is no numbers')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth="[340,420]"}\n!adf:/tableCell\n')), 'unsupported-node-shape: the colwidth attribute of tableCell is no numbers')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth=""}\n!adf:/tableCell\n')), 'unsupported-node-shape: the colwidth attribute of tableCell is no numbers')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth="340,"}\n!adf:/tableCell\n')), 'unsupported-node-shape: the colwidth attribute of tableCell is no comma list of numbers')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth="[]"}\n!adf:/tableCell\n')), 'unsupported-node-shape: the colwidth attribute of tableCell is no comma list of numbers')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth="[340,\\"420\\"]"}\n!adf:/tableCell\n')), 'unsupported-node-shape: the colwidth attribute of tableCell is no comma list of numbers')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth=""}\n!adf:/tableCell\n')), 'unsupported-node-shape: the colwidth attribute of tableCell is no comma list of numbers')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:panel info {panelType=note}\nx\n!adf:/panel\n')), 'unsupported-node-shape: panel spells its panelType attribute as the directive argument, never in {attrs}')
|
||||
assert.equal(content(losslessMarkdownToAdf('Part !adf:mention{id=b1c2 text=A}.\n')), 'unsupported-node-shape: mention spells its text attribute in the content slot, never in {attrs}')
|
||||
})
|
||||
@@ -600,6 +605,7 @@ test('names the attribute value spelled outside the canonical form', () => {
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:media {width="20.0"}\n')), 'unsupported-node-shape: media spells its width attribute as width=20')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth="340, 420"}\n!adf:/tableCell\n')), 'unsupported-node-shape: tableCell spells its colwidth attribute as colwidth="340,420"')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth="340"}\n!adf:/tableCell\n')), 'unsupported-node-shape: tableCell spells its colwidth attribute as colwidth=340')
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:tableCell {colwidth="[340,420]"}\n!adf:/tableCell\n')), 'unsupported-node-shape: tableCell spells its colwidth attribute as colwidth="340,420"')
|
||||
})
|
||||
|
||||
test('names the argument and the body a node takes no reading for', () => {
|
||||
|
||||
Reference in New Issue
Block a user