You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Monarch为Markdown实现自定义语法高亮规则?

Monaco + Monarch 实现Markdown自定义语法高亮方案

一、修改Monarch配置添加自定义规则

Monarch的语法规则按顺序匹配,需将自定义规则放在对应匹配组的最前面,避免被其他规则优先覆盖。

1. 匹配||开头的行

在tokenizer.root数组顶部添加规则,优先识别以||开头的整行内容:

// 匹配以||开头的行
[/^(\s*)||(.*)$/, ['white', 'custom-special-line', 'custom-special-line']],

2. 匹配自定义关键词MyKeyword1/MyKeyword2

在tokenizer.linecontent数组顶部添加关键词匹配规则,确保在粗体、斜体等格式规则之前生效:

// 匹配自定义关键词
[/\b(MyKeyword1|MyKeyword2)\b/, 'custom-keyword'],

修改后的完整Monarch配置

// Difficulty: "Ultra-Violence"
// Language definition for Markdown
// Quite complex definition mostly due to almost full inclusion
// of the HTML mode (so we can properly match nested HTML tag definitions)
return {
    defaultToken: '',
    tokenPostfix: '.md',

    // escape codes
    control: /[\`*_\[\]{}()#+\-\.!]/,
    noncontrol: /[^\`*_\[\]{}()#+\-\.!]/,
    escapes: /\\(?:@control)/,

    // escape codes for javascript/CSS strings
    jsescapes: /\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,

    // non matched elements
    empty: [
        'area', 'base', 'basefont', 'br', 'col', 'frame',
        'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'
    ],

    tokenizer: {
        root: [
            // 新增:匹配以||开头的行
            [/^(\s*)||(.*)$/, ['white', 'custom-special-line', 'custom-special-line']],

            // headers (with #)
            [/^(\s{0,3})(#+)((?:[^\\#]|@escapes)+)((?:#+)?)/, ['white', 'keyword', 'keyword', 'keyword']],

            // headers (with =)
            [/^\s*(=+|\-+)\s*$/, 'keyword'],

            // headers (with ***)
            [/^\s*((\*[ ]?)+)\s*$/, 'meta.separator'],

            // quote
            [/^\s*>+/, 'comment'],

            // list (starting with * or number)
            [/^\s*([\*\-+:]|\d+\.)\s/, 'keyword'],

            // code block (4 spaces indent)
            [/^(\t|[ ]{4})[^ ].*$/, 'string'],

            // code block (3 tilde)
            [/^\s*~~~\s*((?:\w|[\/\-#])+)?\s*$/, { token: 'string', next: '@codeblock' }],

            // github style code blocks (with backticks and language)
            [/^\s*```\s*((?:\w|[\/\-#])+)\s*$/, { token: 'string', next: '@codeblockgh', nextEmbedded: '$1' }],

            // github style code blocks (with backticks but no language)
            [/^\s*```\s*$/, { token: 'string', next: '@codeblock' }],

            // markup within lines
            { include: '@linecontent' },
        ],

        codeblock: [
            [/^\s*~~~\s*$/, { token: 'string', next: '@pop' }],
            [/^\s*```\s*$/, { token: 'string', next: '@pop' }],
            [/.*$/, 'variable.source'],
        ],

        // github style code blocks
        codeblockgh: [
            [/```\s*$/, { token: 'variable.source', next: '@pop', nextEmbedded: '@pop' }],
            [/[^`]+/, 'variable.source'],
        ],

        linecontent: [
            // 新增:匹配自定义关键词
            [/\b(MyKeyword1|MyKeyword2)\b/, 'custom-keyword'],

            // escapes
            [/&\w+;/, 'string.escape'],
            [/@escapes/, 'escape'],

            // various markup
            [/\b__([^\\_]|@escapes|_(?!_))+__\b/, 'strong'],
            [/\*\*([^\\*]|@escapes|\*(?!\*))+\*\*/, 'strong'],
            [/\b_[^_]+_\b/, 'emphasis'],
            [/\*([^\\*]|@escapes)+\*/, 'emphasis'],
            [/`([^\\`]|@escapes)+`/, 'variable'],

            // links
            [/\{+[^}]+\}+/, 'string.target'],
            [/(!?\[)((?:[^\]\\]|@escapes)*)(\]\([^\)]+\))/, ['string.link', '', 'string.link']],
            [/(!?\[)((?:[^\]\\]|@escapes)*)(\])/, 'string.link'],

            // or html
            { include: 'html' },
        ],

        // Note: it is tempting to rather switch to the real HTML mode instead of building our own here
        // but currently there is a limitation in Monarch that prevents us from doing it: The opening
        // '<' would start the HTML mode, however there is no way to jump 1 character back to let the
        // HTML mode also tokenize the opening angle bracket. Thus, even though we could jump to HTML,
        // we cannot correctly tokenize it in that mode yet.
        html: [
            // html tags
            [/<(\w+)\/>/, 'tag'],
            [/<(\w+)/, {
                cases: {
                    '@empty': { token: 'tag', next: '@tag.$1' },
                    '@default': { token: 'tag', next: '@tag.$1' }
                }
            }],
            [/<\/(\w+)\s*>/, { token: 'tag' }],

            [/<!--/, 'comment', '@comment']
        ],

        comment: [
            [/[^<\-]+/, 'comment.content'],
            [/-->/, 'comment', '@pop'],
            [/<!--/, 'comment.content.invalid'],
            [/[<\-]/, 'comment.content']
        ],

        // Almost full HTML tag matching, complete with embedded scripts & styles
        tag: [
            [/[ \t\r\n]+/, 'white'],
            [/(type)(\s*=\s*)(")([^"]+)(")/, ['attribute.name.html', 'delimiter.html', 'string.html',
                { token: 'string.html', switchTo: '@tag.$S2.$4' },
                'string.html']],
            [/(type)(\s*=\s*)(')([^']+)(')/, ['attribute.name.html', 'delimiter.html', 'string.html',
                { token: 'string.html', switchTo: '@tag.$S2.$4' },
                'string.html']],
            [/(\w+)(\s*=\s*)("[^"]*"|'[^']*')/, ['attribute.name.html', 'delimiter.html', 'string.html']],
            [/\w+/, 'attribute.name.html'],
            [/\/>/, 'tag', '@pop'],
            [/>/, {
                cases: {
                    '$S2==style': { token: 'tag', switchTo: 'embeddedStyle', nextEmbedded: 'text/css' },
                    '$S2==script': {
                        cases: {
                            '$S3': { token: 'tag', switchTo: 'embeddedScript', nextEmbedded: '$S3' },
                            '@default': { token: 'tag', switchTo: 'embeddedScript', nextEmbedded: 'text/javascript' }
                        }
                    },
                    '@default': { token: 'tag', next: '@pop' }
                }
            }],
        ],

        embeddedStyle: [
            [/[^<]+/, ''],
            [/<\/style\s*>/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }],
            [/</, '']
        ],

        embeddedScript: [
            [/[^<]+/, ''],
            [/<\/script\s*>/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }],
            [/</, '']
        ],
    }
};

二、设置自定义颜色与CSS变量

Monarch支持通过自定义主题关联token与颜色,也可直接使用CSS变量实现动态颜色配置。

1. 自定义主题映射token颜色

通过monaco.editor.defineTheme创建或修改主题,添加自定义token的样式配置:

monaco.editor.defineTheme('custom-markdown-theme', {
    base: 'vs', // 基于现有主题(可选vs/vs-dark/hc-black)
    inherit: true, // 继承基础主题的其他样式
    rules: [
        { token: 'custom-special-line', foreground: '#ff4757', fontStyle: 'italic' },
        // 使用CSS变量
        { token: 'custom-keyword', foreground: 'var(--custom-keyword-color)', fontStyle: 'bold' }
    ],
    colors: {
        // 可选:配置编辑器其他区域颜色
        'editor.background': '#f8f9fa'
    }
});

// 应用自定义主题
monaco.editor.create(document.getElementById('container'), {
    value: '你的Markdown内容',
    language: 'markdown',
    theme: 'custom-markdown-theme'
});

2. 定义CSS变量

在页面CSS中定义对应变量即可生效:

:root {
    --custom-keyword-color: #2ed573;
}

关键注意事项

  • 规则顺序:Monarch按数组顺序匹配,自定义规则必须放在可能覆盖它的规则前面(比如||行规则要放在标题、列表等行规则之前)
  • 关键词匹配:使用\b确保匹配完整单词,避免部分匹配(如不会匹配MyKeyword1abc)
  • CSS变量支持:Monarch完全兼容CSS变量,主题中使用var(...)格式即可引用

内容的提问来源于stack exchange,提问作者darkhorse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 22:29:55