|
|
|
function findMatchingClosingTag(src, openTag, closeTag) { |
|
let depth = 1; |
|
let index = openTag.length; |
|
while (depth > 0 && index < src.length) { |
|
if (src.startsWith(openTag, index)) { |
|
depth++; |
|
} else if (src.startsWith(closeTag, index)) { |
|
depth--; |
|
} |
|
if (depth > 0) { |
|
index++; |
|
} |
|
} |
|
return depth === 0 ? index + closeTag.length : -1; |
|
} |
|
|
|
function detailsTokenizer(src) { |
|
const detailsRegex = /^<details>\n/; |
|
const summaryRegex = /^<summary>(.*?)<\/summary>\n/; |
|
|
|
if (detailsRegex.test(src)) { |
|
const endIndex = findMatchingClosingTag(src, '<details>', '</details>'); |
|
if (endIndex === -1) return; |
|
|
|
const fullMatch = src.slice(0, endIndex); |
|
let content = fullMatch.slice(10, -10).trim(); |
|
|
|
let summary = ''; |
|
const summaryMatch = summaryRegex.exec(content); |
|
if (summaryMatch) { |
|
summary = summaryMatch[1].trim(); |
|
content = content.slice(summaryMatch[0].length).trim(); |
|
} |
|
|
|
return { |
|
type: 'details', |
|
raw: fullMatch, |
|
summary: summary, |
|
text: content |
|
}; |
|
} |
|
} |
|
|
|
function detailsStart(src) { |
|
return src.match(/^<details>/) ? 0 : -1; |
|
} |
|
|
|
function detailsRenderer(token) { |
|
return `<details> |
|
${token.summary ? `<summary>${token.summary}</summary>` : ''} |
|
${token.text} |
|
</details>`; |
|
} |
|
|
|
function detailsExtension() { |
|
return { |
|
name: 'details', |
|
level: 'block', |
|
start: detailsStart, |
|
tokenizer: detailsTokenizer, |
|
renderer: detailsRenderer |
|
}; |
|
} |
|
|
|
export default function (options = {}) { |
|
return { |
|
extensions: [detailsExtension(options)] |
|
}; |
|
} |
|
|