Files
Tiber365/node_modules/@shikijs/core/dist/index.mjs

2228 lines
71 KiB
JavaScript

import { ShikiError as ShikiError$1 } from '@shikijs/types';
export * from '@shikijs/types';
import { createOnigurumaEngine as createOnigurumaEngine$1, loadWasm as loadWasm$1, getDefaultWasmLoader } from '@shikijs/engine-oniguruma';
import { w as warnDeprecated } from './shared/core.Bn_XU0Iv.mjs';
export { e as enableDeprecationWarnings } from './shared/core.Bn_XU0Iv.mjs';
import { FontStyle, INITIAL, EncodedTokenMetadata, Registry as Registry$1, Theme } from '@shikijs/vscode-textmate';
export { FontStyle, EncodedTokenMetadata as StackElementMetadata } from '@shikijs/vscode-textmate';
import { toHtml } from 'hast-util-to-html';
export { toHtml as hastToHtml } from 'hast-util-to-html';
import { createJavaScriptRegexEngine as createJavaScriptRegexEngine$1, defaultJavaScriptRegexConstructor as defaultJavaScriptRegexConstructor$1 } from '@shikijs/engine-javascript';
function createOnigurumaEngine(options) {
warnDeprecated("import `createOnigurumaEngine` from `@shikijs/engine-oniguruma` or `shiki/engine/oniguruma` instead");
return createOnigurumaEngine$1(options);
}
function createWasmOnigEngine(options) {
warnDeprecated("import `createOnigurumaEngine` from `@shikijs/engine-oniguruma` or `shiki/engine/oniguruma` instead");
return createOnigurumaEngine$1(options);
}
function loadWasm(options) {
warnDeprecated("import `loadWasm` from `@shikijs/engine-oniguruma` or `shiki/engine/oniguruma` instead");
return loadWasm$1(options);
}
function toArray(x) {
return Array.isArray(x) ? x : [x];
}
function splitLines(code, preserveEnding = false) {
const parts = code.split(/(\r?\n)/g);
let index = 0;
const lines = [];
for (let i = 0; i < parts.length; i += 2) {
const line = preserveEnding ? parts[i] + (parts[i + 1] || "") : parts[i];
lines.push([line, index]);
index += parts[i].length;
index += parts[i + 1]?.length || 0;
}
return lines;
}
function isPlainLang(lang) {
return !lang || ["plaintext", "txt", "text", "plain"].includes(lang);
}
function isSpecialLang(lang) {
return lang === "ansi" || isPlainLang(lang);
}
function isNoneTheme(theme) {
return theme === "none";
}
function isSpecialTheme(theme) {
return isNoneTheme(theme);
}
function addClassToHast(node, className) {
if (!className)
return node;
node.properties ||= {};
node.properties.class ||= [];
if (typeof node.properties.class === "string")
node.properties.class = node.properties.class.split(/\s+/g);
if (!Array.isArray(node.properties.class))
node.properties.class = [];
const targets = Array.isArray(className) ? className : className.split(/\s+/g);
for (const c of targets) {
if (c && !node.properties.class.includes(c))
node.properties.class.push(c);
}
return node;
}
function splitToken(token, offsets) {
let lastOffset = 0;
const tokens = [];
for (const offset of offsets) {
if (offset > lastOffset) {
tokens.push({
...token,
content: token.content.slice(lastOffset, offset),
offset: token.offset + lastOffset
});
}
lastOffset = offset;
}
if (lastOffset < token.content.length) {
tokens.push({
...token,
content: token.content.slice(lastOffset),
offset: token.offset + lastOffset
});
}
return tokens;
}
function splitTokens(tokens, breakpoints) {
const sorted = Array.from(breakpoints instanceof Set ? breakpoints : new Set(breakpoints)).sort((a, b) => a - b);
if (!sorted.length)
return tokens;
return tokens.map((line) => {
return line.flatMap((token) => {
const breakpointsInToken = sorted.filter((i) => token.offset < i && i < token.offset + token.content.length).map((i) => i - token.offset).sort((a, b) => a - b);
if (!breakpointsInToken.length)
return token;
return splitToken(token, breakpointsInToken);
});
});
}
async function normalizeGetter(p) {
return Promise.resolve(typeof p === "function" ? p() : p).then((r) => r.default || r);
}
function resolveColorReplacements(theme, options) {
const replacements = typeof theme === "string" ? {} : { ...theme.colorReplacements };
const themeName = typeof theme === "string" ? theme : theme.name;
for (const [key, value] of Object.entries(options?.colorReplacements || {})) {
if (typeof value === "string")
replacements[key] = value;
else if (key === themeName)
Object.assign(replacements, value);
}
return replacements;
}
function applyColorReplacements(color, replacements) {
if (!color)
return color;
return replacements?.[color?.toLowerCase()] || color;
}
function getTokenStyleObject(token) {
const styles = {};
if (token.color)
styles.color = token.color;
if (token.bgColor)
styles["background-color"] = token.bgColor;
if (token.fontStyle) {
if (token.fontStyle & FontStyle.Italic)
styles["font-style"] = "italic";
if (token.fontStyle & FontStyle.Bold)
styles["font-weight"] = "bold";
if (token.fontStyle & FontStyle.Underline)
styles["text-decoration"] = "underline";
}
return styles;
}
function stringifyTokenStyle(token) {
if (typeof token === "string")
return token;
return Object.entries(token).map(([key, value]) => `${key}:${value}`).join(";");
}
function createPositionConverter(code) {
const lines = splitLines(code, true).map(([line]) => line);
function indexToPos(index) {
if (index === code.length) {
return {
line: lines.length - 1,
character: lines[lines.length - 1].length
};
}
let character = index;
let line = 0;
for (const lineText of lines) {
if (character < lineText.length)
break;
character -= lineText.length;
line++;
}
return { line, character };
}
function posToIndex(line, character) {
let index = 0;
for (let i = 0; i < line; i++)
index += lines[i].length;
index += character;
return index;
}
return {
lines,
indexToPos,
posToIndex
};
}
class ShikiError extends Error {
constructor(message) {
super(message);
this.name = "ShikiError";
}
}
const _grammarStateMap = /* @__PURE__ */ new WeakMap();
function setLastGrammarStateToMap(keys, state) {
_grammarStateMap.set(keys, state);
}
function getLastGrammarStateFromMap(keys) {
return _grammarStateMap.get(keys);
}
class GrammarState {
/**
* Theme to Stack mapping
*/
_stacks = {};
lang;
get themes() {
return Object.keys(this._stacks);
}
get theme() {
return this.themes[0];
}
get _stack() {
return this._stacks[this.theme];
}
/**
* Static method to create a initial grammar state.
*/
static initial(lang, themes) {
return new GrammarState(
Object.fromEntries(toArray(themes).map((theme) => [theme, INITIAL])),
lang
);
}
constructor(...args) {
if (args.length === 2) {
const [stacksMap, lang] = args;
this.lang = lang;
this._stacks = stacksMap;
} else {
const [stack, lang, theme] = args;
this.lang = lang;
this._stacks = { [theme]: stack };
}
}
/**
* Get the internal stack object.
* @internal
*/
getInternalStack(theme = this.theme) {
return this._stacks[theme];
}
/**
* @deprecated use `getScopes` instead
*/
get scopes() {
warnDeprecated("GrammarState.scopes is deprecated, use GrammarState.getScopes() instead");
return getScopes(this._stacks[this.theme]);
}
getScopes(theme = this.theme) {
return getScopes(this._stacks[theme]);
}
toJSON() {
return {
lang: this.lang,
theme: this.theme,
themes: this.themes,
scopes: this.scopes
};
}
}
function getScopes(stack) {
const scopes = [];
const visited = /* @__PURE__ */ new Set();
function pushScope(stack2) {
if (visited.has(stack2))
return;
visited.add(stack2);
const name = stack2?.nameScopesList?.scopeName;
if (name)
scopes.push(name);
if (stack2.parent)
pushScope(stack2.parent);
}
pushScope(stack);
return scopes;
}
function getGrammarStack(state, theme) {
if (!(state instanceof GrammarState))
throw new ShikiError("Invalid grammar state");
return state.getInternalStack(theme);
}
function transformerDecorations() {
const map = /* @__PURE__ */ new WeakMap();
function getContext(shiki) {
if (!map.has(shiki.meta)) {
let normalizePosition = function(p) {
if (typeof p === "number") {
if (p < 0 || p > shiki.source.length)
throw new ShikiError(`Invalid decoration offset: ${p}. Code length: ${shiki.source.length}`);
return {
...converter.indexToPos(p),
offset: p
};
} else {
const line = converter.lines[p.line];
if (line === undefined)
throw new ShikiError(`Invalid decoration position ${JSON.stringify(p)}. Lines length: ${converter.lines.length}`);
if (p.character < 0 || p.character > line.length)
throw new ShikiError(`Invalid decoration position ${JSON.stringify(p)}. Line ${p.line} length: ${line.length}`);
return {
...p,
offset: converter.posToIndex(p.line, p.character)
};
}
};
const converter = createPositionConverter(shiki.source);
const decorations = (shiki.options.decorations || []).map((d) => ({
...d,
start: normalizePosition(d.start),
end: normalizePosition(d.end)
}));
verifyIntersections(decorations);
map.set(shiki.meta, {
decorations,
converter,
source: shiki.source
});
}
return map.get(shiki.meta);
}
return {
name: "shiki:decorations",
tokens(tokens) {
if (!this.options.decorations?.length)
return;
const ctx = getContext(this);
const breakpoints = ctx.decorations.flatMap((d) => [d.start.offset, d.end.offset]);
const splitted = splitTokens(tokens, breakpoints);
return splitted;
},
code(codeEl) {
if (!this.options.decorations?.length)
return;
const ctx = getContext(this);
const lines = Array.from(codeEl.children).filter((i) => i.type === "element" && i.tagName === "span");
if (lines.length !== ctx.converter.lines.length)
throw new ShikiError(`Number of lines in code element (${lines.length}) does not match the number of lines in the source (${ctx.converter.lines.length}). Failed to apply decorations.`);
function applyLineSection(line, start, end, decoration) {
const lineEl = lines[line];
let text = "";
let startIndex = -1;
let endIndex = -1;
if (start === 0)
startIndex = 0;
if (end === 0)
endIndex = 0;
if (end === Number.POSITIVE_INFINITY)
endIndex = lineEl.children.length;
if (startIndex === -1 || endIndex === -1) {
for (let i = 0; i < lineEl.children.length; i++) {
text += stringify(lineEl.children[i]);
if (startIndex === -1 && text.length === start)
startIndex = i + 1;
if (endIndex === -1 && text.length === end)
endIndex = i + 1;
}
}
if (startIndex === -1)
throw new ShikiError(`Failed to find start index for decoration ${JSON.stringify(decoration.start)}`);
if (endIndex === -1)
throw new ShikiError(`Failed to find end index for decoration ${JSON.stringify(decoration.end)}`);
const children = lineEl.children.slice(startIndex, endIndex);
if (!decoration.alwaysWrap && children.length === lineEl.children.length) {
applyDecoration(lineEl, decoration, "line");
} else if (!decoration.alwaysWrap && children.length === 1 && children[0].type === "element") {
applyDecoration(children[0], decoration, "token");
} else {
const wrapper = {
type: "element",
tagName: "span",
properties: {},
children
};
applyDecoration(wrapper, decoration, "wrapper");
lineEl.children.splice(startIndex, children.length, wrapper);
}
}
function applyLine(line, decoration) {
lines[line] = applyDecoration(lines[line], decoration, "line");
}
function applyDecoration(el, decoration, type) {
const properties = decoration.properties || {};
const transform = decoration.transform || ((i) => i);
el.tagName = decoration.tagName || "span";
el.properties = {
...el.properties,
...properties,
class: el.properties.class
};
if (decoration.properties?.class)
addClassToHast(el, decoration.properties.class);
el = transform(el, type) || el;
return el;
}
const lineApplies = [];
const sorted = ctx.decorations.sort((a, b) => b.start.offset - a.start.offset);
for (const decoration of sorted) {
const { start, end } = decoration;
if (start.line === end.line) {
applyLineSection(start.line, start.character, end.character, decoration);
} else if (start.line < end.line) {
applyLineSection(start.line, start.character, Number.POSITIVE_INFINITY, decoration);
for (let i = start.line + 1; i < end.line; i++)
lineApplies.unshift(() => applyLine(i, decoration));
applyLineSection(end.line, 0, end.character, decoration);
}
}
lineApplies.forEach((i) => i());
}
};
}
function verifyIntersections(items) {
for (let i = 0; i < items.length; i++) {
const foo = items[i];
if (foo.start.offset > foo.end.offset)
throw new ShikiError(`Invalid decoration range: ${JSON.stringify(foo.start)} - ${JSON.stringify(foo.end)}`);
for (let j = i + 1; j < items.length; j++) {
const bar = items[j];
const isFooHasBarStart = foo.start.offset < bar.start.offset && bar.start.offset < foo.end.offset;
const isFooHasBarEnd = foo.start.offset < bar.end.offset && bar.end.offset < foo.end.offset;
const isBarHasFooStart = bar.start.offset < foo.start.offset && foo.start.offset < bar.end.offset;
const isBarHasFooEnd = bar.start.offset < foo.end.offset && foo.end.offset < bar.end.offset;
if (isFooHasBarStart || isFooHasBarEnd || isBarHasFooStart || isBarHasFooEnd) {
if (isFooHasBarEnd && isFooHasBarEnd)
continue;
if (isBarHasFooStart && isBarHasFooEnd)
continue;
throw new ShikiError(`Decorations ${JSON.stringify(foo.start)} and ${JSON.stringify(bar.start)} intersect.`);
}
}
}
}
function stringify(el) {
if (el.type === "text")
return el.value;
if (el.type === "element")
return el.children.map(stringify).join("");
return "";
}
const builtInTransformers = [
/* @__PURE__ */ transformerDecorations()
];
function getTransformers(options) {
return [
...options.transformers || [],
...builtInTransformers
];
}
// src/colors.ts
var namedColors = [
"black",
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
"brightBlack",
"brightRed",
"brightGreen",
"brightYellow",
"brightBlue",
"brightMagenta",
"brightCyan",
"brightWhite"
];
// src/decorations.ts
var decorations = {
1: "bold",
2: "dim",
3: "italic",
4: "underline",
7: "reverse",
9: "strikethrough"
};
// src/parser.ts
function findSequence(value, position) {
const nextEscape = value.indexOf("\x1B[", position);
if (nextEscape !== -1) {
const nextClose = value.indexOf("m", nextEscape);
return {
sequence: value.substring(nextEscape + 2, nextClose).split(";"),
startPosition: nextEscape,
position: nextClose + 1
};
}
return {
position: value.length
};
}
function parseColor(sequence, index) {
let offset = 1;
const colorMode = sequence[index + offset++];
let color;
if (colorMode === "2") {
const rgb = [
sequence[index + offset++],
sequence[index + offset++],
sequence[index + offset]
].map((x) => Number.parseInt(x));
if (rgb.length === 3 && !rgb.some((x) => Number.isNaN(x))) {
color = {
type: "rgb",
rgb
};
}
} else if (colorMode === "5") {
const colorIndex = Number.parseInt(sequence[index + offset]);
if (!Number.isNaN(colorIndex)) {
color = { type: "table", index: Number(colorIndex) };
}
}
return [offset, color];
}
function parseSequence(sequence) {
const commands = [];
for (let i = 0; i < sequence.length; i++) {
const code = sequence[i];
const codeInt = Number.parseInt(code);
if (Number.isNaN(codeInt))
continue;
if (codeInt === 0) {
commands.push({ type: "resetAll" });
} else if (codeInt <= 9) {
const decoration = decorations[codeInt];
if (decoration) {
commands.push({
type: "setDecoration",
value: decorations[codeInt]
});
}
} else if (codeInt <= 29) {
const decoration = decorations[codeInt - 20];
if (decoration) {
commands.push({
type: "resetDecoration",
value: decoration
});
}
} else if (codeInt <= 37) {
commands.push({
type: "setForegroundColor",
value: { type: "named", name: namedColors[codeInt - 30] }
});
} else if (codeInt === 38) {
const [offset, color] = parseColor(sequence, i);
if (color) {
commands.push({
type: "setForegroundColor",
value: color
});
}
i += offset;
} else if (codeInt === 39) {
commands.push({
type: "resetForegroundColor"
});
} else if (codeInt <= 47) {
commands.push({
type: "setBackgroundColor",
value: { type: "named", name: namedColors[codeInt - 40] }
});
} else if (codeInt === 48) {
const [offset, color] = parseColor(sequence, i);
if (color) {
commands.push({
type: "setBackgroundColor",
value: color
});
}
i += offset;
} else if (codeInt === 49) {
commands.push({
type: "resetBackgroundColor"
});
} else if (codeInt >= 90 && codeInt <= 97) {
commands.push({
type: "setForegroundColor",
value: { type: "named", name: namedColors[codeInt - 90 + 8] }
});
} else if (codeInt >= 100 && codeInt <= 107) {
commands.push({
type: "setBackgroundColor",
value: { type: "named", name: namedColors[codeInt - 100 + 8] }
});
}
}
return commands;
}
function createAnsiSequenceParser() {
let foreground = null;
let background = null;
let decorations2 = /* @__PURE__ */ new Set();
return {
parse(value) {
const tokens = [];
let position = 0;
do {
const findResult = findSequence(value, position);
const text = findResult.sequence ? value.substring(position, findResult.startPosition) : value.substring(position);
if (text.length > 0) {
tokens.push({
value: text,
foreground,
background,
decorations: new Set(decorations2)
});
}
if (findResult.sequence) {
const commands = parseSequence(findResult.sequence);
for (const styleToken of commands) {
if (styleToken.type === "resetAll") {
foreground = null;
background = null;
decorations2.clear();
} else if (styleToken.type === "resetForegroundColor") {
foreground = null;
} else if (styleToken.type === "resetBackgroundColor") {
background = null;
} else if (styleToken.type === "resetDecoration") {
decorations2.delete(styleToken.value);
}
}
for (const styleToken of commands) {
if (styleToken.type === "setForegroundColor") {
foreground = styleToken.value;
} else if (styleToken.type === "setBackgroundColor") {
background = styleToken.value;
} else if (styleToken.type === "setDecoration") {
decorations2.add(styleToken.value);
}
}
}
position = findResult.position;
} while (position < value.length);
return tokens;
}
};
}
// src/palette.ts
var defaultNamedColorsMap = {
black: "#000000",
red: "#bb0000",
green: "#00bb00",
yellow: "#bbbb00",
blue: "#0000bb",
magenta: "#ff00ff",
cyan: "#00bbbb",
white: "#eeeeee",
brightBlack: "#555555",
brightRed: "#ff5555",
brightGreen: "#00ff00",
brightYellow: "#ffff55",
brightBlue: "#5555ff",
brightMagenta: "#ff55ff",
brightCyan: "#55ffff",
brightWhite: "#ffffff"
};
function createColorPalette(namedColorsMap = defaultNamedColorsMap) {
function namedColor(name) {
return namedColorsMap[name];
}
function rgbColor(rgb) {
return `#${rgb.map((x) => Math.max(0, Math.min(x, 255)).toString(16).padStart(2, "0")).join("")}`;
}
let colorTable;
function getColorTable() {
if (colorTable) {
return colorTable;
}
colorTable = [];
for (let i = 0; i < namedColors.length; i++) {
colorTable.push(namedColor(namedColors[i]));
}
let levels = [0, 95, 135, 175, 215, 255];
for (let r = 0; r < 6; r++) {
for (let g = 0; g < 6; g++) {
for (let b = 0; b < 6; b++) {
colorTable.push(rgbColor([levels[r], levels[g], levels[b]]));
}
}
}
let level = 8;
for (let i = 0; i < 24; i++, level += 10) {
colorTable.push(rgbColor([level, level, level]));
}
return colorTable;
}
function tableColor(index) {
return getColorTable()[index];
}
function value(color) {
switch (color.type) {
case "named":
return namedColor(color.name);
case "rgb":
return rgbColor(color.rgb);
case "table":
return tableColor(color.index);
}
}
return {
value
};
}
function tokenizeAnsiWithTheme(theme, fileContents, options) {
const colorReplacements = resolveColorReplacements(theme, options);
const lines = splitLines(fileContents);
const colorPalette = createColorPalette(
Object.fromEntries(
namedColors.map((name) => [
name,
theme.colors?.[`terminal.ansi${name[0].toUpperCase()}${name.substring(1)}`]
])
)
);
const parser = createAnsiSequenceParser();
return lines.map(
(line) => parser.parse(line[0]).map((token) => {
let color;
let bgColor;
if (token.decorations.has("reverse")) {
color = token.background ? colorPalette.value(token.background) : theme.bg;
bgColor = token.foreground ? colorPalette.value(token.foreground) : theme.fg;
} else {
color = token.foreground ? colorPalette.value(token.foreground) : theme.fg;
bgColor = token.background ? colorPalette.value(token.background) : undefined;
}
color = applyColorReplacements(color, colorReplacements);
bgColor = applyColorReplacements(bgColor, colorReplacements);
if (token.decorations.has("dim"))
color = dimColor(color);
let fontStyle = FontStyle.None;
if (token.decorations.has("bold"))
fontStyle |= FontStyle.Bold;
if (token.decorations.has("italic"))
fontStyle |= FontStyle.Italic;
if (token.decorations.has("underline"))
fontStyle |= FontStyle.Underline;
return {
content: token.value,
offset: line[1],
// TODO: more accurate offset? might need to fork ansi-sequence-parser
color,
bgColor,
fontStyle
};
})
);
}
function dimColor(color) {
const hexMatch = color.match(/#([0-9a-f]{3})([0-9a-f]{3})?([0-9a-f]{2})?/);
if (hexMatch) {
if (hexMatch[3]) {
const alpha = Math.round(Number.parseInt(hexMatch[3], 16) / 2).toString(16).padStart(2, "0");
return `#${hexMatch[1]}${hexMatch[2]}${alpha}`;
} else if (hexMatch[2]) {
return `#${hexMatch[1]}${hexMatch[2]}80`;
} else {
return `#${Array.from(hexMatch[1]).map((x) => `${x}${x}`).join("")}80`;
}
}
const cssVarMatch = color.match(/var\((--[\w-]+-ansi-[\w-]+)\)/);
if (cssVarMatch)
return `var(${cssVarMatch[1]}-dim)`;
return color;
}
function codeToTokensBase(internal, code, options = {}) {
const {
lang = "text",
theme: themeName = internal.getLoadedThemes()[0]
} = options;
if (isPlainLang(lang) || isNoneTheme(themeName))
return splitLines(code).map((line) => [{ content: line[0], offset: line[1] }]);
const { theme, colorMap } = internal.setTheme(themeName);
if (lang === "ansi")
return tokenizeAnsiWithTheme(theme, code, options);
const _grammar = internal.getLanguage(lang);
if (options.grammarState) {
if (options.grammarState.lang !== _grammar.name) {
throw new ShikiError$1(`Grammar state language "${options.grammarState.lang}" does not match highlight language "${_grammar.name}"`);
}
if (!options.grammarState.themes.includes(theme.name)) {
throw new ShikiError$1(`Grammar state themes "${options.grammarState.themes}" do not contain highlight theme "${theme.name}"`);
}
}
return tokenizeWithTheme(code, _grammar, theme, colorMap, options);
}
function getLastGrammarState(...args) {
if (args.length === 2) {
return getLastGrammarStateFromMap(args[1]);
}
const [internal, code, options = {}] = args;
const {
lang = "text",
theme: themeName = internal.getLoadedThemes()[0]
} = options;
if (isPlainLang(lang) || isNoneTheme(themeName))
throw new ShikiError$1("Plain language does not have grammar state");
if (lang === "ansi")
throw new ShikiError$1("ANSI language does not have grammar state");
const { theme, colorMap } = internal.setTheme(themeName);
const _grammar = internal.getLanguage(lang);
return new GrammarState(
_tokenizeWithTheme(code, _grammar, theme, colorMap, options).stateStack,
_grammar.name,
theme.name
);
}
function tokenizeWithTheme(code, grammar, theme, colorMap, options) {
const result = _tokenizeWithTheme(code, grammar, theme, colorMap, options);
const grammarState = new GrammarState(
_tokenizeWithTheme(code, grammar, theme, colorMap, options).stateStack,
grammar.name,
theme.name
);
setLastGrammarStateToMap(result.tokens, grammarState);
return result.tokens;
}
function _tokenizeWithTheme(code, grammar, theme, colorMap, options) {
const colorReplacements = resolveColorReplacements(theme, options);
const {
tokenizeMaxLineLength = 0,
tokenizeTimeLimit = 500
} = options;
const lines = splitLines(code);
let stateStack = options.grammarState ? getGrammarStack(options.grammarState, theme.name) ?? INITIAL : options.grammarContextCode != null ? _tokenizeWithTheme(
options.grammarContextCode,
grammar,
theme,
colorMap,
{
...options,
grammarState: undefined,
grammarContextCode: undefined
}
).stateStack : INITIAL;
let actual = [];
const final = [];
for (let i = 0, len = lines.length; i < len; i++) {
const [line, lineOffset] = lines[i];
if (line === "") {
actual = [];
final.push([]);
continue;
}
if (tokenizeMaxLineLength > 0 && line.length >= tokenizeMaxLineLength) {
actual = [];
final.push([{
content: line,
offset: lineOffset,
color: "",
fontStyle: 0
}]);
continue;
}
let resultWithScopes;
let tokensWithScopes;
let tokensWithScopesIndex;
if (options.includeExplanation) {
resultWithScopes = grammar.tokenizeLine(line, stateStack);
tokensWithScopes = resultWithScopes.tokens;
tokensWithScopesIndex = 0;
}
const result = grammar.tokenizeLine2(line, stateStack, tokenizeTimeLimit);
const tokensLength = result.tokens.length / 2;
for (let j = 0; j < tokensLength; j++) {
const startIndex = result.tokens[2 * j];
const nextStartIndex = j + 1 < tokensLength ? result.tokens[2 * j + 2] : line.length;
if (startIndex === nextStartIndex)
continue;
const metadata = result.tokens[2 * j + 1];
const color = applyColorReplacements(
colorMap[EncodedTokenMetadata.getForeground(metadata)],
colorReplacements
);
const fontStyle = EncodedTokenMetadata.getFontStyle(metadata);
const token = {
content: line.substring(startIndex, nextStartIndex),
offset: lineOffset + startIndex,
color,
fontStyle
};
if (options.includeExplanation) {
const themeSettingsSelectors = [];
if (options.includeExplanation !== "scopeName") {
for (const setting of theme.settings) {
let selectors;
switch (typeof setting.scope) {
case "string":
selectors = setting.scope.split(/,/).map((scope) => scope.trim());
break;
case "object":
selectors = setting.scope;
break;
default:
continue;
}
themeSettingsSelectors.push({
settings: setting,
selectors: selectors.map((selector) => selector.split(/ /))
});
}
}
token.explanation = [];
let offset = 0;
while (startIndex + offset < nextStartIndex) {
const tokenWithScopes = tokensWithScopes[tokensWithScopesIndex];
const tokenWithScopesText = line.substring(
tokenWithScopes.startIndex,
tokenWithScopes.endIndex
);
offset += tokenWithScopesText.length;
token.explanation.push({
content: tokenWithScopesText,
scopes: options.includeExplanation === "scopeName" ? explainThemeScopesNameOnly(
tokenWithScopes.scopes
) : explainThemeScopesFull(
themeSettingsSelectors,
tokenWithScopes.scopes
)
});
tokensWithScopesIndex += 1;
}
}
actual.push(token);
}
final.push(actual);
actual = [];
stateStack = result.ruleStack;
}
return {
tokens: final,
stateStack
};
}
function explainThemeScopesNameOnly(scopes) {
return scopes.map((scope) => ({ scopeName: scope }));
}
function explainThemeScopesFull(themeSelectors, scopes) {
const result = [];
for (let i = 0, len = scopes.length; i < len; i++) {
const scope = scopes[i];
result[i] = {
scopeName: scope,
themeMatches: explainThemeScope(themeSelectors, scope, scopes.slice(0, i))
};
}
return result;
}
function matchesOne(selector, scope) {
return selector === scope || scope.substring(0, selector.length) === selector && scope[selector.length] === ".";
}
function matches(selectors, scope, parentScopes) {
if (!matchesOne(selectors[selectors.length - 1], scope))
return false;
let selectorParentIndex = selectors.length - 2;
let parentIndex = parentScopes.length - 1;
while (selectorParentIndex >= 0 && parentIndex >= 0) {
if (matchesOne(selectors[selectorParentIndex], parentScopes[parentIndex]))
selectorParentIndex -= 1;
parentIndex -= 1;
}
if (selectorParentIndex === -1)
return true;
return false;
}
function explainThemeScope(themeSettingsSelectors, scope, parentScopes) {
const result = [];
for (const { selectors, settings } of themeSettingsSelectors) {
for (const selectorPieces of selectors) {
if (matches(selectorPieces, scope, parentScopes)) {
result.push(settings);
break;
}
}
}
return result;
}
function codeToTokensWithThemes(internal, code, options) {
const themes = Object.entries(options.themes).filter((i) => i[1]).map((i) => ({ color: i[0], theme: i[1] }));
const themedTokens = themes.map((t) => {
const tokens2 = codeToTokensBase(internal, code, {
...options,
theme: t.theme
});
const state = getLastGrammarStateFromMap(tokens2);
const theme = typeof t.theme === "string" ? t.theme : t.theme.name;
return {
tokens: tokens2,
state,
theme
};
});
const tokens = syncThemesTokenization(
...themedTokens.map((i) => i.tokens)
);
const mergedTokens = tokens[0].map(
(line, lineIdx) => line.map((_token, tokenIdx) => {
const mergedToken = {
content: _token.content,
variants: {},
offset: _token.offset
};
if ("includeExplanation" in options && options.includeExplanation) {
mergedToken.explanation = _token.explanation;
}
tokens.forEach((t, themeIdx) => {
const {
content: _,
explanation: __,
offset: ___,
...styles
} = t[lineIdx][tokenIdx];
mergedToken.variants[themes[themeIdx].color] = styles;
});
return mergedToken;
})
);
const mergedGrammarState = themedTokens[0].state ? new GrammarState(
Object.fromEntries(themedTokens.map((s) => [s.theme, s.state?.getInternalStack(s.theme)])),
themedTokens[0].state.lang
) : undefined;
if (mergedGrammarState)
setLastGrammarStateToMap(mergedTokens, mergedGrammarState);
return mergedTokens;
}
function syncThemesTokenization(...themes) {
const outThemes = themes.map(() => []);
const count = themes.length;
for (let i = 0; i < themes[0].length; i++) {
const lines = themes.map((t) => t[i]);
const outLines = outThemes.map(() => []);
outThemes.forEach((t, i2) => t.push(outLines[i2]));
const indexes = lines.map(() => 0);
const current = lines.map((l) => l[0]);
while (current.every((t) => t)) {
const minLength = Math.min(...current.map((t) => t.content.length));
for (let n = 0; n < count; n++) {
const token = current[n];
if (token.content.length === minLength) {
outLines[n].push(token);
indexes[n] += 1;
current[n] = lines[n][indexes[n]];
} else {
outLines[n].push({
...token,
content: token.content.slice(0, minLength)
});
current[n] = {
...token,
content: token.content.slice(minLength),
offset: token.offset + minLength
};
}
}
}
}
return outThemes;
}
function codeToTokens(internal, code, options) {
let bg;
let fg;
let tokens;
let themeName;
let rootStyle;
let grammarState;
if ("themes" in options) {
const {
defaultColor = "light",
cssVariablePrefix = "--shiki-"
} = options;
const themes = Object.entries(options.themes).filter((i) => i[1]).map((i) => ({ color: i[0], theme: i[1] })).sort((a, b) => a.color === defaultColor ? -1 : b.color === defaultColor ? 1 : 0);
if (themes.length === 0)
throw new ShikiError$1("`themes` option must not be empty");
const themeTokens = codeToTokensWithThemes(
internal,
code,
options
);
grammarState = getLastGrammarStateFromMap(themeTokens);
if (defaultColor && !themes.find((t) => t.color === defaultColor))
throw new ShikiError$1(`\`themes\` option must contain the defaultColor key \`${defaultColor}\``);
const themeRegs = themes.map((t) => internal.getTheme(t.theme));
const themesOrder = themes.map((t) => t.color);
tokens = themeTokens.map((line) => line.map((token) => mergeToken(token, themesOrder, cssVariablePrefix, defaultColor)));
if (grammarState)
setLastGrammarStateToMap(tokens, grammarState);
const themeColorReplacements = themes.map((t) => resolveColorReplacements(t.theme, options));
fg = themes.map((t, idx) => (idx === 0 && defaultColor ? "" : `${cssVariablePrefix + t.color}:`) + (applyColorReplacements(themeRegs[idx].fg, themeColorReplacements[idx]) || "inherit")).join(";");
bg = themes.map((t, idx) => (idx === 0 && defaultColor ? "" : `${cssVariablePrefix + t.color}-bg:`) + (applyColorReplacements(themeRegs[idx].bg, themeColorReplacements[idx]) || "inherit")).join(";");
themeName = `shiki-themes ${themeRegs.map((t) => t.name).join(" ")}`;
rootStyle = defaultColor ? undefined : [fg, bg].join(";");
} else if ("theme" in options) {
const colorReplacements = resolveColorReplacements(options.theme, options);
tokens = codeToTokensBase(
internal,
code,
options
);
const _theme = internal.getTheme(options.theme);
bg = applyColorReplacements(_theme.bg, colorReplacements);
fg = applyColorReplacements(_theme.fg, colorReplacements);
themeName = _theme.name;
grammarState = getLastGrammarStateFromMap(tokens);
} else {
throw new ShikiError$1("Invalid options, either `theme` or `themes` must be provided");
}
return {
tokens,
fg,
bg,
themeName,
rootStyle,
grammarState
};
}
function mergeToken(merged, variantsOrder, cssVariablePrefix, defaultColor) {
const token = {
content: merged.content,
explanation: merged.explanation,
offset: merged.offset
};
const styles = variantsOrder.map((t) => getTokenStyleObject(merged.variants[t]));
const styleKeys = new Set(styles.flatMap((t) => Object.keys(t)));
const mergedStyles = {};
styles.forEach((cur, idx) => {
for (const key of styleKeys) {
const value = cur[key] || "inherit";
if (idx === 0 && defaultColor) {
mergedStyles[key] = value;
} else {
const keyName = key === "color" ? "" : key === "background-color" ? "-bg" : `-${key}`;
const varKey = cssVariablePrefix + variantsOrder[idx] + (key === "color" ? "" : keyName);
mergedStyles[varKey] = value;
}
}
});
token.htmlStyle = mergedStyles;
return token;
}
function codeToHast(internal, code, options, transformerContext = {
meta: {},
options,
codeToHast: (_code, _options) => codeToHast(internal, _code, _options),
codeToTokens: (_code, _options) => codeToTokens(internal, _code, _options)
}) {
let input = code;
for (const transformer of getTransformers(options))
input = transformer.preprocess?.call(transformerContext, input, options) || input;
let {
tokens,
fg,
bg,
themeName,
rootStyle,
grammarState
} = codeToTokens(internal, input, options);
const {
mergeWhitespaces = true
} = options;
if (mergeWhitespaces === true)
tokens = mergeWhitespaceTokens(tokens);
else if (mergeWhitespaces === "never")
tokens = splitWhitespaceTokens(tokens);
const contextSource = {
...transformerContext,
get source() {
return input;
}
};
for (const transformer of getTransformers(options))
tokens = transformer.tokens?.call(contextSource, tokens) || tokens;
return tokensToHast(
tokens,
{
...options,
fg,
bg,
themeName,
rootStyle
},
contextSource,
grammarState
);
}
function tokensToHast(tokens, options, transformerContext, grammarState = getLastGrammarStateFromMap(tokens)) {
const transformers = getTransformers(options);
const lines = [];
const root = {
type: "root",
children: []
};
const {
structure = "classic",
tabindex = "0"
} = options;
let preNode = {
type: "element",
tagName: "pre",
properties: {
class: `shiki ${options.themeName || ""}`,
style: options.rootStyle || `background-color:${options.bg};color:${options.fg}`,
...tabindex !== false && tabindex != null ? {
tabindex: tabindex.toString()
} : {},
...Object.fromEntries(
Array.from(
Object.entries(options.meta || {})
).filter(([key]) => !key.startsWith("_"))
)
},
children: []
};
let codeNode = {
type: "element",
tagName: "code",
properties: {},
children: lines
};
const lineNodes = [];
const context = {
...transformerContext,
structure,
addClassToHast,
get source() {
return transformerContext.source;
},
get tokens() {
return tokens;
},
get options() {
return options;
},
get root() {
return root;
},
get pre() {
return preNode;
},
get code() {
return codeNode;
},
get lines() {
return lineNodes;
}
};
tokens.forEach((line, idx) => {
if (idx) {
if (structure === "inline")
root.children.push({ type: "element", tagName: "br", properties: {}, children: [] });
else if (structure === "classic")
lines.push({ type: "text", value: "\n" });
}
let lineNode = {
type: "element",
tagName: "span",
properties: { class: "line" },
children: []
};
let col = 0;
for (const token of line) {
let tokenNode = {
type: "element",
tagName: "span",
properties: {
...token.htmlAttrs
},
children: [{ type: "text", value: token.content }]
};
if (typeof token.htmlStyle === "string")
warnDeprecated("`htmlStyle` as a string is deprecated. Use an object instead.");
const style = stringifyTokenStyle(token.htmlStyle || getTokenStyleObject(token));
if (style)
tokenNode.properties.style = style;
for (const transformer of transformers)
tokenNode = transformer?.span?.call(context, tokenNode, idx + 1, col, lineNode, token) || tokenNode;
if (structure === "inline")
root.children.push(tokenNode);
else if (structure === "classic")
lineNode.children.push(tokenNode);
col += token.content.length;
}
if (structure === "classic") {
for (const transformer of transformers)
lineNode = transformer?.line?.call(context, lineNode, idx + 1) || lineNode;
lineNodes.push(lineNode);
lines.push(lineNode);
}
});
if (structure === "classic") {
for (const transformer of transformers)
codeNode = transformer?.code?.call(context, codeNode) || codeNode;
preNode.children.push(codeNode);
for (const transformer of transformers)
preNode = transformer?.pre?.call(context, preNode) || preNode;
root.children.push(preNode);
}
let result = root;
for (const transformer of transformers)
result = transformer?.root?.call(context, result) || result;
if (grammarState)
setLastGrammarStateToMap(result, grammarState);
return result;
}
function mergeWhitespaceTokens(tokens) {
return tokens.map((line) => {
const newLine = [];
let carryOnContent = "";
let firstOffset = 0;
line.forEach((token, idx) => {
const isUnderline = token.fontStyle && token.fontStyle & FontStyle.Underline;
const couldMerge = !isUnderline;
if (couldMerge && token.content.match(/^\s+$/) && line[idx + 1]) {
if (!firstOffset)
firstOffset = token.offset;
carryOnContent += token.content;
} else {
if (carryOnContent) {
if (couldMerge) {
newLine.push({
...token,
offset: firstOffset,
content: carryOnContent + token.content
});
} else {
newLine.push(
{
content: carryOnContent,
offset: firstOffset
},
token
);
}
firstOffset = 0;
carryOnContent = "";
} else {
newLine.push(token);
}
}
});
return newLine;
});
}
function splitWhitespaceTokens(tokens) {
return tokens.map((line) => {
return line.flatMap((token) => {
if (token.content.match(/^\s+$/))
return token;
const match = token.content.match(/^(\s*)(.*?)(\s*)$/);
if (!match)
return token;
const [, leading, content, trailing] = match;
if (!leading && !trailing)
return token;
const expanded = [{
...token,
offset: token.offset + leading.length,
content
}];
if (leading) {
expanded.unshift({
content: leading,
offset: token.offset
});
}
if (trailing) {
expanded.push({
content: trailing,
offset: token.offset + leading.length + content.length
});
}
return expanded;
});
});
}
function codeToHtml(internal, code, options) {
const context = {
meta: {},
options,
codeToHast: (_code, _options) => codeToHast(internal, _code, _options),
codeToTokens: (_code, _options) => codeToTokens(internal, _code, _options)
};
let result = toHtml(codeToHast(internal, code, options, context));
for (const transformer of getTransformers(options))
result = transformer.postprocess?.call(context, result, options) || result;
return result;
}
const VSCODE_FALLBACK_EDITOR_FG = { light: "#333333", dark: "#bbbbbb" };
const VSCODE_FALLBACK_EDITOR_BG = { light: "#fffffe", dark: "#1e1e1e" };
const RESOLVED_KEY = "__shiki_resolved";
function normalizeTheme(rawTheme) {
if (rawTheme?.[RESOLVED_KEY])
return rawTheme;
const theme = {
...rawTheme
};
if (theme.tokenColors && !theme.settings) {
theme.settings = theme.tokenColors;
delete theme.tokenColors;
}
theme.type ||= "dark";
theme.colorReplacements = { ...theme.colorReplacements };
theme.settings ||= [];
let { bg, fg } = theme;
if (!bg || !fg) {
const globalSetting = theme.settings ? theme.settings.find((s) => !s.name && !s.scope) : undefined;
if (globalSetting?.settings?.foreground)
fg = globalSetting.settings.foreground;
if (globalSetting?.settings?.background)
bg = globalSetting.settings.background;
if (!fg && theme?.colors?.["editor.foreground"])
fg = theme.colors["editor.foreground"];
if (!bg && theme?.colors?.["editor.background"])
bg = theme.colors["editor.background"];
if (!fg)
fg = theme.type === "light" ? VSCODE_FALLBACK_EDITOR_FG.light : VSCODE_FALLBACK_EDITOR_FG.dark;
if (!bg)
bg = theme.type === "light" ? VSCODE_FALLBACK_EDITOR_BG.light : VSCODE_FALLBACK_EDITOR_BG.dark;
theme.fg = fg;
theme.bg = bg;
}
if (!(theme.settings[0] && theme.settings[0].settings && !theme.settings[0].scope)) {
theme.settings.unshift({
settings: {
foreground: theme.fg,
background: theme.bg
}
});
}
let replacementCount = 0;
const replacementMap = /* @__PURE__ */ new Map();
function getReplacementColor(value) {
if (replacementMap.has(value))
return replacementMap.get(value);
replacementCount += 1;
const hex = `#${replacementCount.toString(16).padStart(8, "0").toLowerCase()}`;
if (theme.colorReplacements?.[`#${hex}`])
return getReplacementColor(value);
replacementMap.set(value, hex);
return hex;
}
theme.settings = theme.settings.map((setting) => {
const replaceFg = setting.settings?.foreground && !setting.settings.foreground.startsWith("#");
const replaceBg = setting.settings?.background && !setting.settings.background.startsWith("#");
if (!replaceFg && !replaceBg)
return setting;
const clone = {
...setting,
settings: {
...setting.settings
}
};
if (replaceFg) {
const replacement = getReplacementColor(setting.settings.foreground);
theme.colorReplacements[replacement] = setting.settings.foreground;
clone.settings.foreground = replacement;
}
if (replaceBg) {
const replacement = getReplacementColor(setting.settings.background);
theme.colorReplacements[replacement] = setting.settings.background;
clone.settings.background = replacement;
}
return clone;
});
for (const key of Object.keys(theme.colors || {})) {
if (key === "editor.foreground" || key === "editor.background" || key.startsWith("terminal.ansi")) {
if (!theme.colors[key]?.startsWith("#")) {
const replacement = getReplacementColor(theme.colors[key]);
theme.colorReplacements[replacement] = theme.colors[key];
theme.colors[key] = replacement;
}
}
}
Object.defineProperty(theme, RESOLVED_KEY, {
enumerable: false,
writable: false,
value: true
});
return theme;
}
async function resolveLangs(langs) {
return Array.from(new Set((await Promise.all(
langs.filter((l) => !isSpecialLang(l)).map(async (lang) => await normalizeGetter(lang).then((r) => Array.isArray(r) ? r : [r]))
)).flat()));
}
async function resolveThemes(themes) {
const resolved = await Promise.all(
themes.map(
async (theme) => isSpecialTheme(theme) ? null : normalizeTheme(await normalizeGetter(theme))
)
);
return resolved.filter((i) => !!i);
}
class Registry extends Registry$1 {
constructor(_resolver, _themes, _langs, _alias = {}) {
super(_resolver);
this._resolver = _resolver;
this._themes = _themes;
this._langs = _langs;
this._alias = _alias;
this._themes.map((t) => this.loadTheme(t));
this.loadLanguages(this._langs);
}
_resolvedThemes = /* @__PURE__ */ new Map();
_resolvedGrammars = /* @__PURE__ */ new Map();
_langMap = /* @__PURE__ */ new Map();
_langGraph = /* @__PURE__ */ new Map();
_textmateThemeCache = /* @__PURE__ */ new WeakMap();
_loadedThemesCache = null;
_loadedLanguagesCache = null;
getTheme(theme) {
if (typeof theme === "string")
return this._resolvedThemes.get(theme);
else
return this.loadTheme(theme);
}
loadTheme(theme) {
const _theme = normalizeTheme(theme);
if (_theme.name) {
this._resolvedThemes.set(_theme.name, _theme);
this._loadedThemesCache = null;
}
return _theme;
}
getLoadedThemes() {
if (!this._loadedThemesCache)
this._loadedThemesCache = [...this._resolvedThemes.keys()];
return this._loadedThemesCache;
}
// Override and re-implement this method to cache the textmate themes as `TextMateTheme.createFromRawTheme`
// is expensive. Themes can switch often especially for dual-theme support.
//
// The parent class also accepts `colorMap` as the second parameter, but since we don't use that,
// we omit here so it's easier to cache the themes.
setTheme(theme) {
let textmateTheme = this._textmateThemeCache.get(theme);
if (!textmateTheme) {
textmateTheme = Theme.createFromRawTheme(theme);
this._textmateThemeCache.set(theme, textmateTheme);
}
this._syncRegistry.setTheme(textmateTheme);
}
getGrammar(name) {
if (this._alias[name]) {
const resolved = /* @__PURE__ */ new Set([name]);
while (this._alias[name]) {
name = this._alias[name];
if (resolved.has(name))
throw new ShikiError(`Circular alias \`${Array.from(resolved).join(" -> ")} -> ${name}\``);
resolved.add(name);
}
}
return this._resolvedGrammars.get(name);
}
loadLanguage(lang) {
if (this.getGrammar(lang.name))
return;
const embeddedLazilyBy = new Set(
[...this._langMap.values()].filter((i) => i.embeddedLangsLazy?.includes(lang.name))
);
this._resolver.addLanguage(lang);
const grammarConfig = {
balancedBracketSelectors: lang.balancedBracketSelectors || ["*"],
unbalancedBracketSelectors: lang.unbalancedBracketSelectors || []
};
this._syncRegistry._rawGrammars.set(lang.scopeName, lang);
const g = this.loadGrammarWithConfiguration(lang.scopeName, 1, grammarConfig);
g.name = lang.name;
this._resolvedGrammars.set(lang.name, g);
if (lang.aliases) {
lang.aliases.forEach((alias) => {
this._alias[alias] = lang.name;
});
}
this._loadedLanguagesCache = null;
if (embeddedLazilyBy.size) {
for (const e of embeddedLazilyBy) {
this._resolvedGrammars.delete(e.name);
this._loadedLanguagesCache = null;
this._syncRegistry?._injectionGrammars?.delete(e.scopeName);
this._syncRegistry?._grammars?.delete(e.scopeName);
this.loadLanguage(this._langMap.get(e.name));
}
}
}
dispose() {
super.dispose();
this._resolvedThemes.clear();
this._resolvedGrammars.clear();
this._langMap.clear();
this._langGraph.clear();
this._loadedThemesCache = null;
}
loadLanguages(langs) {
for (const lang of langs)
this.resolveEmbeddedLanguages(lang);
const langsGraphArray = Array.from(this._langGraph.entries());
const missingLangs = langsGraphArray.filter(([_, lang]) => !lang);
if (missingLangs.length) {
const dependents = langsGraphArray.filter(([_, lang]) => lang && lang.embeddedLangs?.some((l) => missingLangs.map(([name]) => name).includes(l))).filter((lang) => !missingLangs.includes(lang));
throw new ShikiError(`Missing languages ${missingLangs.map(([name]) => `\`${name}\``).join(", ")}, required by ${dependents.map(([name]) => `\`${name}\``).join(", ")}`);
}
for (const [_, lang] of langsGraphArray)
this._resolver.addLanguage(lang);
for (const [_, lang] of langsGraphArray)
this.loadLanguage(lang);
}
getLoadedLanguages() {
if (!this._loadedLanguagesCache) {
this._loadedLanguagesCache = [
.../* @__PURE__ */ new Set([...this._resolvedGrammars.keys(), ...Object.keys(this._alias)])
];
}
return this._loadedLanguagesCache;
}
resolveEmbeddedLanguages(lang) {
this._langMap.set(lang.name, lang);
this._langGraph.set(lang.name, lang);
if (lang.embeddedLangs) {
for (const embeddedLang of lang.embeddedLangs)
this._langGraph.set(embeddedLang, this._langMap.get(embeddedLang));
}
}
}
class Resolver {
_langs = /* @__PURE__ */ new Map();
_scopeToLang = /* @__PURE__ */ new Map();
_injections = /* @__PURE__ */ new Map();
_onigLib;
constructor(engine, langs) {
this._onigLib = {
createOnigScanner: (patterns) => engine.createScanner(patterns),
createOnigString: (s) => engine.createString(s)
};
langs.forEach((i) => this.addLanguage(i));
}
get onigLib() {
return this._onigLib;
}
getLangRegistration(langIdOrAlias) {
return this._langs.get(langIdOrAlias);
}
loadGrammar(scopeName) {
return this._scopeToLang.get(scopeName);
}
addLanguage(l) {
this._langs.set(l.name, l);
if (l.aliases) {
l.aliases.forEach((a) => {
this._langs.set(a, l);
});
}
this._scopeToLang.set(l.scopeName, l);
if (l.injectTo) {
l.injectTo.forEach((i) => {
if (!this._injections.get(i))
this._injections.set(i, []);
this._injections.get(i).push(l.scopeName);
});
}
}
getInjections(scopeName) {
const scopeParts = scopeName.split(".");
let injections = [];
for (let i = 1; i <= scopeParts.length; i++) {
const subScopeName = scopeParts.slice(0, i).join(".");
injections = [...injections, ...this._injections.get(subScopeName) || []];
}
return injections;
}
}
let instancesCount = 0;
function createShikiInternalSync(options) {
instancesCount += 1;
if (options.warnings !== false && instancesCount >= 10 && instancesCount % 10 === 0)
console.warn(`[Shiki] ${instancesCount} instances have been created. Shiki is supposed to be used as a singleton, consider refactoring your code to cache your highlighter instance; Or call \`highlighter.dispose()\` to release unused instances.`);
let isDisposed = false;
if (!options.engine)
throw new ShikiError("`engine` option is required for synchronous mode");
const langs = (options.langs || []).flat(1);
const themes = (options.themes || []).flat(1).map(normalizeTheme);
const resolver = new Resolver(options.engine, langs);
const _registry = new Registry(resolver, themes, langs, options.langAlias);
let _lastTheme;
function getLanguage(name) {
ensureNotDisposed();
const _lang = _registry.getGrammar(typeof name === "string" ? name : name.name);
if (!_lang)
throw new ShikiError(`Language \`${name}\` not found, you may need to load it first`);
return _lang;
}
function getTheme(name) {
if (name === "none")
return { bg: "", fg: "", name: "none", settings: [], type: "dark" };
ensureNotDisposed();
const _theme = _registry.getTheme(name);
if (!_theme)
throw new ShikiError(`Theme \`${name}\` not found, you may need to load it first`);
return _theme;
}
function setTheme(name) {
ensureNotDisposed();
const theme = getTheme(name);
if (_lastTheme !== name) {
_registry.setTheme(theme);
_lastTheme = name;
}
const colorMap = _registry.getColorMap();
return {
theme,
colorMap
};
}
function getLoadedThemes() {
ensureNotDisposed();
return _registry.getLoadedThemes();
}
function getLoadedLanguages() {
ensureNotDisposed();
return _registry.getLoadedLanguages();
}
function loadLanguageSync(...langs2) {
ensureNotDisposed();
_registry.loadLanguages(langs2.flat(1));
}
async function loadLanguage(...langs2) {
return loadLanguageSync(await resolveLangs(langs2));
}
function loadThemeSync(...themes2) {
ensureNotDisposed();
for (const theme of themes2.flat(1)) {
_registry.loadTheme(theme);
}
}
async function loadTheme(...themes2) {
ensureNotDisposed();
return loadThemeSync(await resolveThemes(themes2));
}
function ensureNotDisposed() {
if (isDisposed)
throw new ShikiError("Shiki instance has been disposed");
}
function dispose() {
if (isDisposed)
return;
isDisposed = true;
_registry.dispose();
instancesCount -= 1;
}
return {
setTheme,
getTheme,
getLanguage,
getLoadedThemes,
getLoadedLanguages,
loadLanguage,
loadLanguageSync,
loadTheme,
loadThemeSync,
dispose,
[Symbol.dispose]: dispose
};
}
async function createShikiInternal(options = {}) {
if (options.loadWasm) {
warnDeprecated("`loadWasm` option is deprecated. Use `engine: createOnigurumaEngine(loadWasm)` instead.");
}
const [
themes,
langs,
engine
] = await Promise.all([
resolveThemes(options.themes || []),
resolveLangs(options.langs || []),
options.engine || createOnigurumaEngine$1(options.loadWasm || getDefaultWasmLoader())
]);
return createShikiInternalSync({
...options,
loadWasm: undefined,
themes,
langs,
engine
});
}
function getShikiInternal(options = {}) {
warnDeprecated("`getShikiInternal` is deprecated. Use `createShikiInternal` instead.");
return createShikiInternal(options);
}
async function createHighlighterCore(options = {}) {
const internal = await createShikiInternal(options);
return {
getLastGrammarState: (...args) => getLastGrammarState(internal, ...args),
codeToTokensBase: (code, options2) => codeToTokensBase(internal, code, options2),
codeToTokensWithThemes: (code, options2) => codeToTokensWithThemes(internal, code, options2),
codeToTokens: (code, options2) => codeToTokens(internal, code, options2),
codeToHast: (code, options2) => codeToHast(internal, code, options2),
codeToHtml: (code, options2) => codeToHtml(internal, code, options2),
...internal,
getInternalContext: () => internal
};
}
function createHighlighterCoreSync(options = {}) {
const internal = createShikiInternalSync(options);
return {
getLastGrammarState: (...args) => getLastGrammarState(internal, ...args),
codeToTokensBase: (code, options2) => codeToTokensBase(internal, code, options2),
codeToTokensWithThemes: (code, options2) => codeToTokensWithThemes(internal, code, options2),
codeToTokens: (code, options2) => codeToTokens(internal, code, options2),
codeToHast: (code, options2) => codeToHast(internal, code, options2),
codeToHtml: (code, options2) => codeToHtml(internal, code, options2),
...internal,
getInternalContext: () => internal
};
}
function makeSingletonHighlighterCore(createHighlighter) {
let _shiki;
async function getSingletonHighlighterCore2(options = {}) {
if (!_shiki) {
_shiki = createHighlighter({
...options,
themes: options.themes || [],
langs: options.langs || []
});
return _shiki;
} else {
const s = await _shiki;
await Promise.all([
s.loadTheme(...options.themes || []),
s.loadLanguage(...options.langs || [])
]);
return s;
}
}
return getSingletonHighlighterCore2;
}
const getSingletonHighlighterCore = /* @__PURE__ */ makeSingletonHighlighterCore(createHighlighterCore);
function getHighlighterCore(options = {}) {
warnDeprecated("`getHighlighterCore` is deprecated. Use `createHighlighterCore` or `getSingletonHighlighterCore` instead.");
return createHighlighterCore(options);
}
function createdBundledHighlighter(arg1, arg2, arg3) {
let bundledLanguages;
let bundledThemes;
let engine;
if (arg2) {
warnDeprecated("`createdBundledHighlighter` signature with `bundledLanguages` and `bundledThemes` is deprecated. Use the options object signature instead.");
bundledLanguages = arg1;
bundledThemes = arg2;
engine = () => createOnigurumaEngine(arg3);
} else {
const options = arg1;
bundledLanguages = options.langs;
bundledThemes = options.themes;
engine = options.engine;
}
async function createHighlighter(options) {
function resolveLang(lang) {
if (typeof lang === "string") {
if (isSpecialLang(lang))
return [];
const bundle = bundledLanguages[lang];
if (!bundle)
throw new ShikiError$1(`Language \`${lang}\` is not included in this bundle. You may want to load it from external source.`);
return bundle;
}
return lang;
}
function resolveTheme(theme) {
if (isSpecialTheme(theme))
return "none";
if (typeof theme === "string") {
const bundle = bundledThemes[theme];
if (!bundle)
throw new ShikiError$1(`Theme \`${theme}\` is not included in this bundle. You may want to load it from external source.`);
return bundle;
}
return theme;
}
const _themes = (options.themes ?? []).map((i) => resolveTheme(i));
const langs = (options.langs ?? []).map((i) => resolveLang(i));
const core = await createHighlighterCore({
engine: options.engine ?? engine(),
...options,
themes: _themes,
langs
});
return {
...core,
loadLanguage(...langs2) {
return core.loadLanguage(...langs2.map(resolveLang));
},
loadTheme(...themes) {
return core.loadTheme(...themes.map(resolveTheme));
}
};
}
return createHighlighter;
}
function makeSingletonHighlighter(createHighlighter) {
let _shiki;
async function getSingletonHighlighter(options = {}) {
if (!_shiki) {
_shiki = createHighlighter({
...options,
themes: options.themes || [],
langs: options.langs || []
});
return _shiki;
} else {
const s = await _shiki;
await Promise.all([
s.loadTheme(...options.themes || []),
s.loadLanguage(...options.langs || [])
]);
return s;
}
}
return getSingletonHighlighter;
}
function createSingletonShorthands(createHighlighter) {
const getSingletonHighlighter = makeSingletonHighlighter(createHighlighter);
return {
getSingletonHighlighter(options) {
return getSingletonHighlighter(options);
},
async codeToHtml(code, options) {
const shiki = await getSingletonHighlighter({
langs: [options.lang],
themes: "theme" in options ? [options.theme] : Object.values(options.themes)
});
return shiki.codeToHtml(code, options);
},
async codeToHast(code, options) {
const shiki = await getSingletonHighlighter({
langs: [options.lang],
themes: "theme" in options ? [options.theme] : Object.values(options.themes)
});
return shiki.codeToHast(code, options);
},
async codeToTokens(code, options) {
const shiki = await getSingletonHighlighter({
langs: [options.lang],
themes: "theme" in options ? [options.theme] : Object.values(options.themes)
});
return shiki.codeToTokens(code, options);
},
async codeToTokensBase(code, options) {
const shiki = await getSingletonHighlighter({
langs: [options.lang],
themes: [options.theme]
});
return shiki.codeToTokensBase(code, options);
},
async codeToTokensWithThemes(code, options) {
const shiki = await getSingletonHighlighter({
langs: [options.lang],
themes: Object.values(options.themes).filter(Boolean)
});
return shiki.codeToTokensWithThemes(code, options);
},
async getLastGrammarState(code, options) {
const shiki = await getSingletonHighlighter({
langs: [options.lang],
themes: [options.theme]
});
return shiki.getLastGrammarState(code, options);
}
};
}
function createJavaScriptRegexEngine(options) {
warnDeprecated("import `createJavaScriptRegexEngine` from `@shikijs/engine-javascript` or `shiki/engine/javascript` instead");
return createJavaScriptRegexEngine$1(options);
}
function defaultJavaScriptRegexConstructor(pattern) {
warnDeprecated("import `defaultJavaScriptRegexConstructor` from `@shikijs/engine-javascript` or `shiki/engine/javascript` instead");
return defaultJavaScriptRegexConstructor$1(pattern);
}
function createCssVariablesTheme(options = {}) {
const {
name = "css-variables",
variablePrefix = "--shiki-",
fontStyle = true
} = options;
const variable = (name2) => {
if (options.variableDefaults?.[name2])
return `var(${variablePrefix}${name2}, ${options.variableDefaults[name2]})`;
return `var(${variablePrefix}${name2})`;
};
const theme = {
name,
type: "dark",
colors: {
"editor.foreground": variable("foreground"),
"editor.background": variable("background"),
"terminal.ansiBlack": variable("ansi-black"),
"terminal.ansiRed": variable("ansi-red"),
"terminal.ansiGreen": variable("ansi-green"),
"terminal.ansiYellow": variable("ansi-yellow"),
"terminal.ansiBlue": variable("ansi-blue"),
"terminal.ansiMagenta": variable("ansi-magenta"),
"terminal.ansiCyan": variable("ansi-cyan"),
"terminal.ansiWhite": variable("ansi-white"),
"terminal.ansiBrightBlack": variable("ansi-bright-black"),
"terminal.ansiBrightRed": variable("ansi-bright-red"),
"terminal.ansiBrightGreen": variable("ansi-bright-green"),
"terminal.ansiBrightYellow": variable("ansi-bright-yellow"),
"terminal.ansiBrightBlue": variable("ansi-bright-blue"),
"terminal.ansiBrightMagenta": variable("ansi-bright-magenta"),
"terminal.ansiBrightCyan": variable("ansi-bright-cyan"),
"terminal.ansiBrightWhite": variable("ansi-bright-white")
},
tokenColors: [
{
scope: [
"keyword.operator.accessor",
"meta.group.braces.round.function.arguments",
"meta.template.expression",
"markup.fenced_code meta.embedded.block"
],
settings: {
foreground: variable("foreground")
}
},
{
scope: "emphasis",
settings: {
fontStyle: "italic"
}
},
{
scope: ["strong", "markup.heading.markdown", "markup.bold.markdown"],
settings: {
fontStyle: "bold"
}
},
{
scope: ["markup.italic.markdown"],
settings: {
fontStyle: "italic"
}
},
{
scope: "meta.link.inline.markdown",
settings: {
fontStyle: "underline",
foreground: variable("token-link")
}
},
{
scope: ["string", "markup.fenced_code", "markup.inline"],
settings: {
foreground: variable("token-string")
}
},
{
scope: ["comment", "string.quoted.docstring.multi"],
settings: {
foreground: variable("token-comment")
}
},
{
scope: [
"constant.numeric",
"constant.language",
"constant.other.placeholder",
"constant.character.format.placeholder",
"variable.language.this",
"variable.other.object",
"variable.other.class",
"variable.other.constant",
"meta.property-name",
"meta.property-value",
"support"
],
settings: {
foreground: variable("token-constant")
}
},
{
scope: [
"keyword",
"storage.modifier",
"storage.type",
"storage.control.clojure",
"entity.name.function.clojure",
"entity.name.tag.yaml",
"support.function.node",
"support.type.property-name.json",
"punctuation.separator.key-value",
"punctuation.definition.template-expression"
],
settings: {
foreground: variable("token-keyword")
}
},
{
scope: "variable.parameter.function",
settings: {
foreground: variable("token-parameter")
}
},
{
scope: [
"support.function",
"entity.name.type",
"entity.other.inherited-class",
"meta.function-call",
"meta.instance.constructor",
"entity.other.attribute-name",
"entity.name.function",
"constant.keyword.clojure"
],
settings: {
foreground: variable("token-function")
}
},
{
scope: [
"entity.name.tag",
"string.quoted",
"string.regexp",
"string.interpolated",
"string.template",
"string.unquoted.plain.out.yaml",
"keyword.other.template"
],
settings: {
foreground: variable("token-string-expression")
}
},
{
scope: [
"punctuation.definition.arguments",
"punctuation.definition.dict",
"punctuation.separator",
"meta.function-call.arguments"
],
settings: {
foreground: variable("token-punctuation")
}
},
{
// [Custom] Markdown links
scope: [
"markup.underline.link",
"punctuation.definition.metadata.markdown"
],
settings: {
foreground: variable("token-link")
}
},
{
// [Custom] Markdown list
scope: ["beginning.punctuation.definition.list.markdown"],
settings: {
foreground: variable("token-string")
}
},
{
// [Custom] Markdown punctuation definition brackets
scope: [
"punctuation.definition.string.begin.markdown",
"punctuation.definition.string.end.markdown",
"string.other.link.title.markdown",
"string.other.link.description.markdown"
],
settings: {
foreground: variable("token-keyword")
}
},
{
// [Custom] Diff
scope: [
"markup.inserted",
"meta.diff.header.to-file",
"punctuation.definition.inserted"
],
settings: {
foreground: variable("token-inserted")
}
},
{
scope: [
"markup.deleted",
"meta.diff.header.from-file",
"punctuation.definition.deleted"
],
settings: {
foreground: variable("token-deleted")
}
},
{
scope: [
"markup.changed",
"punctuation.definition.changed"
],
settings: {
foreground: variable("token-changed")
}
}
]
};
if (!fontStyle) {
theme.tokenColors = theme.tokenColors?.map((tokenColor) => {
if (tokenColor.settings?.fontStyle)
delete tokenColor.settings.fontStyle;
return tokenColor;
});
}
return theme;
}
export { addClassToHast, applyColorReplacements, codeToHast, codeToHtml, codeToTokens, codeToTokensBase, codeToTokensWithThemes, createCssVariablesTheme, createHighlighterCore, createHighlighterCoreSync, createJavaScriptRegexEngine, createOnigurumaEngine, createPositionConverter, createShikiInternal, createShikiInternalSync, createSingletonShorthands, createWasmOnigEngine, createdBundledHighlighter, defaultJavaScriptRegexConstructor, getHighlighterCore, getShikiInternal, getSingletonHighlighterCore, getTokenStyleObject, isNoneTheme, isPlainLang, isSpecialLang, isSpecialTheme, loadWasm, makeSingletonHighlighter, makeSingletonHighlighterCore, normalizeGetter, normalizeTheme, resolveColorReplacements, splitLines, splitToken, splitTokens, stringifyTokenStyle, toArray, tokenizeAnsiWithTheme, tokenizeWithTheme, tokensToHast, transformerDecorations, warnDeprecated };