跳转至

已关闭编辑器的模型生命周期

关闭最后一个编辑器,或在所有编辑器面板都隐藏时关闭文件,可能导致其 Monaco 文本模型在整个渲染器剩余生命周期内保持注册状态。旧的清理钩子位于条件挂载的 EditorPanel 中:它在观察到最终关闭之前就已消失,并在其缺席时错过了关闭事件。MonacoEditor 使用 keepCurrentModel,因此卸载组件并不能提供缺失的清理。

修复方案从应用外壳订阅,并延迟地从现有 monaco-setup 加载获取模型注册表。它只退役已关闭的标签页所有者。它保留存活的同 URI 编辑器、替换模型对象、生成的 diff 命名空间、在分离前保持附加的模型,以及在处置回调期间由重新打开的所有者写入的缓存。现有的滚动、选择、预览和 PDF 缓存保留其独立的 20 条 LRU 生命周期。不会修剪任何打开的标签页、未保存草稿或存活编辑器历史。已关闭文件的撤销遵循 Monaco 现有的有界策略;它不是无限的(见下文)。

测量结果

实际源控制 之前 修复后
八次不同的最后文件关闭 8 个已注册的已关闭模型;2,097,208 个逻辑字符;0 个打开文件 0 个已注册模型;0 个打开文件
在另一个编辑器仍挂载时关闭 已关闭模型被处置;同级编辑器保留 相同
隐藏时关闭,然后打开另一个编辑器 旧的已关闭模型保留;1 个打开文件对应 2 个模型 旧模型被处置;1 个打开文件对应 1 个模型
普通终端/编辑器切换 原始打开模型保留 相同

所有四个控制项都在两个图和两个运行时上按所述之前/修复后预期通过:32 个比较用例,Node 24.20.0 和 Electron 43.7.0 / Node 24.21.0。报告为 worktree-*-results.json 和 main-*-results.json。内容使用八个不同的 fixture 路径,每个模型 256 KiB 逻辑字符;这是一种受控放大,不是现场文件大小测量或堆字节估算。

这解释了一个与 #12845 相关的具体渲染器保留机制。相同的条件面板、钩子位置和 keepCurrentModel 链存在于报告的 v1.4.170 中;source-versions.json 记录了四个历史源码哈希和匹配的行号。该比较是静态的,不是对历史应用的执行。它不能确定哪些分配导致了 #12845 报告的堆,也不能确定 #19831 的过程和原因。

所有权与回归覆盖

closed-editor-model-{lifetime,reentrancy,registry,shell}.test.* 和 closed-editor-view-state-retention.test.ts 中横跨五个套件的 27 个永久用例使用实际安装的 Monaco 模型并存储关闭操作。它们覆盖最终/隐藏面板关闭、存活同级编辑器和共享 URI、替换模型身份、附加/分离、重新打开、注册表移除/替换、重入注册表变更、diff 前缀、有界缓存,以及无需全局注册表扫描的普通编辑关闭。连同三个现有的处置/缓存套件,47 个测试通过。该 fixture 在不创建原生编辑器组件的情况下验证 Monaco 的真实附加端口。

启动路由套件现在也保护生产应用外壳钩子位置:删除该调用会使测试失败。所有横跨九个套件的 75 个选定测试通过。完整的 Web 类型检查当前报告一个无关的现有未使用 NativeChatMessage 导入位于 NativeChatMessageList.windowing.test.tsx;它在更改的文件中不报告错误。

注册表通知会取消过期的排队回调,但保留捕获的模型身份和附加模型监听器。临时缺失的注册表会延迟清理,直到注册返回;被中断的处置批次会重试。身份围栏防止该重试处置同一 URI 上的后继模型。控制器拆除仍会取消该控制器的所有权和监听器;此审计不声称在应用外壳 HMR 重新挂载后协调所有现有模型。

已关闭文件的撤销决策

