Warp/node_modules/shikiji-transformers/dist/index.mjs

395 lines
11 KiB
JavaScript
Raw Normal View History

2024-01-05 12:14:38 +00:00
import { addClassToHast } from 'shikiji';
2024-01-31 06:33:19 +00:00
import { addClassToHast as addClassToHast$1 } from 'shikiji/core';
2024-01-05 12:14:38 +00:00
function separateContinuousSpaces(inputs) {
const result = [];
let current = "";
function bump() {
if (current.length)
result.push(current);
current = "";
}
inputs.forEach((part, idx) => {
if (isTab(part)) {
bump();
result.push(part);
} else if (isSpace(part) && (isSpace(inputs[idx - 1]) || isSpace(inputs[idx + 1]))) {
bump();
result.push(part);
} else {
current += part;
}
});
bump();
return result;
}
function isTab(part) {
return part === " ";
}
function isSpace(part) {
return part === " " || part === " ";
}
function splitSpaces(parts, type, renderContinuousSpaces = true) {
if (type === "all")
return parts;
let leftCount = 0;
let rightCount = 0;
if (type === "boundary") {
for (let i = 0; i < parts.length; i++) {
if (isSpace(parts[i]))
leftCount++;
else
break;
}
}
if (type === "boundary" || type === "trailing") {
for (let i = parts.length - 1; i >= 0; i--) {
if (isSpace(parts[i]))
rightCount++;
else
break;
}
}
const middle = parts.slice(leftCount, parts.length - rightCount);
return [
...parts.slice(0, leftCount),
...renderContinuousSpaces ? separateContinuousSpaces(middle) : [middle.join("")],
...parts.slice(parts.length - rightCount)
];
}
function transformerRenderWhitespace(options = {}) {
const classMap = {
" ": options.classSpace ?? "space",
" ": options.classTab ?? "tab"
};
const position = options.position ?? "all";
const keys = Object.keys(classMap);
return {
name: "shikiji-transformers:render-whitespace",
// We use `root` hook here to ensure it runs after all other transformers
root(root) {
const pre = root.children[0];
const code = pre.children[0];
code.children.forEach(
(line) => {
if (line.type !== "element")
return;
const elements = line.children.filter((token) => token.type === "element");
const last = elements.length - 1;
line.children = line.children.flatMap((token) => {
if (token.type !== "element")
return token;
const index = elements.indexOf(token);
if (position === "boundary" && index !== 0 && index !== last)
return token;
if (position === "trailing" && index !== last)
return token;
const node = token.children[0];
if (node.type !== "text" || !node.value)
return token;
const parts = splitSpaces(
node.value.split(/([ \t])/).filter((i) => i.length),
position === "boundary" && index === last && last !== 0 ? "trailing" : position,
position !== "trailing"
);
if (parts.length <= 1)
return token;
return parts.map((part) => {
const clone = {
...token,
properties: { ...token.properties }
};
clone.children = [{ type: "text", value: part }];
if (keys.includes(part)) {
addClassToHast(clone, classMap[part]);
delete clone.properties.style;
}
return clone;
});
});
}
);
}
};
}
function transformerRemoveLineBreak() {
return {
name: "shikiji-transformers:remove-line-break",
code(code) {
code.children = code.children.filter((line) => !(line.type === "text" && line.value === "\n"));
}
};
}
function transformerCompactLineOptions(lineOptions = []) {
return {
name: "shikiji-transformers:compact-line-options",
line(node, line) {
const lineOption = lineOptions.find((o) => o.line === line);
if (lineOption?.classes)
addClassToHast(node, lineOption.classes);
return node;
}
};
}
2024-01-31 06:33:19 +00:00
function createCommentNotationTransformer(name, regex, onMatch, removeEmptyLines = false) {
2024-01-05 12:14:38 +00:00
return {
name,
code(code) {
const lines = code.children.filter((i) => i.type === "element");
2024-01-31 06:33:19 +00:00
const linesToRemove = [];
2024-01-05 12:14:38 +00:00
lines.forEach((line, idx) => {
let nodeToRemove;
for (const child of line.children) {
if (child.type !== "element")
continue;
const text = child.children[0];
if (text.type !== "text")
continue;
let replaced = false;
text.value = text.value.replace(regex, (...match) => {
if (onMatch.call(this, match, line, child, lines, idx)) {
replaced = true;
return "";
}
return match[0];
});
if (replaced && !text.value.trim())
nodeToRemove = child;
}
2024-01-31 06:33:19 +00:00
if (nodeToRemove) {
2024-01-05 12:14:38 +00:00
line.children.splice(line.children.indexOf(nodeToRemove), 1);
2024-01-31 06:33:19 +00:00
if (line.children.length === 0) {
linesToRemove.push(line);
if (removeEmptyLines) {
const next = code.children[code.children.indexOf(line) + 1];
if (next && next.type === "text" && next.value === "\n")
linesToRemove.push(next);
}
}
}
2024-01-05 12:14:38 +00:00
});
2024-01-31 06:33:19 +00:00
for (const line of linesToRemove)
code.children.splice(code.children.indexOf(line), 1);
2024-01-05 12:14:38 +00:00
}
};
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function transformerNotationMap(options = {}, name = "shikiji-transformers:notation-map") {
const {
classMap = {},
classActivePre = void 0
} = options;
return createCommentNotationTransformer(
name,
new RegExp(`\\s*(?://|/\\*|<!--|#)\\s+\\[!code (${Object.keys(classMap).map(escapeRegExp).join("|")})(:\\d+)?\\]\\s*(?:\\*/|-->)?`),
function([_, match, range = ":1"], _line, _comment, lines, index) {
const lineNum = Number.parseInt(range.slice(1), 10);
lines.slice(index, index + lineNum).forEach((line) => {
addClassToHast(line, classMap[match]);
});
if (classActivePre)
addClassToHast(this.pre, classActivePre);
return true;
}
);
}
function transformerNotationFocus(options = {}) {
const {
classActiveLine = "focused",
classActivePre = "has-focused"
} = options;
return transformerNotationMap(
{
classMap: {
focus: classActiveLine
},
classActivePre
},
"shikiji-transformers:notation-focus"
);
}
function transformerNotationHighlight(options = {}) {
const {
classActiveLine = "highlighted",
classActivePre = "has-highlighted"
} = options;
return transformerNotationMap(
{
classMap: {
highlight: classActiveLine,
hl: classActiveLine
},
classActivePre
},
"shikiji-transformers:notation-highlight"
);
}
2024-01-31 06:33:19 +00:00
function highlightWordInLine(line, ignoredElement, word, className) {
line.children = line.children.flatMap((span) => {
if (span.type !== "element" || span.tagName !== "span" || span === ignoredElement)
return span;
const textNode = span.children[0];
if (textNode.type !== "text")
return span;
return replaceSpan(span, textNode.value, word, className) ?? span;
});
}
function inheritElement(original, overrides) {
return {
...original,
properties: {
...original.properties
},
...overrides
};
}
function replaceSpan(span, text, word, className) {
const index = text.indexOf(word);
if (index === -1)
return;
const createNode = (value) => inheritElement(span, {
children: [
{
type: "text",
value
}
]
});
const nodes = [];
if (index > 0)
nodes.push(createNode(text.slice(0, index)));
const highlightedNode = createNode(word);
addClassToHast$1(highlightedNode, className);
nodes.push(highlightedNode);
if (index + word.length < text.length)
nodes.push(createNode(text.slice(index + word.length)));
return nodes;
}
function transformerNotationWordHighlight(options = {}) {
const {
classActiveWord = "highlighted-word",
classActivePre = void 0
} = options;
return createCommentNotationTransformer(
"shikiji-transformers:notation-highlight-word",
// comment-start | marker | word | range | comment-end
/^\s*(?:\/\/|\/\*|<!--|#)\s+\[!code word:((?:\\.|[^:\]])+)(:\d+)?\]\s*(?:\*\/|-->)?/,
function([_, word, range], _line, comment, lines, index) {
const lineNum = range ? Number.parseInt(range.slice(1), 10) : lines.length;
word = word.replace(/\\(.)/g, "$1");
lines.slice(index + 1, index + 1 + lineNum).forEach((line) => highlightWordInLine(line, comment, word, classActiveWord));
if (classActivePre)
addClassToHast(this.pre, classActivePre);
return true;
},
true
// remove empty lines
);
}
function parseMetaHighlightWords(meta) {
if (!meta)
return [];
const match = Array.from(meta.matchAll(/\/((?:\\.|[^\/])+?)\//ig));
return match.map((v) => v[1].replace(/\\(.)/g, "$1"));
}
function transformerMetaWordHighlight(options = {}) {
const {
className = "highlighted-word"
} = options;
return {
name: "shikiji-transformers:meta-word-highlight",
line(node) {
if (!this.options.meta?.__raw)
return;
const words = parseMetaHighlightWords(this.options.meta.__raw);
for (const word of words)
highlightWordInLine(node, null, word, className);
return node;
}
};
}
2024-01-05 12:14:38 +00:00
function transformerNotationDiff(options = {}) {
const {
classLineAdd = "diff add",
classLineRemove = "diff remove",
classActivePre = "has-diff"
} = options;
return transformerNotationMap(
{
classMap: {
"++": classLineAdd,
"--": classLineRemove
},
classActivePre
},
"shikiji-transformers:notation-diff"
);
}
function transformerNotationErrorLevel(options = {}) {
const {
classMap = {
error: ["highlighted", "error"],
warning: ["highlighted", "warning"]
},
classActivePre = "has-highlighted"
} = options;
return transformerNotationMap(
{
classMap,
classActivePre
},
"shikiji-transformers:notation-error-level"
);
}
2024-01-31 06:33:19 +00:00
function parseMetaHighlightString(meta) {
if (!meta)
return null;
const match = meta.match(/{([\d,-]+)}/);
if (!match)
return null;
const lines = match[1].split(",").flatMap((v) => {
const num = v.split("-").map((v2) => Number.parseInt(v2, 10));
if (num.length === 1)
return [num[0]];
else
return Array.from({ length: num[1] - num[0] + 1 }, (_, i) => i + num[0]);
});
return lines;
}
const symbol = Symbol("highlighted-lines");
function transformerMetaHighlight(options = {}) {
const {
className = "highlighted"
} = options;
return {
name: "shikiji-transformers:meta-highlight",
line(node, line) {
var _a;
if (!this.options.meta?.__raw)
return;
(_a = this.meta)[symbol] || (_a[symbol] = parseMetaHighlightString(this.options.meta.__raw));
const lines = this.meta[symbol] || [];
if (lines.includes(line))
addClassToHast(node, className);
return node;
}
};
}
export { createCommentNotationTransformer, parseMetaHighlightString, parseMetaHighlightWords, transformerCompactLineOptions, transformerMetaHighlight, transformerMetaWordHighlight, transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, transformerNotationWordHighlight, transformerRemoveLineBreak, transformerRenderWhitespace };