“本文并非 Zed 编辑器的广告,而是想探索 Zed 团队背后的商业动机与技术栈。不要因为本文而引起编辑器之争!
Zed[1] 是一款由 Atom[2] 和 Tree-sitter[3] 的创造者开发的高性能多人代码编辑器。
“Atom,是 GitHub 出品的一款文本编辑器。Tree-sitter 是 Rust 实现的一款增量式解析器。
Zed 的口号是 「以思维的速度编写代码」,这句口号至少传达出了下面几个理念:
Zed 目前的特点就是性能好,无论是插入延迟、启动时间还是内存占用,都是同类编译器和 IDE 中占用时空资源最少的。
Zed 提供了一些其他编辑器目前还没有的现代化开发理念:
总的来说,Zed 目前的给我体验和该口号比较吻合,突出了其旨在提供无缝、高效编程体验的目标。Zed 在经历三年的开发之后,于昨日开源,并且已经发布了稳定版本供下载。
Zed 的基本理念始于 2018 年 GitHub 内部的一个名为 Xray 的项目。
Xray[4] 是 GitHub Atom 团队之前立项开发的一个基于 Electron 的文本编辑器,因为 Atom 编辑器推出后性能方面一直被社区和用户诟病,其中在加载大文件的情况下,性能问题尤为明显,因此 Atom 开发团队希望通过 WebGL 将界面这块进行重新实现。但是他们不希望抛弃 Electron,因为他们相信 Electron 还是开发跨平台可扩展界面最优秀的技术平台。并且 Xray 开始选择 Rust 来实现。
然而 Xray 在 2018 年底就因为复杂性太高而无法持续,项目停滞。
Xray 的两位主创 Nathan 和 Antonio 在 2019 年到 2020 年之间就开始重新实现他们的新的编辑器,就是 Zed 和 GPUI(Zed 内部使用的 GUI 库) 的雏形。
2020 年 Nathan 收到了 Warp[5] (Rust 实现的终端) 的一份工作邀请,但是他在 Warp 待了三个月就离职了,因为他始终无法放弃构建终极编辑器的梦想。
因此,他又集结了Antonio 和另一位开发者 Max ,三人一起从 2021 年开始全职进行 Zed 开发。
到 2022 年 3 月,Zed 团队终止闭门造车,开始建立 Alpha 公测社区,逐步扩大用户圈,为公测做准备。这个策略现在看来还是不错的,这也是开源商业社区值得学习的一种策略。不是等产品完全开发完成再面向用户,而是让用户的试用来促进开发。现在 Zed 编辑器开源了,Zed 社区又将引入新的一批用户。
Zed 编辑器的开源许可证说明:
Zed 使用 Channels 来运行一个名为 Fireside Hacks 的新项目,在这个项目中,全球的开发者都可以在一个公共频道中与出现的任何人一起实时开发 Zed。
私以为,Zed Channel 可能为开源社区贡献带来一种全新的方式:开源项目的维护者可以通过 Zen 编辑器与新的项目贡献者实时交流与指导。
也许和创始人在 Wrap 公司工作经历有关,Zed 的商业模式和 Wrap 类似:个人免费和增量付费,团队收费。将来也可能会推出企业专用产品。
Zed 的 Channel 功能将来会对个人也收费(测试期间是完全免费的),但我期望他们的费用不要太贵,因为它可能为开源社区贡献带来一种全新的方式。不要因为付费太贵而断送了这个可能性。
Zed 也提供 AI 功能,这个也是增量付费的一种方式。悄悄说:Wrap AI 是免费的。
Zed 也承诺永远不会在用户的代码编辑器中显示横幅广告。
这些都是 Zed 团队当前对商业模式的设想,希望 Zed 可以做到更好,毕竟用爱发电是不可能长久的。
Zed 性能强劲和支持多人协作的秘密就是它使用下面技术栈:
利用 Rust 支持的安全并发,支持在多核上并行处理工作,而不会影响应用程序的稳定性。基于 Rust 异步,将 CPU 密集型任务从主线程中移出,从而实现了在单线程编辑器中无法实现的响应性。
CRDTs(Conflict-Free Replicated Data Types,无冲突复制数据类型)是一种特殊的数据结构,用于在分布式系统中同步和存储数据。在这样的系统中,多个副本可以独立更新,随后这些更新会被合并,以确保所有副本最终一致。CRDTs 的关键特点是它们的设计允许在不同节点上并行和独立地进行操作,而不需要立即进行中央协调或锁定。
CRDTs 的核心特征:
CRDTs 主要分为两类:
CRDTs 的应用场景
在 Zed 编辑器中,每一个缓冲区(编辑器用户打开文件内容的内存区域)就是一个 CRDTs 结构。使用 CRDTs 是为了支持多用户实时协作编辑功能。通过 CRDTs,编辑器能够确保即使在网络条件不稳定的情况下,多个用户的更改也可以无冲突地被合并,保持文档的一致性和完整性。这使得团队成员可以同时在同一个文档上工作,无需担心编辑冲突或数据丢失。
Tree-sitter 是 Zed 另一位联合创始人的作品,也是开发了很多年。它是专门为编写编辑器而实现的解析器。因为源代码经常变化,需要快速重新解析,并且正在编写中的代码也可能是不完整的,但仍然需要解析其中正确的部分。其他解析器在这种场景下的性能不足以支持 Zed 的理想。
Tree-sitter 使用上下文无关文法的精确形式来解析代码,采用了一种称为广义 LR 解析(或 GLR)的算法,几乎支持任何编程语言的文法。并且支持增量解析来允许在编辑后进行高效的重新解析,并且具有一种新颖的错误恢复技术,即使文件处于无效状态,解析器也能产生有用的结果。
延伸知识:
Zed 中 使用 Sum Tree 来存储解析后产生的抽象语法树(AST)。在 Zed 中也广泛使用它来存储有序集合。
"Sum tree"(和树)是一种特殊类型的数据结构,具有 B-tree(B树)和copy-on-write(写时复制)的特性。这种数据结构通常用于高效地管理可变数据,特别是在需要优化读取和写入性能的场景中。
Sum Tree 工作原理:
应用场景
现代显示器的刷新率范围从 60 到 120 帧每秒,这意味着一个应用程序每帧只有8.33毫秒的时间将像素推送到屏幕。这包括更新应用程序状态,布局UI元素,最后将数据写入帧缓冲区。
对于编辑器的性能来说,占领这 8.33 毫秒就意味着带给用户丝滑般的响应。因此,Zed 决定自己研发 GUI 框架,整体思路是,将需要渲染 Zed 的用户界面的每个特定图形原语编写自定义着色器。通过在 CPU 上以数据驱动的方式描述每个原语的属性,将所有繁重的工作委托给 GPU,在那里并行绘制 UI 元素。
当前开源的 Zed GPUI 已经是经过了一次大的重构的第二版。在第一版 GPUI 中使用的是类似 Flutter 的布局。而重构以后使用的是更加灵活的 Flexbox 弹性布局。使用 的是 Rust 布局库 DioxusLabs/taffy[6] 。
目前 Zed 仅支持 Mac 的版的原因是,当前底层 GPU 平台只支持 Metal 平台,基于 gfx-rs/metal-rs[7] 的库来操作 Metal 3D Graphics API。但是从源码实现来看,Zed 在架构上已经做好了跨平台抽象,但是估计 Windows 和 Linux 相关的支持不会很快,因为具体平台实现的代码还没有开始写。
Zed 团队在编写 GPUI 的过程中最大的挑战就是 Rust 的所有权。如何用 Rust 的特性来动态性来表达真实世界的图形界面,是 Zed 团队的一大挑战。
Zed 团队当前探索出来的方法:在 GPUI 中,应用程序中的每个模型或视图实际上都由一个称为 AppContext
的顶层对象拥有。当创建一个新的模型或视图(统称为实体)时,将应用程序的所有权交给它,以使其能够参与各种应用服务并与其他实体进行交互。
use gpui::{prelude::*, App, AppContext, Model};
struct Counter {
count: usize,
}
fn main() {
App::new().run(|cx: &mut AppContext| {
let counter: Model<Counter> = cx.new_model(|_cx| Counter { count: 0 });
// Call `update` to access the model's state.
counter.update(
cx,
|counter: &mut Counter, cx: &mut ModelContext<Counter>|
{
counter.count += 1;
}
);
// ...
});
}
为了说明,考虑上面的简单示例代码。通过调用 run
来启动应用程序,并传递一个回调函数(闭包),该函数接收一个对 AppContext
的引用,该 AppContext
拥有应用程序的所有状态。这个 AppContext
是访问所有应用程序级别服务的入口,比如打开窗口、显示对话框等。它还有一个 new_model
方法,在下面调用它来创建一个模型并将其所有权交给应用程序。
调用 new_model
返回一个模型句柄,该句柄携带基于所引用对象类型的类型参数。单独使用这个 Model<Counter>
句柄无法访问模型的状态。它只是一个惰性的标识符加上一个编译时类型标签,并且它维护着对由应用程序拥有的底层 Counter
对象的引用计数。
为了更新计数器,调用 update
,传递上下文引用和回调函数。回调函数被提供了对计数器的可变引用,就可以使用它来操作状态。
GPUI 在内部也实现了观察者模式和订阅模式来进行状态更改和事件分发。关于更多GPUI 内部状态管理的内容可以查看最新的官方博客:gpui-ownership[8] 。后续官方团队还会有更多实现细节披露。
这里就不比较优劣了。