import type { Root, Element, ElementContent, Text } from 'hast'
import type { Plugin } from 'unified'
import { visit } from 'unist-util-visit'
/**
* Checks whether a line starts with a colon marker (optionally preceded by
* whitespace) and, if so, strips it and returns the remaining content.
* @param line The line (array of ElementContent) to check.
* @returns Whether the line is a colon-prefixed definition line, and its content with the marker stripped.
*/
const stripLeadingColon = (
line: ElementContent[],
): { isDefinitionLine: boolean; content: ElementContent[] } => {
const [first, ...rest] = line
if (first?.type === 'text') {
const match = first.value.match(/^[ \t]*:[ \t]*/)
if (match) {
const remainder = first.value.slice(match[0].length)
return {
isDefinitionLine: true,
content: remainder ? [{ type: 'text', value: remainder } as Text, ...rest] : rest,
}
}
}
return { isDefinitionLine: false, content: line }
}
/**
* Splits an array of ElementContent nodes into lines based on text nodes containing newlines and
elements.
* @param nodes The array of ElementContent nodes to split into lines.
* @returns An array of lines, where each line is an array of ElementContent nodes.
*/
const splitIntoLines = (nodes: ElementContent[]): ElementContent[][] => {
const lines: ElementContent[][] = [[]]
for (const child of nodes) {
if (child.type === 'text') {
const segments = child.value.split('\n')
segments.forEach((segment, i) => {
if (i > 0) lines.push([])
if (segment.length > 0) {
lines[lines.length - 1].push({ type: 'text', value: segment } as Text)
}
})
} else if (child.type === 'element' && child.tagName === 'br') {
lines.push([])
} else {
lines[lines.length - 1].push(child)
}
}
return lines
}
/**
* A rehype plugin that transforms paragraphs shaped like:
*
* Term
* : Definition A for term.
* : Definition B for term, on a new line.
*
* into a