diff --git a/src/markdown/emit/adf-to-markdown.test.ts b/src/markdown/emit/adf-to-markdown.test.ts index df91465..f356af2 100644 --- a/src/markdown/emit/adf-to-markdown.test.ts +++ b/src/markdown/emit/adf-to-markdown.test.ts @@ -342,7 +342,7 @@ test('escapes only text that would otherwise open a construct', () => { assert.equal(emitted('x"_y"'), 'x"\\_y"\n') }) -test('escapes every text bracket in a line where one opens link text, and every text backtick where one opens a code span', () => { +test('escapes every text bracket in a line where one opens link text, none where none does, and every text backtick where one opens a code span', () => { const emitted = (text: string): string => markdown(adfToLosslessMarkdown(document(paragraph({ text, type: 'text' })))) assert.equal(emitted('[a](b) [c]'), '\\[a](b) \\[c]\n') assert.equal(emitted('see [1] and [docs](url) [2]'), 'see \\[1] and \\[docs](url) \\[2]\n') diff --git a/src/markdown/emit/line-escaping.ts b/src/markdown/emit/line-escaping.ts index 30558cc..57b1870 100644 --- a/src/markdown/emit/line-escaping.ts +++ b/src/markdown/emit/line-escaping.ts @@ -278,14 +278,18 @@ function unspellableRuns(placed: readonly PlacedSegment[], reading: InlineReadin if (segment.highlight === 'open') highlight = { nodes: segment.nodes, start } if (segment.emphasis === 'close') { const opener = openers.pop() - const read = opener === undefined ? undefined : reading.emphasis.get(opener.start) - if (opener !== undefined && (read?.closer !== start || read.width !== segment.text.length) && opener.start > (innermost?.start ?? -1)) innermost = opener + if (opener !== undefined && !readAsWritten(reading, opener.start, start, segment.text.length) && opener.start > (innermost?.start ?? -1)) innermost = opener } if (segment.highlight === 'close' && highlight !== undefined && reading.highlights.get(highlight.start) !== start) unreadHighlights.push(highlight.nodes) } return innermost === undefined ? unreadHighlights : [innermost.nodes] } +function readAsWritten(reading: InlineReading, opener: number, closer: number, width: number): boolean { + const read = reading.emphasis.get(opener) + return read?.closer === closer && read.width === width +} + function isEscapable(escaping: InlineEscaping | undefined): boolean { return escaping === 'backslash' || escaping === 'bracketed' } diff --git a/src/markdown/inline-content.ts b/src/markdown/inline-content.ts index 7e031c8..f6199a1 100644 --- a/src/markdown/inline-content.ts +++ b/src/markdown/inline-content.ts @@ -113,8 +113,8 @@ type SharedScan = Pick const ownMarksInMark = 'move the opaque carry, or the node spelling marks=empty, out of the mark spelling: it holds its own marks' const editorHighlight: AdfMark = { attrs: { color: '#f8e6a0' }, type: 'backgroundColor' } const hreflessLink = 'the link mark spells its href: this one spells none' -const noDefinitions: LinkDefinitions = new Map() const linkInLink = 'no link wraps a link: the [content] this one marks already holds one' +const noDefinitions: LinkDefinitions = new Map() const unlinkable = 'the text of this link holds only nodes that take no link: write the link around text beside them' const spellableLink = 'link takes the directive form only where CommonMark cannot spell it: this one it can, as [text](url "title") or ' const textBreak: TextBreak = { kind: 'textBreak' }