You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

VSCode中Markdown自定义标签预览实现方案咨询

实现VSCode自定义标签预览的实用方案

针对你想在VSCode里实现> [!NOTE]这类自定义标签的预览需求,我给你几个可落地的思路,不用完全排斥正则,也能实现精准的转换:

1. 基于markdown-it写自定义插件(最推荐)

虽然你觉得markdown-it依赖正则,但它的插件机制可以让你精准匹配并转换这类自定义块,而且比自己逐行解析更稳定。你可以写一个专门处理Admonition标签的插件,步骤如下:

  • 首先,利用markdown-it的block.ruler.push添加自定义块规则,匹配以> [!XXX]开头的块引用:

    module.exports = function customAdmonitionPlugin(md) {
      md.block.ruler.push('custom_admonition', function(state, startLine, endLine) {
        const start = state.bMarks[startLine] + state.tShift[startLine];
        const max = state.eMarks[startLine];
        const line = state.src.slice(start, max);
        
        // 匹配 > [!NOTE] 格式的行
        const match = line.match(/^> \[!([A-Z]+)\]/);
        if (!match) return false;
        
        const type = match[1].toLowerCase(); // 转为小写,比如note
        let content = [];
        let currentLine = startLine + 1;
        
        // 收集后续的 > 开头的内容行
        while (currentLine < endLine) {
          const curStart = state.bMarks[currentLine] + state.tShift[currentLine];
          const curMax = state.eMarks[currentLine];
          const curLine = state.src.slice(curStart, curMax);
          if (!curLine.startsWith('> ')) break;
          // 去掉开头的 > ,保留内容
          content.push(curLine.slice(2));
          currentLine++;
        }
        
        // 生成对应的HTML(注意:svg symbol建议只在页面头部插入一次,避免重复)
        const html = `<div class="adm-block adm-${type} "> <div class="adm-heading"> <svg class="adm-icon"> <use xlink:href="#adm-${type}"/></svg><span></span> </div> <div class="adm-body"> <p>${content.join(' ')}</p> </div></div>`;
        
        // 把解析后的HTML插入到state里
        state.push({
          type: 'html_block',
          content: html,
          level: state.level,
          lines: [startLine, currentLine],
          children: []
        });
        
        return currentLine;
      });
    };
    
  • 然后在VSCode扩展里注册这个插件:

    function activate(context) {
      const mdExtension = vscode.extensions.getExtension('vscode.markdown-language-features');
      if (mdExtension) {
        mdExtension.exports.extendMarkdownIt((md) => {
          // 先插入全局的svg symbols
          md.renderer.rules.html_block = function(tokens, idx, options, env, self) {
            const svgSymbols = `<svg xmlns="http://www.w3.org/2000/svg" class="adm-hidden"> <symbol id="adm-info"> <svg enable-background="new 0 0 24 24" xmlns="http://www.w3.org/2000/svg"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m20.5 11.8c0 4.5-3.7 8.2-8.2 8.2s-8.2-3.7-8.2-8.2 3.7-8.2 8.2-8.2 8.2 3.7 8.2 8.2zm-6.7 4c-.1-.1-.2-.2-.2-.3v-4.5c0-.1.1-.2.2-.4s.2-.3.2-.3 0-.1-.1-.1c-.1-.1-.5-.2-1.2-.4-.7-.1-1.2-.2-1.5-.2-.5 0-.7 0-.7.1s.1.3.2.6v4.7c0 .3-.1.5-.2.6-.2.2-.2.3-.2.3.1.1.5.3 1.2.4.6.1 1.2.2 1.6.2h.3c.4-.1.5-.2.5-.5.1 0 .1-.1-.1-.2zm-.9-8.6c-.7-.1-1.2-.2-1.5-.2-.2 0-.5.2-.7.4s-.3.4-.3.6c0 .1 0 .1.1.2s.6.2 1.5.4c1 .3 1.5.4 1.6.3.1 0 .2-.2.3-.5s.2-.5.2-.6v-.1c-.1-.1-.5-.3-1.2-.5z" fill="currentColor"/></svg> </symbol></svg>`;
            // 只在第一个html块前插入一次
            if (idx === 0) return svgSymbols + self.renderToken(tokens, idx, options);
            return self.renderToken(tokens, idx, options);
          };
          return md.use(require('./customAdmonitionPlugin'));
        });
      }
    }
    

2. 逐行解析的优化思路

如果你坚持用逐行解析,也可以结合VSCode的Markdown预览API来实现:

  • 先把整个Markdown内容按行拆分,遍历每一行,识别出> [!XXX]的起始行。

  • 然后收集后续所有以> 开头的行,作为这个标签的内容,直到遇到非> 开头的行或者文件结束。

  • 把这个块转换成你需要的HTML,剩下的内容交给标准的Markdown引擎处理。

  • 最后把转换后的HTML和处理后的普通Markdown内容拼接起来,返回给VSCode预览。

    这个方法的缺点是需要处理更多的边界情况(比如嵌套引用、换行等),不如基于markdown-it的插件稳定。

3. 扩展现有Admonition插件

如果不想从零写插件,可以用现成的markdown-it-admonition插件,然后自定义它的渲染模板,让它生成你需要的HTML结构。比如配置插件的render选项,替换默认的输出为你带svg的div结构。

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

火山引擎 最新活动