const FontStyle = { NotSet: -1, None: 0, Italic: 1, Bold: 2, Underline: 4, }; const FONT_STYLE_TO_CSS = { [FontStyle.Italic]: "font-style: italic", [FontStyle.Bold]: "font-weight: bold", [FontStyle.Underline]: "text-decoration: underline", }; const renderToHtml = function (lines, options = {}) { const theme = options.theme; const themes = options.themes; const highlightedLines = makeHighlightSet(options.highlightLines); const addLines = makeHighlightSet(options.addLines); const deleteLines = makeHighlightSet(options.deleteLines); const focusLines = makeHighlightSet(options.focusLines); let className = "shiki"; if (highlightedLines.size) { className += " highlighted"; } if (addLines.size) { className += " added"; } if (deleteLines.size) { className += " deleted"; } if (focusLines.size) { className += " focus"; } let html = ""; if (theme) { html += `
`;
} else if (themes) {
const backgroundStyles = Object.entries(themes).map(
([theme, theme$]) => {
if (theme === "light") {
return `background-color:${theme$.theme.bg};`;
}
return `--shiki-${theme}-bg:${theme$.theme.bg};`;
}
);
const foregroundStyles = Object.entries(themes).map(
([theme, theme$]) => {
if (theme === "light") {
return `color:${theme$.theme.fg};`;
}
return `--shiki-${theme}:${theme$.theme.fg};`;
}
);
const classes = `${className} shiki-themes ${Object.values(themes)
.map((theme) => theme.theme.name)
.join(" ")}`;
html += ``;
}
if (options.langId) {
html += `${options.langId}`;
}
html += ``;
lines.forEach((l, index) => {
const lineNumber = index + 1;
let lineClass = "line";
if (highlightedLines.has(lineNumber)) {
lineClass += " highlight";
}
if (addLines.has(lineNumber)) {
lineClass += " add";
}
if (deleteLines.has(lineNumber)) {
lineClass += " del";
}
if (focusLines.has(lineNumber)) {
lineClass += " focus";
}
html += ``;
l.forEach((token) => {
const cssDeclarations = [];
if (theme) {
cssDeclarations.push(`color:${token.color || theme.theme.fg}`);
} else if (themes) {
/**
* The `htmlStyle` property can be a `string` or an `object`. The `string` representation is deprecated.
* @see https://github.com/search?q=repo%3Ashikijs%2Fshiki+htmlStyle&type=code
*/
if (typeof token.htmlStyle === "string") {
cssDeclarations.push(token.htmlStyle);
} else if (typeof token.htmlStyle === "object") {
for (const [key, value] of Object.entries(
token.htmlStyle
)) {
cssDeclarations.push(`${key}:${value}`);
}
}
}
if (token.fontStyle > FontStyle.None) {
cssDeclarations.push(FONT_STYLE_TO_CSS[token.fontStyle]);
}
html += `${escapeHtml(
token.content
)}`;
});
html += `\n`;
});
html = html.replace(/\n*$/, ""); // Get rid of final new lines
html += ``;
return html;
};
const makeHighlightSet = function (highlightLines) {
const lines = new Set();
if (!highlightLines) {
return lines;
}
for (let lineSpec of highlightLines) {
if (lineSpec.toString().includes("-")) {
const [begin, end] = lineSpec
.split("-")
.map((lineNo) => Number(lineNo));
for (let line = begin; line <= end; line++) {
lines.add(line);
}
} else if (lineSpec.toString().trim()) {
lines.add(Number(lineSpec));
}
}
return lines;
};
const htmlEscapes = {
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'",
};
function escapeHtml(html) {
return html.replace(/[&<>"']/g, (chr) => htmlEscapes[chr]);
}
exports.renderToHtml = renderToHtml;