
Void 代码库完全指南AI 编辑器架构、双进程模型、LLM 消息管线与 Apply 机制深度解析【免费下载链接】void开源AI代码编辑器Cursor的替代方案。项目地址: https://gitcode.com/GitHub_Trending/void2/voidVoid 是一款基于 VSCode 二次开发的开源 AI 代码编辑器其核心业务代码几乎全部集中在src/vs/workbench/contrib/void/目录下。本指南以官方《Void Codebase Guide》为骨架结合当前仓库源码逐层拆解 Void 的代码组织方式、Electron 双进程模型、Service/Action 架构、LLM 内部消息管线以及 Fast Apply / Slow Apply 的完整实现原理帮助你快速具备在 Void 代码库中定位与开发的能力。Void 代码在哪里目录速览Void 的代码库并不可怕绝大多数 Void 自己的代码都存放在src/vs/workbench/contrib/void/这一目录下。该目录下继续分为三个子目录对应三种运行环境browser/运行在渲染进程浏览器进程中的代码例如 editCodeService.ts、void.contribution.ts、autocompleteService.ts 以及大量 React 界面组件browser/react/src/下的 sidebar、quick-edit、diff、settings 等。common/可被任一进程使用的共享代码例如 voidSettingsService.ts、voidModelService.ts、modelCapabilities.ts、sendLLMMessageService.ts、mcpService.ts 等类型定义与服务。electron-main/运行在主进程中的代码例如 sendLLMMessageChannel.ts、llmMessage/sendLLMMessage.ts 等。如需构建、运行等工程化指令请参考 HOW_TO_CONTRIBUTE.md本文聚焦代码库本身的架构讲解。VSCode 运行机制速览Electron 双进程模型Void 继承自 VSCode本质上是一个 Electron 应用。理解 Electron 的双进程模型是进入 Void 代码库的第一道门槛主进程main process负责应用内部事务可以自由import node_modules拥有完整的 Node.js 能力。Void 的electron-main/目录下的代码都运行在这里。浏览器进程browser process这里的 browser 指代 HTML 渲染环境而不特指网页浏览器。Void 的界面、编辑器渲染都发生在这个进程。browser/目录下的代码可以使用window等浏览器对象。由此衍生出三个约定俗成的目录规则browser/目录下的代码永远运行在浏览器进程可以使用window等浏览器对象electron-main/目录下的代码永远运行在主进程可以import node_modulescommon/目录下的代码两个进程都可以用但不获得任何特殊导入能力。浏览器进程不能 import node_modules两种绕过方案浏览器进程被禁止直接import node_modules。Void 为此设计了两套解决方案二者在仓库中都有实际落点把 node_module 代码打包进浏览器React 就是这么做的。相关构建配置可参考 void/browser/react/build.js 与 tsup.config.jsReact 组件会被打包成浏览器可用的产物browser/react/out/。在主进程实现逻辑再通过 channel 建立主进程 ↔ 浏览器进程的通信sendLLMMessage正是如此。浏览器进程侧的 sendLLMMessageService.ts 通过mainProcessService.getChannel(void-channel-llmMessage)拿到 channel把请求转交给主进程侧的 sendLLMMessageChannel.ts 执行真实网络请求再通过onText、onFinalMessage、onError、onAbort等事件钩子把结果流式回传。核心术语Editor、Model、URI 与 Workbench在 Void/VSCode 代码库中工作时以下术语会高频出现Editor编辑器你敲代码的那个东西。打开 10 个标签页其实只是一个editorEditor 内部包含标签页即 model。也就是说Editor 是容器Model 是内容。Model模型文件内容的内部表示在多个 editor 之间共享。例如按Cmd\新建一个分栏 editor 时A.ts的 model 会被两个 editor 共享——两个 editor、一个 model这正是改动能够双向同步的原因。URI每个 model 对应一个 URI如/Users/.../my_file.txt。在 VSCode 语境中URI也叫 resource通常就是一个路径。Workbench工作台包裹所有 editor、终端、文件系统树等 UI 的外壳。类型通常用ITextModel表示 model用ICodeEditor表示 editor。在多数场景下你并不需要关心太多其他类型。Service单例注册与依赖注入VSCode 按 Service 组织代码。一个 Service 就是一个只挂载一次单例的类你可以用registerSingleton注册它之后在任何构造器里用Service装饰器注入使用。Void 提供了一个完整的示例文件 _dummyContrib.ts展示了标准的三件套写法且每次注册都一样// 1. 定义接口 装饰器 export interface IDummyService { readonly _serviceBrand: undefined; // services 需要这个保持 undefined 即可 } export const IDummyService createDecoratorIDummyService(DummyService); // 2. 实现类 class DummyService extends Disposable implements IWorkbenchContribution, IDummyService { static readonly ID workbench.contrib.void.dummy // workbenchContributions 需要services 不需要 _serviceBrand: undefined; constructor(ICodeEditorService codeEditorService: ICodeEditorService) { super(); } } // 3. 注册二选一 registerSingleton(IDummyService, DummyService, InstantiationType.Eager); registerWorkbenchContribution2(DummyService.ID, DummyService, WorkbenchPhase.BlockRestore);Void 的核心服务几乎都遵循同样的模式例如voidSettingsServicevoidSettingsService.ts以registerSingleton(IVoidSettingsService, VoidSettingsService, InstantiationType.Eager)注册voidModelServicevoidModelService.ts亦然。Action / Command可被用户与代码共同调用的函数Actions 是你注册到 VSCode 上的函数之后你或用户都可以调用它它们也叫 Commands。两种触发方式用户触发按CmdShiftP打开命令面板command palette执行代码触发通过commandService按 ID 调用。Void 用 Action 注册了CmdL、CmdK等按键监听。Action 最大的好处是用户可以自由修改键位绑定。参考_dummyContrib.ts中registerAction2的写法registerAction2(class extends Action2 { constructor() { super({ f1: true, id: void.dummy, title: localize2(dummy, dummy: Init), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.Digit0, weight: KeybindingWeight.VoidExtension, } }); } async run(accessor: ServicesAccessor): Promisevoid { /* ... */ } });Void 还把所有 Action ID 集中定义在 actionIDs.ts 中如VOID_ACCEPT_DIFF_ACTION_ID、VOID_REJECT_DIFF_ACTION_ID。内部 LLM 消息管线Internal LLM Message Pipeline从你在 Void 侧边栏发出第一条消息到请求真正到达你的模型提供商中间经过了一整条依赖链。Void 选择在主进程发送 LLM 消息原因有二规避本地提供商的CSP内容安全策略问题可以更方便地使用node_modules中的 SDK 与 HTTP 库。其核心调用链在仓库中可完整追踪用户在侧边栏 / 快捷编辑界面触发发送浏览器进程侧的 sendLLMMessageService.tsILLMMessageService校验modelSelection是否为空、消息是否为空然后生成requestIdgenerateUuid()把消息参数、settingsOfProvider、mcpTools一起通过 IPC channelvoid-channel-llmMessage发给主进程主进程侧的 sendLLMMessageChannel.ts 与 llmMessage/sendLLMMessage.ts 真正组装 payload、向模型提供商发起请求流式结果通过onText/onFinalMessage/onError事件回传到浏览器进程的 hooks 表最终渲染到界面。值得特别注意的是modelCapabilitiesmodelCapabilities.ts这是必须在新模型发布时同步更新的关键文件。它定义了每个模型/提供商的静态能力描述VoidStaticModelInfo包括字段含义contextWindow输入上下文窗口大小token 数reservedOutputTokenSpace为输出预留的 token 空间为null时默认 4096supportsSystemMessage系统消息支持方式false/system-role/developer-role/separatedseparated 表示以独立字段传递如 AnthropicspecialToolFormat工具调用格式openai-style/anthropic-style/gemini-stylesupportsFIM是否支持 FIMfill-in-middle格式即是否可用于自动补全reasoningCapabilities推理能力描述是否支持推理、能否关闭、能否输出思考内容、预算/努力程度滑块、开源模型的 think 标签等additionalOpenAIPayload追加到 OpenAI 兼容请求体中的额外字段cost/downloadable每百万 token 价格信息与模型是否可下载仅信息展示不参与请求组装该文件还实现了modelOptionsFallback兜底逻辑对于未能精确匹配的模型名通过字符串包含规则如lower.includes(claude)、lower.includes(deepseek)、lower.includes(qwen3)等回退到最接近的已知模型能力配置确保任何未知模型也能以合理默认值工作。Apply 机制Fast Apply 与 Slow ApplyVoid 有两种 ApplyFast Apply基于 Search/Replace与Slow Apply重写整个文件。当用户点击 Apply 且启用了 Fast Apply 时Void 会提示 LLM 输出如下格式的 Search/Replace 块 ORIGINAL // 原始代码 // 替换后的代码 UPDATED这就是 Void 能在上千行的文件上快速应用改动的原因——它本质上等同于让 LLM 替用户按 CtrlF 做一次查找并替换查询。相关的提示词模板可以在 prompts.ts 中看到searchReplaceGivenDescription_systemMessage/searchReplaceGivenDescription_userMessage而 Search/Replace 块的解析逻辑位于 extractCodeFromResult.tsextractSearchReplaceBlocks、ExtractedSearchReplaceBlock。Apply 内部实现DiffZone、DiffArea 与流式 DiffeditCodeServiceeditCodeService.ts是 Apply 的执行者。同一份代码同时服务于三条路径LLM 调用 Edit 工具、用户点击 Apply、以及用户提交CmdK——三者只是 Fast/Slow Apply 模式不同。关键术语DiffZone一个{startLine, endLine}的文本区域Void 在其中计算并展示红色/绿色的Diff。当文件发生任何改动时Void 会遍历该文件上的所有 DiffArea 并刷新其 Diff。DiffArea更一般化的抽象只追踪行号像 DiffZone 一样。唯一支持流式的 DiffArea 是 DiffZone每个正在流式的 DiffZone 都持有一个llmCancelToken。这些数据结构在 editCodeServiceTypes.ts 中有完整定义DiffZone带_streamStateisStreaming、streamRequestIdRef、当前流式行号line与_diffOfId映射Diff由ComputedDiff派生分为edit/insertion/deletion三种类型还有CtrlKZone承载 CmdK 输入框、TrackingZone携带任意元数据、VoidFileSnapshot用于快照审批状态等。Apply 的工作流程点击 Apply 时Void 在整个文件上创建一个DiffZone这样 LLM 产生的任何改动都会以红/绿 diff 显示出来随后开始流式传输改动LLM 调用 Edit 时本质上是调用了 Apply提交 CmdK 时与 Apply 相同只是创建一个更小的 DiffZone而非整个文件。Diff 计算与定位的核心辅助函数在 helpers/findDiffs.ts负责把流式文本与文件内容对比、找出精确的 diff 行区间。editCodeService.ts中还包含了 Search/Replace 块的错误反馈逻辑当ORIGINAL块在文件中找不到完全匹配、出现多次匹配、或与另一个ORIGINAL块重叠时会生成人类可读的错误信息回传给 LLM 并中止本次 Apply保证快速应用不会误改代码。写文件的内部机制voidModelService当 Void 要修改你的代码时它只是往一个文本 model 里写入内容。这意味着要向文件写入内容你只需要知道它的 URI而不需要手动 load、save 等。为了让这套机制正常工作背后有一些烦人的 URI/model 生命周期问题Void 全部封装在了voidModelServicevoidModelService.ts中initializeModel(uri)通过ITextModelService.createModelReference(uri)创建 model 引用并保存在_modelRefOfURI[uri.fsPath]中持有强引用防止被垃圾回收getModel(uri)/getModelFromFsPath(fsPath)同步取回{ model, editorModel }getModelSafe(uri)若 model 尚未初始化则先初始化再返回saveModel(uri)调用ITextFileService.save并设置skipSaveParticipants: true避免触发扩展的格式化参与器额外改动文件否则撤销栈里会多出无关条目。注释中明确说明这样可以让我们的改动 → 保存被当作同一次变更处理。Void 设置内部机制voidSettingsServiceVoid 有一个voidSettingsServicevoidSettingsService.ts存储所有 Void 设置——提供商、模型、全局 Void 设置等。可以把它想象成任何核心 Void 服务的隐式依赖所有核心服务都直接或间接读取它。其状态结构VoidSettingsState包含settingsOfProvider每个提供商的设置API Key、endpoint、模型列表等modelSelectionOfFeature每个功能当前选择的模型optionsOfModelSelection每个功能/提供商/模型的选项如推理预算overridesOfModel用户对模型能力的覆盖globalSettings全局设置mcpUserStateOfNameMCP 服务器的用户开关状态_modelOptions由以上数据计算出的可选模型列表。设置术语表FeatureNameChat|CtrlK|Autocomplete|Apply|SCM源码 voidSettingsTypes.ts 中featureNames定义含提交信息生成器SCM 功能ModelSelection一个{providerName, modelName}对ProviderName提供商名称如ollama、openAI、anthropic、gemini、deepseek、openRouter、groq、xAI、mistral、vLLM、lmStudio、liteLLM、openAICompatible、googleVertex、microsoftAzure、awsBedrockModelName模型名字符串如gpt-4o、claude-3-7-sonnet-latestRefreshProvider会被反复 ping 以更新模型列表的提供商本地提供商ollama、vLLM、lmStudio支持自动探测setAutodetectedModels负责把探测结果合并进模型列表ChatModenormal|gather|agent源码中默认值为agent。持久化加密存储与状态迁移voidSettingsService的存储实现非常值得借鉴所有状态通过IEncryptionService加密后以VOID_SETTINGS_STORAGE_KEY见 storageKeys.ts写入IStorageServiceStorageScope.APPLICATION、StorageTarget.USER。每次状态变更都会经过_validatedModelState校验——自动重算_didFillInProviderSettings、重建可选项列表、并在用户所选模型失效时自动回退到列表第一项或null。代码中还包含多条版本迁移逻辑如 1.0.3 增加includeToolLintErrors、1.2.5 将autoApprove从布尔改为对象、1.3.5 增加 SCM 功能等保证旧版本用户升级后状态结构自动对齐。全局设置默认值源码defaultGlobalSettings给出的默认值如下设置项默认值含义enableFastApplytrue启用 Fast ApplySearch/ReplacechatModeagent对话模式normal / gather / agentenableAutocompletefalse是否启用自动补全autoRefreshModelstrue自动刷新模型列表syncApplyToChattrueApply 跟随 Chat 的模型选择syncSCMToChattrueSCM 跟随 Chat 的模型选择showInlineSuggestionstrue显示内联建议includeToolLintErrorstrue工具 lint 错误是否纳入上下文disableSystemMessagefalse是否禁用系统消息autoAcceptLLMChangesfalse是否自动接受 LLM 的改动autoApprove{}按工具类型配置的自动审批aiInstructions自定义 AI 指令isOnboardingCompletefalse新手引导是否完成提供商默认端点modelCapabilities.ts中的defaultProviderSettings给出了各提供商的默认配置本地服务端点便于开箱即用Ollamahttp://127.0.0.1:11434vLLMhttp://localhost:8000LM Studiohttp://localhost:1234LiteLLMhttp://localhost:4000OpenAI-Compatibleendpoint为空需自行填写 baseURL如https://my-website.com/v1支持headersJSON自定义请求头Google Vertex默认region: us-west2Microsoft Azure默认azureApiVersion: 2024-05-01-previewAWS Bedrock默认region: us-east-1审批状态Approval StateeditCodeService的数据结构中包含了用户需要审查的全部改动信息但这些信息不是以方便消费的格式存放的。因此 Void 专门编写了一个派生服务把这些原始数据转换为更实用的审批状态供界面如 diff 上的接受/拒绝按钮直接消费。相关动作 ID 定义在 actionIDs.tsVOID_ACCEPT_DIFF_ACTION_ID、VOID_REJECT_DIFF_ACTION_ID快照结构VoidFileSnapshot与DiffAreaSnapshotEntry则定义在 editCodeServiceTypes.ts——快照只保留diffAreaSnapshotKeys中列出的关键字段type、diffareaid、originalCode、startLine、endLine、editorId用于在流式过程中记录改动前的文件状态。构建流程关于 Void 的构建管线仓库内注释说明其由独立的构建仓库void-builder负责Void 主仓库专注于源码。这意味着发布流程、打包、签名、自动更新等工程化环节与主仓库解耦主仓库的 CI 相关配置可参考根目录的 CodeQL.yml 与 gulpfile.js。VSCode 代码库学习路线Void 团队为想要深入 VSCode 生态的开发者整理了一份分级参考清单原文为外部链接此处转为可操作的知识点摘要入门必读VSCode 用户界面指南auxbar、面板等与 UX 设计指南Container、View、Item 等抽象。贡献者必读VSCode 源码组织方式——这是整份清单中最重要的一篇解释了入口文件在哪里、browser/与common/的含义等VSCode 内置样式变量即var(--vscode-{theme 名点替换为横线})形式的 CSS 变量以及 Webview 主题化指南。杂项VSCode 内置命令全集不常用仅作参考。值得注意的是VSCode 的仓库就是 Monaco 编辑器的源码来源——一个 editor 就是一个 Monaco 编辑器它共享ITextModel等代码。扩展 API历史遗留Void 已经不再是一个扩展因此这些链接不再是必需但如果未来重新构建扩展可能会用到——包括扩展所需文件清单、扩展package.json的 schema、contributes挂载机制、完整扩展 API重点看页面底部的 cancellation tokens、events、disposables 模式、以及package.json中的 activation events。结语从整体看Void 的架构哲学非常清晰主进程负责重活发请求、操作文件系统浏览器进程负责 UI 与状态common/承载共享逻辑Service 单例负责全局状态Action 负责可重绑定的命令入口。无论你是想为 Void 新增一个提供商、调试 Apply 的 diff 逻辑还是理解它如何管理模型能力都可以从 void/ 目录出发沿着voidSettingsService配置→sendLLMMessageService请求管线→editCodeService代码修改这条主线快速上手。【免费下载链接】void开源AI代码编辑器Cursor的替代方案。项目地址: https://gitcode.com/GitHub_Trending/void2/void创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考