Skip to content

第 1 章:全景——Zed 到底是什么

一句话定义:Zed 是一个以 GPUI 实体运行时为外壳、以可快照化协同文本模型为内核、 以 Project 抽象承接本地与分布式工程能力的原生桌面开发环境。

1. 先纠正三个直觉

1.1 它不只是“Rust 写的编辑器”

如果只看到 Rust 和 GPU,会漏掉 Zed 更关键的结构:文本从一开始就能产生可复制的操作, Project 从一开始就允许 local/remote 两种实现,UI 状态从一开始就被 GPUI 的实体和事件管理。

也就是说,协作、远程和 AI 不是后来挂在单机编辑器上的三块功能,而是不断复用既有内核:

text
                    ┌──────────────┐
用户动作 / 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 不是直接编辑文件字符串

最短的数据链也至少经过:

text
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_platformgpui_macosgpui_linuxgpui_windowsgpui_wgpugpui

职责是接收 OS 事件、维护窗口、排版文字、组织场景并提交 GPU。平台差异被压在底层, 上层只面对 ApplicationWindowElementScene

第 2 层:基础数据结构

代表 crate:sum_treeropetextclockcollections

这里解决大文档增量编辑、多个坐标维度快速跳转、持久化快照、操作排序和逻辑位置稳定性。 这是 Zed “快”与“可协作”的共同地基。

第 3 层:编辑语义

代表 crate:languagemulti_buffereditorbuffer_diffmarkdown

language::Buffer 把纯文本提升成源码文件;MultiBuffer 把多个摘录合成可编辑文档; Editor 管选择、命令、补全和显示状态。

第 4 层:工程模型

代表 crate:fsworktreeprojectlspgittaskterminaldap

这里把文件树、打开的 Buffer、语言服务器、Git 仓库、任务、终端和调试会话组合成一个 可本地也可远程的 Project。

第 5 层:产品工作区

代表 crate:workspaceuiproject_panelgit_uiterminal_viewsettings_uitheme

Workspace 对应一个窗口中的完整产品状态:中心 pane group、三个 dock、status bar、 Project 以及所有可打开 item。

第 6 层:分布式与智能

代表 crate:clientrpcprotocollabremoteagentagent_uilanguage_modelextension_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约行数为什么大
editor161k显示、输入、选择、补全、LSP UI 与大量测试
project104k文件、LSP、Git、任务、调试、local/remote 双路径
agent86kloop、工具、权限、沙箱、持久化与 eval
agent_ui84k会话、消息、diff、选择器、审批和历史界面
gpui75k状态运行时、Element、窗口、文本排版和测试设施
workspace50kpane、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 地图,再进入这个组合根。

独立源码学习笔记 · 文档采用 CC BY-SA 4.0