Svelte中Shiki插件行高亮与代码块标题兼容问题修复
修复Shiki代码块行高亮与标题共存问题
原代码的核心问题是当meta中同时包含行高亮规则和代码标题时,解析逻辑会误将整个meta判定为标题,清空meta导致行高亮失效。以下是修复后的完整代码:
import { parse } from 'node-html-parser'; import { getHighlighter } from 'shiki'; const THEME = 'github-dark'; /** * 返回将大括号和反引号替换为HTML实体的代码 * @param {string} code - 待转义的代码 * @returns {string} - 转义后的HTML */ function escapeHtml(code) { return code.replace( /[{}`]/g, (character) => ({ '{': '{', '}': '}', '`': '`' })[character], ); } /** * 返回要高亮的行号数组 * @param {string} rangeString - 待解析的范围字符串(例如 {1,3-5,8}) * @returns {number[]} */ function rangeParser(rangeString) { const result = []; const ranges = rangeString.split(','); ranges.forEach((element) => { if (element.indexOf('-') === -1) { const num = parseInt(element.trim(), 10); if (!isNaN(num)) result.push(num); } else { const limits = element.split('-').map(s => parseInt(s.trim(), 10)); const start = limits[0]; const end = limits[1]; if (!isNaN(start) && !isNaN(end)) { for (let i = start; i <= end; i += 1) { result.push(i); } } } }); return result; } /** * @param html {string} - 待处理的HTML * @returns {string} - 可聚焦的HTML */ function makeFocussable(html) { const root = parse(html); const pre = root.querySelector('pre'); if (pre) pre.setAttribute('tabIndex', '0'); return root.toString(); } /** * @param code {string} - 待高亮的代码 * @param lang {string} - 代码语言 * @param meta {string} - 代码元数据 * @returns {string} - 已高亮的HTML */ async function highlighter(code, lang, meta) { const shikiHighlighter = await getHighlighter({ theme: THEME, }); let html; let blockName = ''; let highlightMeta = ''; // 先解析代码标题(括号包裹的部分) if (meta) { const titleMatch = meta.match(/\((.*?)\)$/); if (titleMatch) { blockName = titleMatch[1].trim(); // 移除标题部分,保留剩余的高亮规则 meta = meta.replace(titleMatch[0], '').trim(); } // 解析行高亮规则 const highlightMatch = meta.match(/\{([\d,-]+)\}/); if (highlightMatch) { highlightMeta = highlightMatch[1]; } } // 生成高亮HTML const shikiOptions = { lang }; if (highlightMeta) { const highlightLines = rangeParser(highlightMeta); shikiOptions.lineOptions = highlightLines.map(line => ({ line, classes: ['highlight-line'], })); } html = shikiHighlighter.codeToHtml(code, shikiOptions); html = makeFocussable(html); // 添加代码标题 if (blockName) { html = `<div class="code-block-title">${blockName}</div>${html}`; } return escapeHtml(html); } export default highlighter;
关键修改点说明:
- 重构meta解析逻辑:先匹配末尾的括号标题,提取后从meta中移除,再解析剩余的行高亮规则,确保两者都能被识别
- 修复escapeHtml函数:正则添加反引号`,并修正替换对象的键值,正确转义反引号
- 增强rangeParser鲁棒性:添加空值和非数字判断,避免无效行号导致错误
- 优化代码结构:将Shiki配置项单独提取,逻辑更清晰,移除原代码中无用的
blockName冗余语句 - 兼容多种meta组合:支持仅标题、仅高亮、两者共存三种场景
内容的提问来源于stack exchange,提问作者Лео
相关产品推荐
相关产品推荐

