import { INITIAL, EncodedTokenMetadata } from '@shikijs/vscode-textmate'; function textmateThemeToMonacoTheme(theme) { let rules = "rules" in theme ? theme.rules : undefined; if (!rules) { rules = []; const themeSettings = theme.settings || theme.tokenColors; for (const { scope, settings: { foreground, background, fontStyle } } of themeSettings) { const scopes = Array.isArray(scope) ? scope : [scope]; for (const s of scopes) { if (s && (foreground || background || fontStyle)) { rules.push({ token: s, foreground: normalizeColor(foreground), background: normalizeColor(background), fontStyle }); } } } } const colors = Object.fromEntries( Object.entries(theme.colors || {}).map(([key, value]) => [key, `#${normalizeColor(value)}`]) ); return { base: theme.type === "light" ? "vs" : "vs-dark", inherit: false, colors, rules }; } function shikiToMonaco(highlighter, monaco, options = {}) { const themeMap = /* @__PURE__ */ new Map(); const themeIds = highlighter.getLoadedThemes(); for (const themeId of themeIds) { const tmTheme = highlighter.getTheme(themeId); const monacoTheme = textmateThemeToMonacoTheme(tmTheme); themeMap.set(themeId, monacoTheme); monaco.editor.defineTheme(themeId, monacoTheme); } const colorMap = []; const colorToScopeMap = /* @__PURE__ */ new Map(); const _setTheme = monaco.editor.setTheme.bind(monaco.editor); monaco.editor.setTheme = (themeName) => { const ret = highlighter.setTheme(themeName); const theme = themeMap.get(themeName); colorMap.length = ret.colorMap.length; ret.colorMap.forEach((color, i) => { colorMap[i] = color; }); colorToScopeMap.clear(); theme?.rules.forEach((rule) => { const c = normalizeColor(rule.foreground); if (c && !colorToScopeMap.has(c)) colorToScopeMap.set(c, rule.token); }); _setTheme(themeName); }; monaco.editor.setTheme(themeIds[0]); function findScopeByColor(color) { return colorToScopeMap.get(color); } const { tokenizeMaxLineLength = 2e4, tokenizeTimeLimit = 500 } = options; const monacoLanguageIds = new Set(monaco.languages.getLanguages().map((l) => l.id)); for (const lang of highlighter.getLoadedLanguages()) { if (monacoLanguageIds.has(lang)) { monaco.languages.setTokensProvider(lang, { getInitialState() { return new TokenizerState(INITIAL); }, tokenize(line, state) { if (line.length >= tokenizeMaxLineLength) { return { endState: state, tokens: [{ startIndex: 0, scopes: "" }] }; } const grammar = highlighter.getLanguage(lang); const result = grammar.tokenizeLine2(line, state.ruleStack, tokenizeTimeLimit); if (result.stoppedEarly) console.warn(`Time limit reached when tokenizing line: ${line.substring(0, 100)}`); const tokensLength = result.tokens.length / 2; const tokens = []; for (let j = 0; j < tokensLength; j++) { const startIndex = result.tokens[2 * j]; const metadata = result.tokens[2 * j + 1]; const color = normalizeColor(colorMap[EncodedTokenMetadata.getForeground(metadata)] || ""); const scope = findScopeByColor(color) || ""; tokens.push({ startIndex, scopes: scope }); } return { endState: new TokenizerState(result.ruleStack), tokens }; } }); } } } class TokenizerState { constructor(_ruleStack) { this._ruleStack = _ruleStack; } get ruleStack() { return this._ruleStack; } clone() { return new TokenizerState(this._ruleStack); } equals(other) { if (!other || !(other instanceof TokenizerState) || other !== this || other._ruleStack !== this._ruleStack) { return false; } return true; } } function normalizeColor(color) { if (!color) return color; color = (color.charCodeAt(0) === 35 ? color.slice(1) : color).toLowerCase(); if (color.length === 3 || color.length === 4) color = color.split("").map((c) => c + c).join(""); return color; } export { shikiToMonaco, textmateThemeToMonacoTheme };