十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PowerToys 中的 Monaco Editor 集成:从 WebView2 嵌入、语言定制到版本更新与安装包维护

PowerToys 中的 Monaco Editor 集成:从 WebView2 嵌入、语言定制到版本更新与安装包维护 PowerToys 中的 Monaco Editor 集成从 WebView2 嵌入、语言定制到版本更新与安装包维护【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys本文以 PowerToys 仓库中doc/devdocs/common/monaco-editor.md文档为主体系统讲解 VS Code 同款编辑器 Monaco 在 PowerToys 中的集成方案它通过 WebView2 嵌入到 WinUI 3 应用中为 Registry Preview、文件预览File Preview和 Peek 提供带语法高亮的代码文件预览能力。读完本文你可以掌握 Monaco 资源的目录布局、自定义语言与文件扩展名的注册方式、monaco_languages.json的生成流程以及 Monaco 升级和安装包清单自动生成的完整操作路径。Monaco 在 PowerToys 中的角色与使用位置Monaco 是驱动 Visual Studio Code 的文本编辑器。在 PowerToys 中它作为一个组件被集成进来为桌面应用提供语法高亮、行号、智能编辑等高级文本展示能力。根据 Monaco Editor 文档Monaco 主要被用于以下三处Registry Preview 模块——用于预览和展示注册表文件.regFile Preview 预览处理器——在文件资源管理器中预览代码文件时提供语法高亮Peek 模块——用于文件内容的快速预览。从源码结构看这三处能力共享同一套运行时资源src/common/FilePreviewCommon 中的MonacoHelper类是预览各方的公共入口而实际渲染则发生在各模块内的 WebView2 控件中加载的 HTML 页面里。技术实现WebView2 虚拟主机与运行时资源定位Monaco 通过 WebView2 嵌入到 PowerToys 的 WinUI 3 应用中从而让桌面应用复用 Monaco 的 Web 端能力。理解这一集成需要掌握两个关键机制虚拟主机与资源目录定位。虚拟主机 PowerToysLocalMonacoMonacoHelper.cs 定义了常量VirtualHostName PowerToysLocalMonaco。WebView2 的SetVirtualHostNameToFolderMapping机制会把本地的 Monaco 资源目录映射为虚拟主机powertoyslocalmonaco前端页面因此可以像访问网站一样引用资源。这一点与 index.html 中的引用方式完全对应script srchttp://[[PT_URL]]/monacoSRC/min/vs/loader.js/script script srchttp://[[PT_URL]]/monacoSpecialLanguages.js typemodule/script其中[[PT_URL]]、[[PT_THEME]]、[[PT_CODE]]等双中括号占位符会在运行时由托管代码替换为实际值。从 index.html 的注释可以看到完整的参数约定占位符含义[[PT_CODE]]文件内容的 Base64 编码[[PT_THEME]]vs浅色或vs-dark深色[[PT_LANG]]文件对应的 Monaco 语言 id[[PT_WRAP]]是否自动换行[[PT_MINIMAP]]是否显示小地图[[PT_CONTEXTMENU]]是否启用 Monaco 内置右键菜单Peek 中因内置菜单不工作而禁用改用自定义菜单页面加载后的流程是Base64 解码得到代码文本 → 加载vs/editor/editor.main→ 调用registerAdditionalLanguages注册 PowerToys 的定制语言 → 基于customTokenThemeRules定义一个继承系统主题的自定义主题 → 以readOnly: true创建编辑器。此外页面还注册了Toggle text wrapping、Toggle minimap等自定义右键菜单动作并通过遍历MenuRegistry._menuItems移除剪切、格式化等不适用于只读预览的菜单项见 index.html。运行时目录定位与语言映射MonacoHelper通过 GetRuntimeMonacoDirectory 定位运行时资源在可执行文件目录下查找Assets/Monaco若可执行文件位于WinUI3Apps子目录会先纠正路径找不到则抛出DirectoryNotFoundException。扩展名到语言的映射由monaco_languages.json驱动。GetLanguage 会把扩展名转为小写后遍历 JSON 中list数组各项的extensions字段做精确匹配命中则返回对应语言id未命中或发生异常时回退到plaintext。值得注意的是同一份 monaco_languages.json 不仅被运行时的MonacoHelper使用还被安装程序引用以注册预览处理器的文件扩展名——这正是后文更新 Monaco 后必须重新生成该文件的原因。源码目录结构与关键文件Monaco 相关源文件统一位于 src/Monaco 目录核心结构如下monacoSRC/min/vs/从 monaco-editor npm 包中保留下来的最小化minified编辑器主程序含editor/编辑器主程序、basic-languages/各语言 Monarch 定义、language/CSS/HTML/JSON/TS 智能服务和入口loader.jscustomLanguages/PowerToys 自行编写的语言定义当前包含 reg.js注册表文件、gitignore.js、srt.js字幕文件monacoSpecialLanguages.js把自定义语言与已有语言的新扩展名注册到 Monaco 的入口模块customTokenThemeRules.js自定义 token 的配色规则generateLanguagesJson.html在浏览器中重新生成monaco_languages.json的工具页index.htmlWebView2 加载的预览页面模板。各模块如 Registry Preview、File Preview 处理器则把这套文件作为应用资源打包分发运行时由MonacoHelper从Assets/Monaco读取。语言注册机制monacoSpecialLanguages.js 深入解读monacoSpecialLanguages.js 是理解 PowerToys 定制机制的核心文件它对外暴露异步函数registerAdditionalLanguages(monaco)内部分两类操作1. 为已有语言追加扩展名——registerAdditionalLanguage(id, extensions, originalId, monaco)// 摘自 monacoSpecialLanguages.js 的 registerAdditionalLanguages registerAdditionalLanguage(cppExt, [.ino, .pde], cpp, monaco); registerAdditionalLanguage(xmlExt, [.wsdl, .projitems, .csproj, .fsproj, .shproj, .vcxproj, .vbproj, .resx, .resw], xml, monaco); registerAdditionalLanguage(txtExt, [.sln, .log, .vsconfig, .env, .ahk, .ion], txt, monaco); registerAdditionalLanguage(iniExt, [.inf, .gitconfig, .gitattributes, .editorconfig], ini, monaco);其实现是用新的id和扩展名数组调用monaco.languages.register然后从vs/basic-languages/originalId/originalId中取出原语言的conf编辑配置和 Monarch 词法定义分别通过setLanguageConfiguration和setMonarchTokensProvider挂到新 id 上。这样.ino/.pde获得与 C 完全相同的高亮.log/.sln等则获得与纯文本相同的处理——这正对应文档中若想让 LOG 文件像 TXT 一样被预览就把 LOG 加到 TXT 语言定义的示例。一个值得注意的细节originalId txt时直接return不为 txt 系语言加载 Monarch 定义纯文本无需词法分析。2. 注册全新语言——registerAdditionalNewLanguage(id, extensions, definition, monaco)registerAdditionalNewLanguage(reg, [.reg], regDefinition(), monaco); registerAdditionalNewLanguage(gitignore, [.gitignore], gitignoreDefinition(), monaco); registerAdditionalNewLanguage(srt, [.srt], srtDefinition(), monaco);definition是自定义文件中导出的 Monarch 词法定义。以 reg.js 为例它定义了.reg文件的完整 token 规则export function regDefinition() { return { tokenPostfix: .reg, tokenizer: { root: [ // Header (case-sensitive) [/Windows Registry Editor Version 5.00/, comment], [/REGEDIT4/, comment], // Keys [/\[\-.*\]/, invalid], [/\\.*[^\]]/, keyword], // Values [//, keyword], [/hex\({0,1}[0-9,a,b]\)|hex|dword(?\:)/, type], // Hive names (case in-sensitive) [/HKEY_CLASSES_ROOT/, type], [/HKEY_LOCAL_MACHINE/, type], // ...更多 hive 与符号规则 ] } } };可以看到它区分了头注释Windows Registry Editor Version 5.00、键路径keyword、默认值标记、数据类型hex(4)/dword:/hex(0)以及大小写不敏感的 hive 名HKEY_*使 Registry Preview 中对.reg的预览具备真正的语义高亮。另外文件中的languageDefinitions()函数把 cpp、xml、razor、vb、ini、shell 等语言的压缩源码原样内嵌为 AMDdefine模块——从源码结构看这是为了在虚拟主机环境下保证require(vs/basic-languages/...)能被同步解析避免加载时序问题。自定义 Monaco新增语言与扩展名操作指南以下内容整合自 FilePreviewCommon 文档即主文档指引的详细操作步骤并对照当前仓库源码给出落点。添加新的语言定义用 Monarch 下新建语言定义文件参照reg.js导出一个返回 Monarch 定义的函数例如export function idDefinition()。记住文件名和导出函数名后续要用在 monacoSpecialLanguages.js 顶部其他 import 之后添加import { idDefinition } from ./customLanguages/file.js;在registerAdditionalLanguages函数中调用registerAdditionalNewLanguage(id, [.fileExtension], idDefinition(), monaco)id可以是任意字符串推荐用某个文件扩展名如php、reg若需要为新 token 定制颜色在 customTokenThemeRules.js 中追加一条规则{token: token-name, foreground: ff0000}除foreground外还可指定background与fontStyle。注意这些规则作用于所有语言不应修改默认 token 的颜色而应为本语言创建专属的新 token。当前仓库中的实际示例是 gitignore 的反向匹配规则export const customTokenThemeRules [ {token: custom-negation.gitignore, foreground: c00ce0} ];最后执行下面monaco_languages.json的重新生成步骤让托管侧的安装与映射同步到新语言。为已有语言添加文件扩展名在registerAdditionalLanguages中添加registerAdditionalLanguage(id, [.fileExtension], existingId, monaco)其中existingId可在 monaco_languages.json 中查到例如给 php 追加扩展名时id 设为phpExt、existingId 设为php由于运行环境的模块加载需要把目标语言的既有 Monarch 定义复制到同文件的languageDefinitions函数中原始定义位于 monacoSRC/min/vs/basic-languages/ 对应子目录重新生成monaco_languages.json。重新生成 monaco_languages.jsonmonaco_languages.json包含 Monaco 支持的全部扩展名与语言 id 映射MonacoHelper类和安装程序都依赖它注册预览处理器因此每次更新 Monaco 或新增语言后都必须重新生成在本地 Web 服务器上运行 generateLanguagesJson.html浏览器直接以file://打开时会阻止所需功能例如可用 VS Code 的 Preview Server 扩展右键选择Launch on browser浏览器会自动下载新的monaco_languages.json用下载的文件替换源码目录中的旧文件。版本管理与 Monaco 更新流程查看当前版本monaco-editor.md 说明当前 Monaco 版本可在 loader.js 文件中名为versionMonaco的变量处找到。更新步骤更新 Monaco 需要执行以下四个步骤下载最新版本 Monaco执行npm i monaco-editor删除下载文件中除min文件夹最小化代码以外的所有内容把min文件夹复制/覆盖到项目的 src/Monaco/monacoSRC 目录按上文流程生成新的 Monaco 语言 JSON 文件用新文件覆盖现有的 monaco_languages.json。文档给出的耗时参考整个 Monaco 更新流程通常需要约30 分钟。此外由于更新类 PR 基本是整体替换 Monaco 源码 少量微调可以直接参考历史 Monaco 更新 PR 作为模板。安装程序中的 Monaco 清单自动生成Monaco 源文件数量庞大不可能在 WiX 安装配置中手工逐一罗列。文档提到这一工作由脚本Generate-Monaco-wxs.ps1完成自动为所有 Monaco 文件生成安装清单避免手工维护。当前仓库中对应实现是 installer/PowerToysSetupVNext/generateMonacoWxs.ps1其工作流程为定位 NuGet 包WixToolset.Heat提供的heat.exe按 x64/x86 平台选择路径找不到时快速失败对..\..\src\Monaco\monacoSRC目录执行目录热directory harvest $heatExe dir $SourceDir -out $OutputFile -cg $ComponentGroup -dr $DirectoryRef -var $Variable -gg -srd -nologo生成组件组MonacoSRCHeatGenerated输出MonacoSRC.wxs并用变量var.MonacoSRCHarvestPath参数化源路径后处理生成的 WXS为每个Component注入Software\Classes\powertoys\components下的注册表键值使卸载时能按组件清理追加RemoveMonacoSRCFolders组件为 heat 发现的每个目录生成RemoveFolder ... Onuninstall/条目保证卸载时目录被移除。这套机制正是文档所述简化 Monaco 在 PowerToys 内维护与更新的落地方式升级 Monaco 时只需替换monacoSRC内容并重新生成语言 JSON安装清单由 heat 自动重新收割无需人工增删文件条目。小结PowerToys 对 Monaco 的集成体现了一套可复用的Web 编辑器嵌入桌面应用工程范式WebView2 虚拟主机解决资源引用问题index.html占位符模板实现主题/内容/行为的运行时注入monacoSpecialLanguages.jscustomLanguages/提供轻量的语言扩展点monaco_languages.json作为唯一事实来源打通前端高亮与托管侧的扩展名注册heat 脚本则把百余个静态文件纳入安装包而免去手工维护。若你计划为 PowerToys 预览能力添加新语言、新扩展名或升级 Monaco 版本上述目录、注册函数与生成流程即为完整的操作依据。【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表