保留 Monaco 的有界已关闭文件撤销存储,而不是无限期保留每个已编辑模型,遵循 VS Code 的模型服务 使用的相同设计。已安装的 Monaco 保留约 20 MiB 的已关闭文件撤销数据。测试确认普通 POSIX 路径和 file:///C:/... 路径在重新打开且内容匹配时恢复撤销。大文件(fixture 中超过 10 MiB 文本)和原始 Windows C:/... 路径则不会。这些情况在关闭/重新打开后丢失撤销,包括其模型先前泄漏的最终/隐藏标签页。现有视图状态缓存在其 20 条预算内保持完整。这是有意的有界内存权衡,而非零用户可见风险。

源码围栏与独立主分支发布

source-versions.json 围栏 1,021 个仓库路径:比较证明的完整已加载仓库图,外加选定的静态接线/调用方文件。Vite 拒绝 src/ 或 config/ 下的任何额外仓库模块。包代码不属于该仓库图声明:十二个独立的依赖哈希固定了已安装包元数据以及相关的 Monaco 模型/注册表/事件和 Zustand store 实现。

  • Worktree 基线:f142544a8653eedab2eec0237a9005001497b2f5。
  • 发布 main:291b4ddd6f1c1af480169885e0fda7f9c78ff053。
  • fix.patch 仅包含八处产品更改,包括两个新模块。加载器从签出的修复源码反向应用它,以重建基线行为。
  • main-context.patch 重建 22 个无关上下文差异,包括两个在 main 上不存在的路径。它是证明脚手架;不要将其作为产品补丁应用。
  • Main 在 monaco-setup.ts 中保留其无关的 registerShellMarkdownAliases 导入/调用。独立产品补丁保留这两行。
  • 加载器检查每个之前/修复后哈希,拒绝源码/依赖漂移,将 CRLF 规范化为 LF,并支持工作签出和独立发布的主分支签出。loader-results.json 记录所有四个图/变体组合的合成 CRLF 和模拟主分支签出控制。

对比测试会执行实际的旧版界面、旧/新清理钩子、释放函数、store 关闭操作和模型注册表。它向钩子提供受控的 Panel/Shell 端口和一个 fixture store。实际的 app-shell 放置、首次懒注册、条件面板调用方以及 keepCurrentModel 接线均为基于源码围栏的静态检查;它不会渲染整个应用,也不会通过 Vite 本身来验证 HMR。它不会打开 Electron 窗口,不会启动原生编辑器部件,也不会测量 RSS、堆字节数或事件分配速率。

审查后续

注册表转换回归由四个新用例覆盖。无信息量的冷注册表 fixture 断言已移除,并且本次审计的产品补丁、源码哈希、loader 控制以及全部八份对比报告均已从当前源码重新生成。审计目录现在已在 .gitignore 中显式允许。

复现

在已安装依赖的仓库根目录中:

ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/closed-editor-model-lifetime/vitest.config.mjs
node docs/audits/closed-editor-model-lifetime/loader-controls.cjs

默认值为 ORCA_CLOSED_MODEL_GRAPH=worktree 和 ORCA_CLOSED_MODEL_VARIANT=fixed。请针对每个 graph(worktree、main)和 variant(before、fixed)重复执行。使用你的 shell 语法设置这些环境变量;运行器和路径处理是平台中立的。将 ORCA_CLOSED_MODEL_OUTPUT 或 ORCA_CLOSED_MODEL_LOADER_OUTPUT 设置为输出路径,以避免替换已签入的报告。

对于 Electron,请使用 ELECTRON_RUN_AS_NODE=1、ORCA_BACKGROUND_LAUNCH=1、--no-experimental-webstorage --expose-gc 运行已安装的 Electron 二进制文件,随后执行 node_modules/vitest/vitest.mjs run --config docs/audits/closed-editor-model-lifetime/vitest.config.mjs 以及相同的 graph/variant 环境。这会使用 Electron 的 Node 运行时,而不会创建窗口。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 orca48/orca