UNPKG

@rtdui/code-highlight

Version:

React code highlight component based on refractor

198 lines (196 loc) 6.18 kB
'use client'; function getNodeText(node) { function one(node2) { if (node2.type === "text") { return node2.value; } return "children" in node2 ? all(node2) : ""; } function all(node2) { let index = -1; const result = []; while (++index < node2.children.length) { result[index] = one(node2.children[index]); } return result.join(""); } if ("children" in node) { return all(node); } return "value" in node ? node.value : ""; } function filterTree(tree, test) { function preorder(node, index, parentNode) { const children = []; if (!test(node, index, parentNode)) return void 0; if ("children" in node && node.children !== void 0) { let childIndex = -1; while (++childIndex < node.children.length) { const result = preorder(node.children[childIndex], childIndex, node); if (result) { children.push(result); } } if (node.children.length > 0 && children.length === 0) { return void 0; } } const next = {}; for (const key in node) { if (Object.prototype.hasOwnProperty.call(node, key)) { next[key] = key === "children" ? children : node[key]; } } return next; } return preorder(tree); } function rangeParser(strRange) { const res = []; let m; for (const str of strRange.split(",").map((str2) => str2.trim())) { if (/^-?\d+$/.test(str)) { res.push(Number.parseInt(str, 10)); } else if (m = str.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/)) { const [_, lhs, sep, rhs] = m; if (lhs && rhs) { const n_lhs = Number.parseInt(lhs, 10); let n_rhs = Number.parseInt(rhs, 10); const incr = n_lhs < n_rhs ? 1 : -1; if (sep === "-" || sep === ".." || sep === "\u2025") n_rhs += incr; for (let i = n_lhs; i !== n_rhs; i += incr) res.push(i); } } } return res; } const calculateLinesToHighlight = (meta) => { const RE = /{([\d,-]+)}/; const parsedMeta = meta.split(",").map((str) => str.trim()).join(); if (RE.test(parsedMeta)) { const strlineNumbers = (RE.exec(parsedMeta) || [])[1]; const lineNumbers = rangeParser(strlineNumbers); return (index) => lineNumbers.includes(index + 1); } return () => false; }; const calculateStartingLine = (meta) => { const RE = /showLineNumbers=(?<lines>\d+)/i; if (RE.test(meta)) { const { groups } = RE.exec(meta); return Number(groups.lines); } return 1; }; const createLineNodes = (number) => { const a = new Array(number); for (let i = 0; i < number; i++) { a[i] = { type: "element", tagName: "span", properties: { className: [] }, children: [] }; } return a; }; const addNodePositionClosure = () => { let startLineNum = 1; const addNodePosition = (ast) => { return ast.reduce((result, node) => { if (node.type === "text") { const { value } = node; const numLines = (value.match(/\n/g) || "").length; if (numLines === 0) { node.position = { // column: 1 is needed to avoid error with @next/mdx // https://github.com/timlrx/rehype-prism-plus/issues/44 start: { line: startLineNum, column: 1 }, end: { line: startLineNum, column: 1 } }; result.push(node); } else { const lines = value.split("\n"); for (const [i, line] of lines.entries()) { result.push({ type: "text", value: i === lines.length - 1 ? line : `${line} `, position: { start: { line: startLineNum + i, column: 1 }, end: { line: startLineNum + i, column: 1 } } }); } } startLineNum += numLines; return result; } if (Object.prototype.hasOwnProperty.call(node, "children")) { const initialLineNum = startLineNum; node.children = addNodePosition(node.children); result.push(node); node.position = { start: { line: initialLineNum, column: 1 }, end: { line: startLineNum, column: 1 } }; return result; } result.push(node); return result; }, []); }; return addNodePosition; }; function decorator(refractorRoot, diff, meta) { refractorRoot.children = addNodePositionClosure()(refractorRoot.children); if (refractorRoot.children.length > 0) { refractorRoot.position = { start: { line: refractorRoot.children[0].position.start.line, column: 0 }, end: { line: refractorRoot.children[refractorRoot.children.length - 1].position.end.line, column: 0 } }; } else { refractorRoot.position = { start: { line: 0, column: 0 }, end: { line: 0, column: 0 } }; } const shouldHighlightLine = calculateLinesToHighlight(meta); const startingLineNumber = calculateStartingLine(meta); const codeLineArray = createLineNodes(refractorRoot.position.end.line); for (const [i, line] of codeLineArray.entries()) { line.properties.className = ["code-line"]; const treeExtract = filterTree( refractorRoot, (node) => node.position.start.line <= i + 1 && node.position.end.line >= i + 1 ); line.children = treeExtract.children; if (meta.toLowerCase().includes("showLineNumbers".toLowerCase())) { line.properties.line = [(i + startingLineNumber).toString()]; line.properties.className.push("line-number"); } if (shouldHighlightLine(i)) { line.properties.className.push("highlight-line"); } if (diff && getNodeText(line).trim().substring(0, 1) === "-") { line.properties.className.push("deleted"); } else if (diff && getNodeText(line).trim().substring(0, 1) === "+") { line.properties.className.push("inserted"); } } if (codeLineArray.length > 0 && getNodeText(codeLineArray[codeLineArray.length - 1]).trim() === "") { codeLineArray.pop(); } refractorRoot.children = codeLineArray; } export { decorator }; //# sourceMappingURL=decorator.mjs.map