2026/9/24 18:31:12

vscode-copilot-chat 中的 Visualization Runner:为 `[visualizable]` 测试一键接入 VS Code 可视化调试

vscode-copilot-chat 中的 Visualization Runner:为 `[visualizable]` 测试一键接入 VS Code 可视化调试 人工智能AI 应用AI Agent代码智能体交互助手工具调用MCP Clients【免费下载链接】vscode-copilot-chatCopilot Chat extension for VS Code项目地址https://gitcode.com/gh_mirrors/vs/vscode-copilot-chat点击查看免费下载在微软官方 Copilot Chat 扩展仓库 vscode-copilot-chat 中开发团队内嵌了一个小巧但机制完整的开发者工具扩展Visualization Runnervisualization-runner。它以 Visualize Test 代码操作Code Action为入口自动识别测试名中带有[visualizable]标记的 vitest 用例并借助 VS Code 调试可视化机制Debug Value Editor把这些测试的结果以结构化属性的形式直观呈现出来。阅读本文后你将掌握该扩展的完整工作原理从 CodeLens 代码操作、TypeScript AST 测试发现到全局钩子$$debugValueEditor_run与 Vitest 模块拦截的运行链路并了解它在仓库中对应的实现与配套启动器文件。一、扩展概览一行 README 背后的完整链路官方 README.vscode/extensions/visualization-runner/README.md用两句话概括了扩展的全部职责This extension add Visualize Test code actions to tests that have[visualizable]in their name. It runs them usingfile://./../../../test/testVisualizationRunner.ts.翻译过来即该扩展为名称中包含[visualizable]的测试添加 Visualize Test 代码操作并借助test/testVisualizationRunner.ts来运行这些测试。虽然描述极简但仓库中实际承载它的文件却构成了一条完整、可独立运行的开发链路文件作用package.json扩展清单ms-vscode发布者、v0.1.0、入口entry.js、依赖ms-vscode.debug-value-editorentry.js激活入口通过tsx加载 TypeScript并开启hediet/node-reload热重载extension.ts核心逻辑CodeLens 注册、AST 测试发现、[visualizable]过滤test/testVisualizationRunner.tsPlayground 启动器拦截vitest模块、按路径定位测试并执行test/testVisualizationRunnerSTest.ts面向 stest 与 JSX 树可视化的另一套启动器变体src/util/common/debugValueEditorGlobals.ts定义$$debugValueEditor_*与$$playgroundRunner_*全局钩子接口从清单可见该扩展不是一个独立的测试框架而是测试发现 → 代码操作 → 调试通道 → 可视化运行的完整接线器其最终落点是对接仓库内的 Playground 启动器。二、扩展清单与激活机制2.1 package.json 关键字段.vscode/extensions/visualization-runner/package.json 中值得注意的配置publisher: ms-vscode、version: 0.1.0是仓库开发工作区内部使用的扩展engines.vscode: ^1.85.0要求 VS Code 1.85 及以上main: ./entry.js入口是编译后的 JavaScript 而不是 TS 源文件activationEvents: [onLanguage:typescript]仅在打开 TypeScript 语言文件时激活extensionDependencies: [ms-vscode.debug-value-editor]硬依赖Debug Value Editor 扩展——这正是 Visualize Test 命令debug-value-editor.debug-and-send-request的提供方。2.2 入口与热重载entry.js 承担激活引导require(tsx/cjs); const { enableHotReload, hotRequire } require(hediet/node-reload); enableHotReload({ entryModule: module }); function activate(context) { context.subscriptions.push(hotRequire(module, ./extension, ext new ext.Extension())); } module.exports { activate };它先加载tsx/cjs让 CommonJS 环境能够 require TypeScript 文件再调用hediet/node-reload的hotRequire加载./extension并在热重载时重建Extension实例。这意味着修改extension.ts后无需重启 VS Code 开发宿主即可生效大幅提升调试体验。三、核心实现如何发现并标记可可视化测试extension.ts 是整个扩展的核心逻辑分为三块测试发现缓存、CodeLens 提供器、[visualizable]过滤。3.1 基于 TypeScript AST 的测试发现扩展借助 TypeScript 编译器 API 对当前文档做纯文本级的语法解析不执行代码见getTests函数用ts.createSourceFile解析文档文本解析失败时静默返回空数组递归遍历 AST识别形如test(name, fn)的调用表达式parseTest同时识别describe(name, fn)与suite(name, fn)分组parseDescribeOrSuite并把分组名压入currentPath形成层级路径最终为每个test(...)生成{ path: string[], lineNumber }例如[describe 分组名, 测试名]。3.2 缓存与防抖_testCaches为减少高频解析开销扩展用仓库自带的工具类CachedFunction与LRUCachedFunction源自 src/util/vs/base/common/cache.ts按文件名缓存测试列表并以document.version作为缓存键private readonly _testCaches new CachedFunction((fileName: string) { let first true; return new LRUCachedFunction(async (modelVersion: number) { if (first) { first false; } else { await timeout(1000); } const document workspace.textDocuments.find(d d.fileName fileName); if (document?.version ! modelVersion) { return []; } const text document.getText(); if (!text.includes(test)) { return []; } const tests getTests(text); return tests.filter(isVisualizableTest); }); });细节值得注意首次调用立即执行此后每次解析前强制等待 1 秒timeout(1000)相当于对编辑器改动做了节流若文档版本已变化则直接返回空结果避免把过期缓存喂给 CodeLens文档中连test字样都没有时直接跳过 AST 解析最终只保留通过isVisualizableTest过滤的用例。3.3[visualizable]标记与过滤过滤逻辑只有一行function isVisualizableTest(info: TestInfo): boolean { return info.path.some(p p.indexOf([visualizable]) ! -1); }即只要测试路径describe/suite 分组名或 test 名称中的任意一段包含子串[visualizable]该用例就会被选中。因此标记方式非常灵活——你可以标记单个测试名也可以在整个describe([visualizable] ...)分组上标记让组内所有测试都可被可视化。3.4 CodeLens生成 Visualize Test 代码操作扩展向javascript与typescript两种语言、file协议的文档注册 CodeLens 提供器languages.registerCodeLensProvider([ { language: javascript, scheme: file }, { language: typescript, scheme: file } ], { provideCodeLenses: async (document, token) { ... } })对每个命中的测试在测试声明行首生成一个 CodeLens其命令为{ title: Visualize Test, command: debug-value-editor.debug-and-send-request, arguments: [{ launchConfigName: Test Visualization Runner, args: { fileName: document.fileName, path: t.path }, revealAvailablePropertiesView: true, }] }命令与参数的含义debug-value-editor.debug-and-send-request由依赖的ms-vscode.debug-value-editor扩展提供负责启动调试配置并把请求发送给 Playground 运行器launchConfigName: Test Visualization Runner指定要使用的调试配置名与仓库中定义的调试配置对应args.fileName当前测试文件的绝对路径args.path测试的层级路径如[describe 名, test 名]revealAvailablePropertiesView: true运行后自动展开属性可视化视图。3.5 运行效果下图为扩展在工作区中的实际效果在标记了[visualizable]的测试上方出现 Visualize Test 入口选中后即触发调试运行测试产出的属性数据会在调试可视化视图中展开四、Playground 运行器test/testVisualizationRunner.ts 的完整机制README 明确说明测试由file://./../../../test/testVisualizationRunner.ts运行该文件test/testVisualizationRunner.ts是整个机制的另一半。它被调试配置加载进一个独立进程扮演 Playground Runner核心工作包括4.1 拦截vitest模块轻量级测试桩运行器通过劫持Module.prototype.require拦截对vitest的请求(Module as any).prototype.require function (this: { filename: string }, path: string) { if (path vitest) { return createVitestModule(this.filename); } return r.call(this, path); };createVitestModule返回一个手写的迷你 Vitest 桩vitest.describe(name, fn)与vitest.test(name, fn)并不真正执行测试体而是把它们注册进TestContainer/Test结构通过getDiscoverFn实现发现即登记、登记后清空的惰性收集vitest.expect()返回的toBe/toMatchInlineSnapshot/toMatchFileSnapshot均为空操作确保测试文件在仅被扫描时不会因断言失败而中断。这样做的目的是测试文件被 require 后代码可以按路径逐级查找describe→test结构TestContainer.findTest只执行被点中的那一个用例。4.2 全局钩子与 Debug Value Editor 的接线运行器在globalThis上挂载两组钩子类型定义见 src/util/common/debugValueEditorGlobals.tsexport interface IDebugValueEditorGlobals { $$debugValueEditor_run: (args: any) void; $$debugValueEditor_properties: readonly any[]; $$debugValueEditor_debugChannels: Recordstring, DebugChannel; $$debugValueEditor_refresh?: (body: string) void; } export interface IPlaygroundRunnerGlobals { $$playgroundRunner_data: { currentPath: string[] }; }运行器侧的接线如下g.$$playgroundRunner_data { currentPath: [] }; g.$$debugValueEditor_run args (setTimeout(() { run(args); }, 0)); (g.$$debugValueEditor_debugChannels ?? (g.$$debugValueEditor_debugChannels {}))[run] host ({ handleRequest: (args) { setTimeout(() run(args as any), 0); } });其中注释解释了setTimeout的原因The timeout seems to fix a deadlock-issue of tsx, when the run function is called from the debugger.——即从调试器回调中直接执行会与 tsx 产生死锁因此所有入口都通过setTimeout(..., 0)延后到事件循环下一轮。4.3 单测执行与热重载联动run(args)的流程为打印 Running test: path.join( )→setTestFile按需通过hotRequire加载测试文件 →setTest记录当前路径 →runCurrentTest查找并执行测试async function runCurrentTest() { const t testsPerFileName.get(currentFileName!)?.findTest(currentPath); if (!t) { console.error(Test not found, currentPath); return; } try { const startTime Date.now(); g.$$debugValueEditor_properties []; await t?.runner(); console.log( Test finished ( (Date.now() - startTime) ms).); } catch (e) { console.error(Test failed:, e); } }关键点测试执行前清空$$debugValueEditor_properties执行期间测试把可视化的数据如 JSX 树、模拟数据写入该全局数组供调试可视化视图读取由于hotRequire的存在测试文件一旦修改会立即重新加载并自动重跑当前用例形成 改代码 → 自动重跑 → 可视化刷新 的闭环启动 5 秒后若仍未有测试被执行会打印 Did not run a test after 5 seconds. Probably a bug in the extension? 的告警用于暴露接线故障。4.4 另一套变体testVisualizationRunnerSTest.ts仓库中还提供了面向 stest 与 JSX 树可视化的变体 test/testVisualizationRunnerSTest.ts。它与主运行器共用$$debugValueEditor_run、$$playgroundRunner_data等全局钩子但通过hotRequire加载独立的./testVisualizationRunnerSTestRunner.ts并把路径参数规范化为path.join(__dirname, path.relative(__dirname, fileName))后交给 runner 处理。二者结构一致说明这套 全局钩子 调试通道 的模式在仓库内被复用于不同测试形态。五、工作原理总览与使用方式5.1 端到端数据流把以上内容串起来一次 Visualize Test 的完整生命周期为开发者打开 TypeScript/JavaScript 测试文件onLanguage:typescript触发激活extension.ts用 TypeScript AST 解析出所有test/describe/suite路径过滤出含[visualizable]的用例CodeLens 显示 Visualize Test点击后触发debug-value-editor.debug-and-send-request命令附带launchConfigName: Test Visualization Runner、fileName、path参数调试配置启动test/testVisualizationRunner.ts进程该进程拦截require(vitest)注册测试结构并通过$$debugValueEditor_run接收请求运行器按path定位并执行指定测试测试写入$$debugValueEditor_properties随后在调试属性视图中可视化展示并打印耗时日志。5.2 如何在本仓库中使用该扩展位于 .vscode/extensions/visualization-runner 目录属于仓库自带的工作区开发工具。要复现其效果需要确保 VS Code 中已安装依赖扩展ms-vscode.debug-value-editor见 package.json 的extensionDependencies在任意测试用例名或 describe/suite 分组名中加入[visualizable]标记例如describe([visualizable] string utils, () { test(should trim whitespace, () { /* ... */ }); });打开文件后等待 CodeLens 出现 Visualize Test点击即可经调试通道运行并查看可视化结果修改测试代码后得益于hediet/node-reload与hotRequire运行器会自动重新加载并重跑当前用例无需重启。5.3 对仓库开发的启示从源码结构看这套机制的价值在于把测试执行与结果可视化解耦扩展只负责发现与触发运行器负责加载与执行而 Debug Value Editor 负责展示。仓库作者用它来可视化测试产生的 JSX 树、模拟数据等结构化内容是调试复杂测试输出的高效手段。如果你希望在 vscode-copilot-chat 的开发环境中快速验证某个测试的内部数据形态[visualizable] Visualize Test 就是官方提供的现成入口。六、小结入口与发现extension.ts 通过 CodeLens 提供器 TypeScript AST 解析为含[visualizable]的测试生成 Visualize Test 代码操作运行与展示test/testVisualizationRunner.ts 拦截vitest模块、按路径定位用例并通过$$debugValueEditor_*全局钩子把测试结果交给 Debug Value Editor 可视化调试体验tsxhediet/node-reload热重载贯穿扩展与运行器测试修改后自动重跑刷新可扩展性同一套全局钩子模式被 testVisualizationRunnerSTest.ts 复用支撑 stest 与 JSX 树可视化场景。无论你是想理解 VS Code 扩展如何与调试器交互还是希望复用这套 标记即可视化 的开发工作流本仓库的 Visualization Runner 都是一个结构清晰、代码量小、机制完整的参考实现。赞分享人工智能AI 应用AI Agent代码智能体交互助手工具调用MCP Clients【免费下载链接】vscode-copilot-chatCopilot Chat extension for VS Code项目地址https://gitcode.com/gh_mirrors/vs/vscode-copilot-chat点击查看免费下载相关推荐VS Code Copilot 扩展开发之 Visualization RunnerVisualize Test 代码透镜与单条测试调试链路VS Code Copilot 扩展开发之 Visualization RunnerVisualize Test 代码透镜与单条测试调试链路 本文围绕 VS开发工具代码编辑器在 VS Code 原生 Copilot Chat 中使用 OmniRouteOmniCopilot 扩展接入指南在 VS Code 原生 Copilot Chat 中使用 OmniRouteOmniCopilot 扩展接入指南 OmniRoute 是一个单端点接入多 p后端API网关LLM 网关人工智能大模型MCP 服务桌面应用377 条 AI 提示词从哪抄、怎么改awesome-prompts 完整上手指南377 条 AI 提示词从哪抄、怎么改awesome prompts 完整上手指南 awesome prompts 是一个提示词库Prompt 集合收录提示工程文档人工智能上一篇告别布局调试噩梦5款Flexbox for Android可视化工具全测评下一篇ROCm 6.5实战指南从零到AI推理的完整避坑手册创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考