如何在 monaco-editor 中使用 VSC 主题?

问题描述

据我所知,monaco-editor 和 VSCode 使用两种不同的格式来定义主题

似乎早期版本的 VSC 主要使用 tmTheme definition format,它允许使用 this tool 转换主题(另见 GitHub issue from 2017)。但是,由于 VSC 现在使用一种新格式来定义其主题,我想知道是否有一种(简单的)方法可以在 Monaco 编辑器中使用现有的 VSC 主题

非常感谢您的帮助! :)

附言这 GitHub issue comment from 2019 似乎表明确实没有简单的方法可以做到这一点,但希望从那时起情况有所改变。 ?

解决方法

我创建了一个小的 POC 存储库来演示它是如何工作的。它基于 monaco-vscode-textmate-theme-converter 插件。您可以找到 repo here,我发布了一个工具来实际转换主题 here。请注意,由于一些加载问题,您可能需要在开始之前对演示应用进行硬刷新。

,

主题扩展中仍然支持 tmTheme 格式并在导入时进行转换(从我记得的内容)。然而,扩展中主题的主要定义是使用dark_plus.json 中所示的方法。

将 json 格式转换为 monaco-editor 期望的结构非常容易:

export interface Colors { [key: string]: string }
export interface ITokenEntry {
    name?: string;
    scope: string[] | string;
    settings: {
        foreground?: string;
        background?: string;
        fontStyle?: string;
    };
}

// This is the structure of a vscode theme file.
export interface IThemeObject {
    name: string;
    type?: string;
    include?: string;
    colors?: Colors;

    settings?: ITokenEntry[];    // Old style specification.
    tokenColors?: ITokenEntry[]; // This is how it should be done now.
}

这些接口描述了json文件的格式。较旧的主题定义使用 settings 成员来定义主题颜色。在这种情况下,只需将 tokenColors 成员设置为 settings 成员并继续。

加载主题后,您可以使用此静态方法将其加载到 monaco-editor 中:

    /**
     * Updates the theme used by all code editor instances.
     *
     * @param theme The theme name.
     * @param type The base type of the theme.
     * @param values The actual theme values.
     */
    public static updateTheme(theme: string,type: "light" | "dark",values: IThemeObject): void {
        // Convert all color values to CSS hex form.
        const entries: { [key: string]: string } = {};
        for (const [key,value] of Object.entries(values.colors || {})) {
            entries[key] = colorToHex(value) || "";
        }

        const tokenRules: Monaco.ITokenThemeRule[] = [];
        (values.tokenColors || []).forEach((value: ITokenEntry): void => {
            const scopeValue = value.scope || [];
            const scopes = Array.isArray(scopeValue) ? scopeValue : scopeValue.split(",");
            scopes.forEach((scope: string): void => {
                tokenRules.push({
                    token: scope,foreground: colorToHex(value.settings.foreground),background: colorToHex(value.settings.background),fontStyle: value.settings.fontStyle,});
            });
        });

        CodeEditor.currentThemeId = theme.replace(/[^a-zA-Z]+/g,"-");
        Monaco.defineTheme(CodeEditor.currentThemeId,{
            base: type === "light" ? "vs" : "vs-dark",inherit: true,rules: tokenRules,colors: entries,});

        Monaco.setTheme(CodeEditor.currentThemeId);
    }

CodeEditor 是我包装 monaco-editor 的 TS 类。函数 colorToHex 定义为:

import Color from "color";

/**
 * Converts a color string or a color to a hex string.
 *
 * @param color The value to convert.
 *
 * @returns A hex string of the given color,including the alpha value.
 */
export const colorToHex = (color: string | Color | undefined): string | undefined => {
    if (!color) {
        return;
    }

    if (typeof color === "string") {
        color = new Color(color);
    }

    // Hex color values have no alpha component,so we have to add that explicitly.
    if (color.alpha() < 1) {
        let alpha = Math.round((color.alpha() * 255)).toString(16);
        if (alpha.length < 2) {
            alpha = "0" + alpha;
        }

        return color.hex() + alpha;
    } else {
        return color.hex();
    }
};