Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function directiveToMarkdown(options) {
*/
function handleDirective(node, _, state, info) {
const tracker = state.createTracker(info)
const sequence = fence(node)
let sequence = fence(node)
const exit = state.enter(node.type)
let value = tracker.move(sequence + (node.name || ''))
const attributesValue = attributes(node, state)
Expand Down Expand Up @@ -200,7 +200,19 @@ export function directiveToMarkdown(options) {

if (shallow && shallow.children && shallow.children.length > 0) {
value += tracker.move('\n')
value += tracker.move(state.containerFlow(shallow, tracker.current()))
const content = state.containerFlow(shallow, tracker.current())
value += tracker.move(content)

// A line in the content of colons with only spaces and tabs around them
// (such as `:::` in code) closes the container if it has as many colons
// as the fence, so make the fence longer.
const size = longestColonLine(content) + 1

if (size > sequence.length) {
const extra = ':'.repeat(size - sequence.length)
value = extra + value
sequence += extra
}
}

value += tracker.move('\n' + sequence)
Expand Down Expand Up @@ -560,6 +572,29 @@ function inlineDirectiveLabel(node) {
)
}

/**
* Get the size of the longest sequence of colons on a line with only spaces
* and tabs around it.
*
* @param {string} value
* Serialized markdown.
* @returns {number}
* Size of the longest sequence, `0` if there are none.
*/
function longestColonLine(value) {
const expression = /^[\t ]*:+[\t ]*$/gm
let size = 0
/** @type {RegExpExecArray | null} */
let match

while ((match = expression.exec(value))) {
const count = ccount(match[0], ':')
if (count > size) size = count
}

return size
}

/**
* Determine the fence size for a directive node.
*
Expand Down
90 changes: 90 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,96 @@ test('directiveToMarkdown()', async function (t) {
}
)

await t.test(
'should serialize containers w/ more colons than a line of colons in code',
async function () {
assert.equal(
toMarkdown(
{
type: 'containerDirective',
name: 'a',
children: [{type: 'code', value: 'b\n:::\nc'}]
},
{extensions: [directiveToMarkdown()]}
),
'::::a\n```\nb\n:::\nc\n```\n::::\n'
)
}
)

await t.test(
'should serialize containers w/ more colons than a line of colons, spaces, and tabs',
async function () {
assert.equal(
toMarkdown(
{
type: 'containerDirective',
name: 'a',
children: [{type: 'code', value: ' \t::::: '}]
},
{extensions: [directiveToMarkdown()]}
),
'::::::a\n```\n \t::::: \n```\n::::::\n'
)
}
)

await t.test(
'should not serialize containers w/ more colons for a line w/ colons and other characters',
async function () {
assert.equal(
toMarkdown(
{
type: 'containerDirective',
name: 'a',
children: [{type: 'code', value: ':::b\n::: c'}]
},
{extensions: [directiveToMarkdown()]}
),
':::a\n```\n:::b\n::: c\n```\n:::\n'
)
}
)

await t.test(
'should serialize outer containers w/ more colons than inner containers that have code',
async function () {
assert.equal(
toMarkdown(
{
type: 'containerDirective',
name: 'a',
children: [
{
type: 'containerDirective',
name: 'b',
children: [{type: 'code', value: ':::'}]
}
]
},
{extensions: [directiveToMarkdown()]}
),
':::::a\n::::b\n```\n:::\n```\n::::\n:::::\n'
)
}
)

await t.test(
'should roundtrip a container w/ a directive in code',
async function () {
const value = '::::a\n```markdown\n:::b\nc\n:::\n```\n::::\n'
const tree = fromMarkdown(value, {
extensions: [directive()],
mdastExtensions: [directiveFromMarkdown()]
})

assert.equal(
toMarkdown(tree, {extensions: [directiveToMarkdown()]}),
value
)
}
)

await t.test(
'should escape a `:` in phrasing when followed by an alpha',
async function () {
Expand Down
Loading