第 1 章:全景——Zed 到底是什么
一句话定义:Zed 是一个以 GPUI 实体运行时为外壳、以可快照化协同文本模型为内核、 以 Project 抽象承接本地与分布式工程能力的原生桌面开发环境。
1. 先纠正三个直觉
1.1 它不只是“Rust 写的编辑器”
如果只看到 Rust 和 GPU,会漏掉 Zed 更关键的结构:文本从一开始就能产生可复制的操作, Project 从一开始就允许 local/remote 两种实现,UI 状态从一开始就被 GPUI 的实体和事件管理。
也就是说,协作、远程和 AI 不是后来挂在单机编辑器上的三块功能,而是不断复用既有内核:
┌──────────────┐
用户动作 / Agent ──▶│ Workspace/UI │
└──────┬───────┘
│
┌──────▼───────┐
│ Project │ 统一工程语义
└───┬──────┬───┘
│ │
local store│ │remote store / proto
│ │
┌────────▼──┐ ┌─▼────────────┐
│ Worktree │ │ Remote host │
│ LSP / Git │ │ collab host │
└─────┬─────┘ └──────┬───────┘
└──────┬────────┘
┌────▼────┐
│ Buffer │ operation + snapshot
└─────────┘1.2 GPUI 不只是绘图库
GPUI 同时提供:
- 应用和窗口事件循环;
Entity<T>状态容器与上下文受控读写;- observe/subscribe/notify 反应式传播;
- 前台与后台 executor;
- Action、焦点、键位分发;
- Element 布局、prepaint、paint 与 GPU Scene。
因此应用代码里的 cx 不是“顺手传的上下文”,而是能力边界。没有合适的 cx,就不能 随意修改实体、窗口或全局状态。
1.3 Editor 不是直接编辑文件字符串
最短的数据链也至少经过:
filesystem file
→ language::Buffer(文本 + 语法 + 诊断 + 文件状态)
→ MultiBuffer(一个视图拼接一个或多个 Buffer 片段)
→ DisplayMap(inlay → fold → tab → wrap → block → highlight)
→ EditorElement(布局、输入、选择、命中测试)
→ GPUI Scene(quad / glyph / path / surface)这解释了搜索结果、diff、diagnostics 或 Agent edit 为什么仍能复用同一个 Editor:它们改变 的是 MultiBuffer 的摘录和显示映射,不必另写一套文本控件。
2. 六层架构地图
第 1 层:平台与渲染
代表 crate:gpui_platform、gpui_macos、gpui_linux、gpui_windows、 gpui_wgpu、gpui。
职责是接收 OS 事件、维护窗口、排版文字、组织场景并提交 GPU。平台差异被压在底层, 上层只面对 Application、Window、Element 和 Scene。
第 2 层:基础数据结构
代表 crate:sum_tree、rope、text、clock、collections。
这里解决大文档增量编辑、多个坐标维度快速跳转、持久化快照、操作排序和逻辑位置稳定性。 这是 Zed “快”与“可协作”的共同地基。
第 3 层:编辑语义
代表 crate:language、multi_buffer、editor、buffer_diff、markdown。
language::Buffer 把纯文本提升成源码文件;MultiBuffer 把多个摘录合成可编辑文档; Editor 管选择、命令、补全和显示状态。
第 4 层:工程模型
代表 crate:fs、worktree、project、lsp、git、task、terminal、dap。
这里把文件树、打开的 Buffer、语言服务器、Git 仓库、任务、终端和调试会话组合成一个 可本地也可远程的 Project。
第 5 层:产品工作区
代表 crate:workspace、ui、project_panel、git_ui、terminal_view、 settings_ui、theme。
Workspace 对应一个窗口中的完整产品状态:中心 pane group、三个 dock、status bar、 Project 以及所有可打开 item。
第 6 层:分布式与智能
代表 crate:client、rpc、proto、collab、remote、agent、agent_ui、 language_model、extension_host。
这一层把同一套工程模型连接到云端房间、远程主机、语言模型和 Wasm 扩展。
3. 一次编辑的完整旅程
假设用户在一个 Rust 文件中输入 x。
步骤 A:平台事件进入 GPUI
平台适配器把键盘输入交给 Window。Keymap 根据焦点路径和 context 匹配 Action;如果是文本 输入,则通过输入处理器交给 Editor。
步骤 B:Editor 把显示坐标还原为 Buffer 坐标
屏幕上的 cursor 使用 DisplayPoint。Editor 借助 DisplaySnapshot 依次穿过 block、wrap、 tab、fold、inlay 层,最终解析到 MultiBuffer 和底层 Buffer 的 Anchor/offset。
步骤 C:Buffer 产生 operation
language::Buffer 委托 text::Buffer::edit。后者不只修改 Rope,还生成带 Lamport 时间戳的 EditOperation,更新版本向量、undo history 与 Anchor 可解析结构。
源码入口:crates/text/src/text.rs:747-909。
步骤 D:事件向上冒泡
文本 Buffer 触发 operation/edited 事件,language::Buffer 重新安排 Tree-sitter parse, MultiBuffer 汇总底层 edit,DisplayMap 把失效范围逐层变换,Editor 通知 GPUI 重绘。
步骤 E:副作用并行展开
- LSP Store 发送
didChange; - collaboration host 广播
UpdateBuffer; - Git diff 重新计算受影响 hunk;
- edit prediction 更新上下文;
- Agent/diagnostics 观察者可能刷新界面。
关键点是这些消费者不需要把完整文档重新读一遍。它们拿到 operation、版本或局部 patch, 各自进行增量工作。
4. 本地、协作、远程为什么能共用上层 UI
Project 是语义门面
Project 的源码注释直接说明:它负责 tasks、LSP、collab queries,并同步 worktree 状态; 同一个 Project 可以是 local 或 remote。
证据:crates/project/src/project.rs:210-215。
上层调用 project.open_buffer(...)、project.references(...)、project.format(...),不需要 先判断目标是在本机、协作 host 还是 SSH server。
Store 内部分叉
Project 不是把所有分支塞进一个巨大 if remote。它组合多个 Store:
| Store | 本地职责 | 远程职责 |
|---|---|---|
WorktreeStore | 扫描文件系统 | 接收 worktree proto 更新 |
BufferStore | 加载/保存文件 | 创建远端 Buffer 副本、同步 operation |
LspStore | 启动 language server | 转发 LSP request/response |
GitStore | 调用 Git backend | 请求 host 执行 Git 操作 |
TaskStore | 解析并运行任务 | 请求远端终端/任务 |
这是一种“门面稳定、能力下沉、传输可替换”的分布式客户端架构。
5. 规模数字应该怎样解读
按 Rust 文本行数粗略统计,最大的几个领域是:
| crate | 约行数 | 为什么大 |
|---|---|---|
editor | 161k | 显示、输入、选择、补全、LSP UI 与大量测试 |
project | 104k | 文件、LSP、Git、任务、调试、local/remote 双路径 |
agent | 86k | loop、工具、权限、沙箱、持久化与 eval |
agent_ui | 84k | 会话、消息、diff、选择器、审批和历史界面 |
gpui | 75k | 状态运行时、Element、窗口、文本排版和测试设施 |
workspace | 50k | pane、dock、item、持久化与窗口级动作 |
行数不是架构重要性的排名。例如 sum_tree 只有约 3.3k 行,却被 Rope、Buffer、 MultiBuffer、DisplayMap、worktree 等热路径反复复用。
6. Zed 的四个核心设计选择
6.1 用持久化摘要树统一“序列 + 索引”
SumTree 的每个内部节点都保存子树 Summary,并允许同一 Summary 提供多个 Dimension。 Rope 可以按 byte、Point、UTF-16 point 跳转,显示映射也能用同一模式维护输入/输出坐标。
6.2 用 Entity 约束可变状态,用 Snapshot 扩散读取
可变状态留在 GPUI 前台实体;昂贵或异步计算拿不可变快照。这比到处共享锁更容易推理, 也让 UI 的生命周期和异步任务取消更清晰。
6.3 用 operation 表达可合并变化
Operation 既服务 undo/redo,也服务协作复制、远程同步和版本等待。协作不是一条额外路径, 而是文本状态变化本身就具备分布式语义。
6.4 用组合根显式装配产品
crates/zed/src/main.rs 中数十个 init 调用看起来很长,但它让产品能力的注册顺序、全局 依赖和平台分支集中可见。下一章先理解 crate 地图,再进入这个组合根。