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




