- 开发工具
- 代码编辑器
- 数据科学
【免费下载链接】positron
Positron, a next-generation data science IDE
Positron 是一款基于 VS Code 构建的下一代数据科学 IDE,为 Python 和 R 提供一等公民支持。本文以仓库根目录的 CLAUDE.md 为主线,系统解读 Positron 工程实践的核心脉络:为什么严禁直接运行 TypeScript 编译、四类测试(Vitest / Core Mocha / Extension Host / E2E)如何分层分工、以及隔离 Positron 代码以最小化上游合并冲突的方法论。读完本文,你将掌握 Positron 的构建检查工作流、测试选型决策表与代码风格规范,并能在实际开发中直接复用这些命令与规则。
构建体系:为什么严禁直接运行 TypeScript 编译
Positron 源码量级庞大(src/vs/下数以万计的 TypeScript 文件),直接对src/tsconfig.json执行npx tsc、tsc --noEmit等命令必然失败或长时间挂起。因此项目采用**后台守护进程(build daemons)**负责全部编译工作,开发者不应在任何场景下绕过这一体系。
标准构建检查工作流
正确的编译验证流程分为四步:
- 检查守护进程状态:
npm run build-ps(对应 scripts/build-ps.sh); - 按需启动缺失的守护进程:
npm run build-start(对应 scripts/build-start.sh); - 在守护进程后台编译的同时继续开发任务(初始启动约需 30-60 秒);
- 检查最新一轮 TypeScript 编译的错误:
npm run build-check(对应 scripts/build-check.sh)。
其中build-check有两个关键语义需要理解:
- 它会阻塞直到所有守护进程完成当前编译周期,因此调用前不要画蛇添足地
sleep; - 它输出的是上一轮编译周期的错误,所以应当调用一次并完整读取输出,而不是反复轮询。
从 scripts/build-check.sh 的源码可以看到,它通过node scripts/deemon-status.mts并行等待五类守护进程全部进入空闲状态,各自以 "Starting compilation..." 为开始标记、以 "Finished compilation with..." 或 "Found N errors" 为结束标记,任一守护进程失败则整体以非零码退出:
watch-client(npm run watch-client)watch-extensions(npm run watch-extensions)watch-e2e(npm run watch-e2e)watch-client-transpile(npm run watch-client-transpile)watch:typecheck(npm run watch-copilot)
常见边界情况
安装新依赖后出现缺失包错误时,只需重启守护进程链:
npm run build-stop && npm run build-start && npm run build-checkVitest 测试文件的类型检查例外:build-check默认排除*.vitest.ts文件,因此不会暴露测试代码中的类型错误。要检查这些文件,运行:
npm run test:positron:check-ts该命令是对tsc --noEmit -p ./vitest.tsconfig.json --skipLibCheck的封装(见根目录 package.json),作用域限定于 vitest 文件及其环境声明,数秒内即可完成,输出与 VS Code 问题面板对.vitest.{ts,tsx}文件的展示一致。只想过滤某个文件时:
npm run test:positron:check-ts 2>&1 | grep '<file>.vitest.ts'复现 CI 失败的内部限制
文档明确标注:只有在用户明确要求复现CI 专属失败(arm64 真实 CI 镜像)时才需要走本地复现流程。该流程需要私有 GHCR 访问权限与 license 文件,不适用于外部贡献者——判断依据是gh api repos/posit-dev/positron-ci-images是否返回 404(404 即无权限,此时应告知用户而非尝试配置)。进行复现前必须完整阅读 .devcontainer/ci-arm/README.md,不能只看其中的 "Claude Workflows" 章节,因为该章节的命令依赖文件其他部分的 Setup/Gotchas 上下文;流程涉及复用长期存在的实验室 worktree(不要重建)并将其指向新分支。
上游兼容:在 VS Code 分叉中隔离 Positron 代码
Positron 分叉(fork)了 VS Code。为把合并冲突降到最低,核心策略是隔离 Positron 代码:
- 优先新建文件,而非修改上游文件;
- 使用
./scripts/file-origin.sh <file>检查文件来源。
scripts/file-origin.sh 的实现逻辑值得一看:它读取文件头 20 行,若包含 "Copyright...Posit" 或路径中含positron判定为positron,若版权头含 "Microsoft" 则判定为upstream,否则输出unknown。这为快速定位"哪些文件是我们改过的"提供了自动化依据。
当上游文件的修改不可避免时,必须用标记块包裹改动:
// --- Start Positron --- // Explanation of why this change is necessary // Commented out upstream code to aid merge conflict resolution // Keep changes minimal and contiguous ... // --- End Positron ---标记块要求:写明改动原因、保留被注释的上游代码以辅助解决合并冲突、改动保持最小且连续。
测试体系:三类测试与四种运行器
Positron 将测试划分为三大类,理解它们的边界是写好测试的第一步。
术语与分类
| 类别 | 运行器 | 特点 |
|---|---|---|
| Unit tests | Positron Vitest / Core Mocha | 隔离测试单个模块;Vitest 快、无需构建守护进程;Core Mocha 是上游 VS Code 的测试套件,较慢、依赖构建守护进程 |
| Extension host tests | Mocha(ext host) | 需要激活的扩展宿主,历史文档称其为 "integration tests",即同一事物;分 API 契约测试(extensions/vscode-api-tests/对vscodeAPI 面的回归)与扩展内部测试(各扩展自测自身功能,如positron-python、positron-r、git、typescript-language-features) |
| E2E tests | Playwright | 运行完整应用,见 test/e2e/README.md |
从仓库结构看,test/e2e/下分为infra/(驱动、浏览器、Electron、测试运行器等)、pages/(Positron 页面对象模型 POM)与tests/(按功能区域组织的用例)。
测试放哪里:最低层级覆盖原则
核心原则是:在能覆盖该行为的最低层级测试。毫秒级完成的单元测试远优于秒级且易 flaky 的 E2E 测试;只有真正需要完整应用的工作流才使用 E2E。文档给出了完整的决策表:
| 测试对象 | 运行器 | 作用域/路径 | 模式 | 文件扩展名 |
|---|---|---|---|---|
| 纯函数或类(无服务) | Vitest | src/vs/** | Plain test | .vitest.ts |
| 需要 DI 服务的服务或类 | Vitest | src/vs/** | BuildercreateTestContainer() | .vitest.ts |
| React 组件(仅 props) | Vitest | src/vs/** | RTL prop-drivensetupRTLRenderer() | .vitest.tsx |
| 使用服务的 React 组件 | Vitest | src/vs/** | RTL service-contextwithReactServices() | .vitest.tsx |
| 既有上游 VS Code 测试(少见) | Mocha(Electron/Node/Browser) | src/vs/**/test/ | 匹配既有写法Mocha suite()/test() | .test.ts/.integrationTest.ts |
| 需要激活扩展或工作区 API 的代码 | Mocha(ext host) | extensions/<name>/ | vscode APIimport * as vscode | .test.ts |
| 跨系统用户可见工作流 | Playwright | test/e2e/ | Page Object Modelapp.workbench.* | .test.ts |
两条硬性约束:
- 不要为 Positron 代码在
src/vs/下新建.test.ts——请使用 Vitest;Core Mocha 行只用于维护既有上游测试; - Vitest 同样收集
extensions/**/*.vitest.{ts,tsx}——只要逻辑不依赖vscodeAPI 即可用 Vitest 替代扩展宿主测试。不过向扩展添加第一个 vitest 文件时,必须同时将该扩展的测试从构建中排除(详见下文 "extensions 内的测试")。
运行测试的命令速查
Vitest(*.vitest.ts/*.vitest.tsx,无需构建守护进程):
npm run test:positron # 运行全部 npx vitest run src/path/to/<file>.vitest.ts # 运行单个文件 npx vitest --watch src/path/to/<file>.vitest.ts # watch 模式(保存即重跑;q 退出,h 查看键盘帮助) npx vitest run --coverage --coverage.include='**/myFile.tsx' <test-file> # 作用域覆盖率 npx vitest run --update <file> # 更新内联快照Core Mocha(src/vs/下的*.test.ts/*.integrationTest.ts,需要构建守护进程):
npm run build-start && npm run build-check # 先确保守护进程在跑 npm run test:core # 运行全部(等价 ./scripts/test.sh) ./scripts/test.sh --run src/path/to/<file>.test.ts # 运行单个文件 ./scripts/test.sh --runGlob <glob>.test.js # 按 glob 运行(注意用 .js 扩展名)Extension host(extensions/<extension-name>/*.test.ts):
npm run test-extension -- -l <extension-name> --grep <pattern> # 运行单个扩展的测试 npm run test:ext-host # 运行完整 CI 驱动(等价 ./scripts/test-integration.sh)注意:positron-python有自己的测试体系,详见 extensions/positron-python/CLAUDE.md。
E2E(Playwright,完整应用):
npx playwright test test/e2e/tests/<test-name>.test.ts --project e2e-electron --grep '<pattern>'E2E 测试有两条特殊纪律:
- 必须在后台运行而非前台——单个用例耗时 1-7 分钟、整套套件耗时数十分钟,前台阻塞调用会使用户消息无法送达,导致"卡住了,请停止"无法传达、也无法中止任务。应使用后台运行方式(或对超长运行使用
nohup ... &)并跟踪日志; - 后台运行还能让你边看日志边针对失败即时行动。
extensions 内的 Vitest 测试
在扩展中添加第一个 vitest 文件时,有四件事与src/vs/不同(详见 .claude/rules/vitest-tests.md)。最关键的是同一变更中必须将测试从该扩展的构建中排除,在extensions/<name>/tsconfig.json添加:
"exclude": [ "src/**/*.vitest.ts", "src/**/*.vitest.tsx" ]原因:不排除的话测试会被编译进out/并随扩展一起发布,而其中的vitest导入在运行时无法解析。仓库用extensions/noVitestFilesInBuilds.vitest.ts自动守护这一点——它按扩展真实构建的方式解析每个扩展的 tsconfig,若某个.vitest.文件会被包含则测试失败。手工验证可用命令而非肉眼检查:
npx tsc -p extensions/<name> --listFilesOnly | grep vitest # 期望无输出 ls extensions/<name>/out | grep vitest # 期望无输出Vitest 的配置形态
根目录 vitest.config.ts 展示了 Positron 的 Vitest 落地方式:
include覆盖src/vs/**/*.vitest.{ts,tsx}、src/*.vitest.{ts,tsx}、test/e2e/**/*.vitest.{ts,tsx}及extensions/**/*.vitest.{ts,tsx}(内置扩展按文件逐个 opt in,仅加载不导入vscode的模块);- 环境为
happy-dom,启用 globals; setupFiles注册@testing-library/jest-dom匹配器(toBeInTheDocument、toHaveTextContent等);restoreMocks与clearMocks保证测试间 spy 状态与调用历史干净隔离。
三种典型测试形态(规则文件 .claude/rules/vitest-tests.md 中的代码示例):
纯函数:直接复制既有示例改断言即可,无 builder、无服务、无 setup。
带 DI 的服务:用 builder 装配测试服务,从覆盖依赖的最低预设开始,让 "missing service" 错误引导逐级提升:
describe('MyService', () => { const ctx = createTestContainer() .withRuntimeServices() .stub(IMyService, { getData: vi.fn() }) .build(); it('does the thing', () => { const svc = ctx.instantiationService.createInstance(MyService); expect(svc.doThing()).toBe(true); }); });React 组件:props 驱动的组件用setupRTLRenderer(),依赖服务的组件用withReactServices(),完整 RTL 约定见 .claude/rules/vitest-rtl.md。
文件放置约定:测试文件紧邻源码(browser/的测试放test/browser/,common/的放test/common/),扩展名.vitest.ts(React 组件用.vitest.tsx),版权头后需写/// <reference types="vitest/globals" />以获得 IDE 智能提示,缩进用 Tab。
排查失败的 E2E 测试:专用技能优先
对于指定的失败或 flaky 的 e2e 测试,应使用debug-e2e-test技能而非通用调试技能(见 .claude/skills/debug-e2e-test/SKILL.md)。该技能有两条入口:
- failing in CI(CI 失败)——从该测试在 CI 的真实失败历史出发,而非猜测机制;
- failed on your machine(本机失败)——从该次运行自己的
test-results/下的 trace、快照与日志出发(无需 API key 或网络)。
无论哪条入口,都要求给出可证伪的根因,并明确证据的排除与保留;不要在它之上再叠加通用调试流程,也不要先跑通用流程再consult技能——到那时最昂贵的"决定调查什么"环节已经在没有证据的情况下完成了。通用调试技能仍负责其余一切:Vitest 或扩展宿主失败、以及任何非测试失败的 bug。
目录结构速览
| 目录 | 内容 |
|---|---|
src/ | 带 Positron 改动的 VS Code 核心源码 |
extensions/ | 内置扩展,含 Positron 专属扩展 |
test/e2e/ | Playwright 端到端测试 |
positron/ | Positron 专属代码与资源 |
build/ | 构建配置与脚本 |
代码风格:可被机器执行的规范
Positron 的代码风格规则不是空泛约定,而是由 pre-commit 钩子强制执行的:
- TypeScript/JavaScript 缩进必须用 Tab,不能用空格;
- 严禁使用 em-dash、en-dash、智能引号等非 ASCII 标点,一律用 ASCII 连字符与直引号;
- pre-commit 钩子检查暂存文件的 unicode、缩进、版权头、格式与 eslint 问题。
手动运行与自动修复:
npm run precommit # 检查全部暂存文件 npm run precommit -- <file> # 检查指定文件 node scripts/format.mts <file> [file2] ... # 自动修复格式(对应 scripts/format.mts) npx eslint --fix <file> [file2] ... # 自动修复 eslint 问题用户配置项的两套命名规则
注册面向用户的配置时(详见 .claude/rules/configuration.md),需要注意两套容易混淆的命名:
- 设置键(setting key)、标题、显示名:省略冗余词("Positron"、"Setting" 等),使用主题化命名空间而非
positron.前缀。例如用startupDiagnostics.timeout而不是positron.startupDiagnostics.timeout;标题写 "Kernel Supervisor" 而不是 "Positron Kernel Supervisor Settings"; localize()ID:保留positron.前缀。它是内部查找键,供本地化工具链使用,加前缀是为了让 Positron 引入的字符串与上游 VS Code 字符串区分开、可被定位。
两条规则看似矛盾实则分工明确:设置键面向用户(bare),localize ID 面向工具链(prefixed)。
AI 功能必须挂在总开关上
新增任何 AI 相关功能(调用模型、建议补全、展示 AI 动作)时,必须挂到ai.enabled主开关(详见 .claude/rules/ai-gating.md),让用户或 Posit Workbench 管理员能在单点关闭全部 AI 能力。要点:
ai.enabled是 Positron 自有设置,默认true,作用域为WINDOW,在src/vs/workbench/contrib/positronAssistant/common/positronAIConfiguration.ts中声明并导出为AI_ENABLED_KEY常量——导入该常量,不要硬编码字符串;- 它位于各功能自身前置条件之上,检查时是叠加而非替换(还需同时满足功能自身的设置、是否配置 chat 模型、notebook 模式等);
- 实时读取而非构造时缓存——
ai.enabled可无重载切换,构造器里捕获一次的值会过期; - 在 Posit Workbench 中该开关通过
POSITRON_ENFORCED_SETTINGS环境变量强制执行,getValue与 context key 已反映强制值; - 三种读取形态按运行环境选择:action
when子句用ContextKeyExpr.has('config.${AI_ENABLED_KEY}'),服务/非 React 代码用this._configurationService.getValue<boolean>(AI_ENABLED_KEY) === true,React 组件用usePositronConfiguration<boolean>(AI_ENABLED_KEY)。
通用工程纪律
最后是两条影响日常协作的通用规则:
- GitHub 交互统一使用
ghCLI; - 不要使用无作用域(unscoped)的搜索工具——它们会去检索大型编译产物并挂起,始终使用内置的带作用域搜索工具。
结语
CLAUDE.md 与其引用的规则文件共同勾勒出一套可落地、可验证的工程方法论:用守护进程把巨型代码库的编译成本从"每次阻塞"变成"后台并行";用"最低层级覆盖"的决策表让测试又快又稳;用// --- Start Positron ---标记块与 file-origin 脚本让分叉维护长期可承受;再以 pre-commit 钩子和ai.enabled总开关把风格与 AI 边界固化成机器检查。对于在 Positron 仓库中贡献代码的开发者,这套流程既是效率保障,也是与上游 VS Code 长期共存的生存法则。
- 开发工具
- 代码编辑器
- 数据科学
【免费下载链接】positron
Positron, a next-generation data science IDE
相关推荐
VS Code Container Tools 容器开发排障指南:非 root 用户、Docker 套接字与远程守护进程
VS Code Container Tools 容器开发排障指南:非 root 用户、Docker 套接字与远程守护进程 VS Code 的 Container
文档教程Trigger.dev 仓库开发协作指南:读懂 AGENTS.md 与 CLAUDE.md 分层的构建、测试与架构体系
Trigger.dev 仓库开发协作指南:读懂 AGENTS.md 与 CLAUDE.md 分层的构建、测试与架构体系 本指南以 Trigger.dev 开源仓
AI Agent后端任务调度开发工具可观测性AI 应用Paseo Server 开发指南:多编码 Agent 守护进程的架构、构建、测试与工程规范
Paseo Server 开发指南:多编码 Agent 守护进程的架构、构建、测试与工程规范 Paseo 是一个用于监控与控制本地 AI 编码 Agent(Cl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考