多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

为 @pierre/diffs 注册自定义 Shiki 语言与主题:完整实战指南

为 @pierre/diffs 注册自定义 Shiki 语言与主题:完整实战指南 【免费下载链接】pierrepierre’s open source code项目地址https://gitcode.com/gh_mirrors/pi/pierre点击查看免费下载pierre/diffs是 pierre 仓库中负责文件、Diff、补丁与 CodeView 评审界面渲染的代码包其语法高亮基于 Shiki 引擎。当内置语言清单无法覆盖你的业务文件格式或内置主题不符合产品视觉规范时本指南将教你通过registerCustomLanguage、registerCustomTheme与registerCustomCSSVariableTheme三个 API在任何 surfaceFile、FileDiff、CodeView及其 React 封装首次渲染之前完成自定义语言与主题的注册并掌握懒加载、扩展名映射、light/dark 双主题切换等实战要点。何时需要自定义注册pierre/diffs内置了 Shiki 的全部捆绑语言与 pierre 主题如pierre-dark、pierre-light及其变体但在以下场景中你必须显式注册自定义语言团队内部 DSL、专有配置文件或 Shiki 未收录的语法例如某类模板引擎、领域专用语言自定义主题需要与产品设计系统对齐的配色或希望用一组 CSS 变量动态驱动高亮颜色懒加载优化不希望把整份语法/主题打包进首屏 bundle而是通过动态import()按需加载。注册的时机非常关键必须在任何 surface第一次使用该语言或主题之前完成注册。例如在 React 应用中注册代码应放在组件模块顶层模块副作用或者放在应用入口、register辅助模块中确保先于渲染逻辑执行。仓库提供的 skill 文档 recipe-custom-highlighting.md 给出的最小示例是import { registerCustomLanguage, registerCustomTheme } from pierre/diffs; registerCustomLanguage( my-language, () import(./my-language.tmLanguage.json), [myext] ); registerCustomTheme(my-theme, () import(./my-theme.json));注册完成后把file.lang设为自定义语言名、options.theme设为自定义主题名即可生效。若主题颜色由 CSS 变量提供则改用registerCustomCSSVariableTheme。注册自定义语言registerCustomLanguageregisterCustomLanguage的完整签名如下见 registerCustomLanguage.tsfunction registerCustomLanguage( lang: string, loader: DynamicImportLanguageRegistration, extensionsOrFilenames: string[] [] ): void三个参数的含义与约束参数说明lang自定义语言名。不能使用text与ansi这两个是保留名注册时会直接抛出Error重复注册同一名称不会覆盖而是打印console.error后静默返回。loader懒加载器返回一个 Promise解析出 Shiki 语法注册对象TextMate grammar如.tmLanguage.json。extensionsOrFilenames文件扩展名或精确文件名到该语言的映射默认为空数组。扩展名/文件名映射规则第三个参数支持三类写法源码注释明确说明见 getFiletypeFromFileName.ts精确文件名如Dockerfile、CMakeLists.txt——解析时也会对仓库相对路径回退到 basename 匹配因此docker/Dockerfile与顶层Dockerfile等价getFiletypeFromFileName.ts不带点的扩展名如proto、foo对应.proto、.foo文件复合扩展名如blade.php、component.ts匹配引擎会先尝试复合扩展再回退到简单扩展。扩展名映射写入内部CUSTOM_EXTENSION_TO_FILE_FORMAT表并递增版本号当该扩展已有映射时setCustomExtension会打印警告并覆盖旧映射。这样文件名的语言推断getFiletypeFromFileName即可自动把.myext文件识别为自定义语言无需在每个文件对象上手动设置lang。名称必须与 grammar 匹配注册 loader 并不等于自动添加语法别名。源码注释特别强调注册的名称必须与返回的 grammar 的name或aliases之一匹配registerCustomLanguage.ts。测试 languageAttachment.test.ts 验证了这一点若 loader 返回的 grammar 不声明该名称attachResolvedLanguages会抛出No returned grammar declares tf as its name or an alias.且语言不会附加到高亮器若需要别名应在 grammar 对象上主动追加例如将hclgrammar 复制一份并扩展aliases数组加入custom-hcl若同名 grammar 已加载但缺少所需别名会报hcl is already loaded without alias custom-hcl之类的错误此时应给 grammar 一个唯一名称或先加载别名。注册自定义主题registerCustomThemeregisterCustomTheme注册一个懒加载的 Shiki 主题见 registerCustomTheme.tsexport type CustomThemeLoader ThemeLoader ThemeRegistration | ThemeRegistrationResolved ; function registerCustomTheme( themeName: string, loader: CustomThemeLoader ): void实现要点loader 被pierre/theming的createTheme包装其结果会经过 Shiki 的normalizeTheme规范化后再缓存因此fg/bg等值会从colors映射推导保持与内置主题一致的行为重复注册同名主题不会抛错内部把泛型 resolver 的DuplicateThemeError转换为“打印console.error并返回”的形状与语言注册的语义保持一致主题名采用字符串形式DiffsThemeNames类型定义为BundledTheme | (string {})types.ts因此任何注册过的自定义主题名都能直接用于options.theme无需类型扩展。测试 CodeView.highlighterReady.test.ts 展示了两个与主题加载相关的可靠行为加载失败的 chunk如Theme chunk failed to load可以在后续渲染中重试若用户把theme从旧主题切到新主题过时的主题加载完成后不会触发多余渲染只有当前主题 resolve 后视图才真正输出——这是懒加载与并发切换场景下的正确性保证。CSS 变量主题registerCustomCSSVariableTheme当主题颜色由 CSS 变量提供例如跟随宿主应用的主题系统、实现运行时换肤时使用registerCustomCSSVariableTheme见 registerCustomCSSVariableTheme.tsfunction registerCustomCSSVariableTheme( name: string, variableDefaults: Recordstring, string, fontStyle: boolean false ): void内部基于 Shiki 的createCssVariablesTheme创建主题变量前缀固定为formatCSSVariablePrefix(global)的结果--diffs-formatCSSVariablePrefix.tsvariableDefaults为每个颜色键提供默认值当对应 CSS 变量未定义时兜底使用fontStyle控制是否输出字体样式斜体等默认false。典型用法先注册再在样式表里定义变量并切换实现主题跟随import { registerCustomCSSVariableTheme } from pierre/diffs; registerCustomCSSVariableTheme(app-theme, { editor.background: #ffffff, editor.foreground: #1f2328, // 其余颜色键按需补充前缀为 --diffs- }); // 应用样式:root 定义 --diffs-editor.background 等变量即可控制高亮颜色注册完成后options.theme: app-theme即可使用该 CSS 变量主题。底层createCssVariablesTheme与codeToHtml也作为 passthrough API 从包入口直接导出见 api-highlighting.md 的 Shiki passthrough 一节。在渲染中使用自定义语言与主题通过 file.lang 指定语言FileContents类型中name用于显示与语言推断lang用于显式指定高亮语言types.tsinterface FileContents { name: string; // 文件名显示 语言推断 contents: string; lang?: SupportedLanguages; // 显式覆盖推断结果 header?: string; cacheKey?: string; }lang支持你注册的自定义语言名。对已构造的文件对象也可以使用便捷函数setLanguageOverride(fileOrDiff, lang)它返回一个浅拷贝的新对象setLanguageOverride.ts避免就地修改import { setLanguageOverride } from pierre/diffs; const file { name: schema.myext, contents: ... }; const highlighted setLanguageOverride(file, my-language);通过 options.theme 指定主题主题可以是单个名称也可以是 light/dark 配对ThemesType Recorddark | light, DiffsThemeNames见 types.ts从而支持自动切换const options { theme: { light: my-theme, dark: my-theme-dark }, };CodeView的默认选项为{ theme: DEFAULT_THEMES }CodeView.ts即默认使用内置主题自定义注册后即可替换为你的主题名。getHighlighterOptionsgetHighlighterOptions.ts会把theme展开为需要加载的主题名列表并据此驱动共享高亮器加载。懒加载、预加载与资源回收注册只是登记了 loader真正的加载发生在共享高亮器被创建或使用时。getSharedHighlightershared_highlighter.ts是贯穿所有渲染的单例缓存每个线程主线程与 worker 线程默认只实例化一个 Shiki highlighter所有语法高亮共享它。它会遍历请求的langs与themes把已解析的注册直接附加、未解析的通过Promise.all并行加载后再附加。与之配套的 API完整清单见 api-highlighting.mdpreloadHighlighter(options)在渲染前主动加载共享高亮器及其语言/主题避免首帧等待shared_highlighter.tsgetHighlighterIfLoaded(props?)仅在指定语言/主题已附加时才返回实例否则返回undefinedresolveLanguage(s)/resolveTheme(s)显式解析并缓存语言/主题注册disposeHighlighter()释放高亮器并清理语言/主题的解析缓存与附加集合shared_highlighter.ts。preferredHighlighter可选shiki-js默认使用 JavaScript 正则引擎或shiki-wasm使用 Oniguruma WASM 引擎见 shared_highlighter.ts。当使用 worker 池时注册代码需要确保 worker 上下文也能拿到同样的 loaderisWorkerContext可用于区分运行环境。完整示例React 应用接入自定义语言与主题将以上 API 组合进一个 React 应用// register.ts —— 在渲染任何 surface 之前导入一次 import { registerCustomLanguage, registerCustomTheme, } from pierre/diffs; // 1. 注册自定义语言懒加载 grammar 扩展名映射 registerCustomLanguage( my-language, () import(./my-language.tmLanguage.json), [myext, MyFile.txt] ); // 2. 注册自定义主题懒加载 JSON 主题 registerCustomTheme(my-theme, () import(./my-theme.json)); // 3. 注册 CSS 变量主题运行时换肤 registerCustomCSSVariableTheme(css-theme, { editor.background: #f6f8fa, editor.foreground: #24292f, });// FileView.tsx import { useEffect, useState } from react; import { File } from pierre/diffs/react; import ./register; // 确保注册先于渲染 export function FileView() { const [file, setFile] useState({ name: config.myext, contents: key value\n, lang: my-language, // 显式指定自定义语言 }); useEffect(() { // 渲染前预热共享高亮器避免首帧空白 void preloadHighlighter({ themes: [my-theme], langs: [my-language], }); }, []); return ( File file{file} options{{ theme: my-theme }} // 或 { light: a, dark: b } / ); }对于虚拟化评审界面CodeView及其 React 封装使用同样的theme选项当主题加载尚未完成时CodeView会等待高亮器就绪后再渲染条目过时主题的加载结果不会触发多余渲染测试 CodeView.highlighterReady.test.ts 验证。仓库中的演示应用 apps/demo 可作为 CodeView 基础用法的运行参考。常见错误与调试提示现象原因与处理注册text/ansi抛错这两个是保留语言名换用其他名称registerCustomLanguage.ts。同名语言/主题重复注册不覆盖仅打印console.error若想“更新”请先通过disposeHighlighter清理或使用不同名称。No returned grammar declares X as its name or an alias注册名与 grammar 的name/aliases不匹配在 grammar 上补充aliaseslanguageAttachment.test.ts。自定义扩展名没被识别确认注册时传入的extensionsOrFilenames写法正确文件名精确匹配、扩展名不带点、复合扩展如blade.phpgetFiletypeFromFileName.ts。主题切换后渲染异常确保新旧主题都已注册并发加载场景下CodeView只渲染当前主题的条目。CSS 变量主题不生效变量名必须以--diffs-为前缀并在variableDefaults中为每个键提供兜底值。更完整的 API 清单可继续阅读 api-highlighting.md语言/主题/共享高亮器/流式 API 全表以及 SKILL.md包安装与各场景入口指引。赞分享【免费下载链接】pierrepierre’s open source code项目地址https://gitcode.com/gh_mirrors/pi/pierre点击查看免费下载相关推荐pierre/diffs Highlighting API 完全指南语言、主题、共享 Highlighter 与流式高亮pierre/diffs Highlighting API 完全指南语言、主题、共享 Highlighter 与流式高亮 导读 pierre/diffs Pierre Diffs React API 完全指南pierre/diffs/react 组件、Hooks 与 Provider 实战解析Pierre Diffs React API 完全指南 pierre/diffs/react 组件、Hooks 与 Provider 实战解析 pierrShiki 自定义主题加载完全指南从 TextMate 主题对象到 loadTheme 动态注册Shiki 自定义主题加载完全指南从 TextMate 主题对象到 loadTheme 动态注册 本文围绕 Shiki本项目所在仓库的 加载自定义主题指南前端开发工具上一篇AG-UI单例模式终极指南全局服务管理的10个核心技术下一篇nunif开源AI工具如何重塑2D视频转3D立体视觉体验创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表