155 lines
4.7 KiB
JavaScript
155 lines
4.7 KiB
JavaScript
import { visit } from 'unist-util-visit'
|
|
import { remove } from 'unist-util-remove'
|
|
|
|
const dflts = {
|
|
commentTag: 'span',
|
|
commentClass: 'hljs-comment',
|
|
openingCommentHighlight: 'highlight-start',
|
|
closingCommentHighlight: 'highlight-end',
|
|
openingCommentStrikeout: 'strikeout-start',
|
|
closingCommentStrikeout: 'strikeout-end',
|
|
highlightTag: 'section',
|
|
highlightClass: 'highlight-lines',
|
|
strikeoutClass: 'strikeout-lines',
|
|
swallow: true,
|
|
}
|
|
|
|
export default (userOptions = {}) => {
|
|
// Merge defaults with user-supplied options
|
|
const options = {
|
|
...dflts,
|
|
...userOptions,
|
|
}
|
|
|
|
const splitParams = (node) => {
|
|
if (node.children.length === 1 && node.children[0].type === 'text') {
|
|
const content = node.children[0].value.split('\n')
|
|
node.children = content.map((value) =>
|
|
value.includes('//')
|
|
? {
|
|
type: 'element',
|
|
tagName: 'span',
|
|
properties: {
|
|
className: options.commentClass,
|
|
},
|
|
children: [{ type: 'text', value }],
|
|
}
|
|
: { type: 'text', value: value + '\n', isParamLine: true }
|
|
)
|
|
}
|
|
}
|
|
|
|
// Keep track of whether we've opened a highlight block
|
|
let isOpen = 0
|
|
let children = {}
|
|
|
|
// Detect opening or closing comment
|
|
const isOpeningOrClosingComment = (node) => {
|
|
if (
|
|
node &&
|
|
node.tagName === options.commentTag &&
|
|
node.properties?.className.includes(options.commentClass)
|
|
) {
|
|
if (node.children[0].value.includes(options.openingCommentHighlight))
|
|
return ['opening', 'highlight']
|
|
if (node.children[0].value.includes(options.closingCommentHighlight))
|
|
return ['closing', 'highlight']
|
|
if (node.children[0].value.includes(options.openingCommentStrikeout))
|
|
return ['opening', 'strikeout']
|
|
if (node.children[0].value.includes(options.closingCommentStrikeout))
|
|
return ['closing', 'strikeout']
|
|
}
|
|
|
|
return [false, 'highlight']
|
|
}
|
|
|
|
// Visitor method
|
|
const visitor = (node, i, parent) => {
|
|
const [type, variant] = isOpeningOrClosingComment(node)
|
|
if (type) {
|
|
if (type === 'opening') {
|
|
isOpen = 1
|
|
|
|
// Deal with extra linebreaks in the element before the start
|
|
const prev = parent.children[i - 1]
|
|
if (prev?.type === 'text' && prev.value.includes('\n'))
|
|
prev.value = prev.value.replace(/\n/g, '') + '\n'
|
|
|
|
if (options.swallow) node.__remove_dupes = true
|
|
} else if (type === 'closing') {
|
|
// Deal with extra linebreaks in the element before the end
|
|
const prev = children[i - 1]
|
|
if (prev.type === 'text' && prev.value.includes('\n') && !node.isParamLine)
|
|
prev.value = prev.value.trimEnd()
|
|
|
|
isOpen = 0
|
|
const curNode = { ...node }
|
|
parent.children[i] = {
|
|
type: 'element',
|
|
tagName: options.highlightTag,
|
|
properties: {
|
|
className: Array.isArray(options[`${variant}Class`])
|
|
? options[`${variant}Class`]
|
|
: [options[`${variant}Class`]],
|
|
},
|
|
children: [
|
|
{
|
|
type: 'element',
|
|
tagName: 'div',
|
|
properties: {
|
|
className: ['code-section-inner'],
|
|
},
|
|
children: [...Object.values(children)],
|
|
},
|
|
],
|
|
}
|
|
|
|
if (!options.swallow) parent[i].children.push(curNode)
|
|
children = {}
|
|
}
|
|
} else if (isOpen) {
|
|
if (
|
|
parent.tagName === 'code' ||
|
|
(parent.tagName === 'span' &&
|
|
parent.properties?.className &&
|
|
parent.properties.className.includes('hljs-params'))
|
|
) {
|
|
if (node.type === 'text' && node.value === '\n') {
|
|
// Linebreak
|
|
node.type = 'element'
|
|
node.tagName = 'span'
|
|
node.children = [
|
|
{
|
|
type: 'text',
|
|
value: '\n',
|
|
},
|
|
{
|
|
type: 'element',
|
|
tagName: 'span',
|
|
properties: {
|
|
className: ['code-line-break'],
|
|
},
|
|
},
|
|
]
|
|
}
|
|
// On the first highlighted line, trim the previous linebreak (not for param lines)
|
|
if (isOpen === 1 && node.type === 'text' && !node.isParamLine)
|
|
node.value = node.value.replace('\n', '')
|
|
children[i] = { ...node }
|
|
isOpen++
|
|
node.__remove_dupes = true
|
|
}
|
|
}
|
|
}
|
|
|
|
const isParamsNode = (node) =>
|
|
node?.properties?.className && node.properties.className.includes('hljs-params')
|
|
|
|
const transform = (tree) => {
|
|
visit(tree, (node) => isParamsNode(node), splitParams)
|
|
visit(tree, () => true, visitor)
|
|
remove(tree, (node) => node.__remove_dupes === true)
|
|
}
|
|
|
|
return transform
|
|
}
|