← 返回博客

本周 Zed Industries:#12

本周对于团队而言意义非凡。我们公开发布了预览通道,并关闭了一些排名靠前的功能请求。我们还在继续探索编辑器中的 AI,并致力于通过 Zed 实现 Zed 的开源。

约瑟夫

本周早些时候,我对呼叫仪表盘做了一些优化。其中一些更改包括添加一些图表,按持续时间长度和呼叫开始时间对呼叫进行分类。

我一直想公开发布我们的预览通道,所以写了一篇小博客文章,并帮助调整了一些 zed.dev 代码,以便将其发布。我很高兴能在新功能发布到稳定通道之前获得更多测试覆盖。

最后,本周我还有一些小的任务要处理。其中一项是调整应用内反馈提交机制,以便为用户提供反馈正在发送的确认信息,同时防止后续尝试重新提交相同的反馈。这感觉很有必要,因为我们数据库中重复反馈的频率一直在增加。

米凯拉

上周,我添加了一堆功能,周末我决定也开始着手搜索和替换 :D。我希望 Zed 在开始直播之前所有闪亮的功能都能稳定下来。本周主要是在稳定和调整这些功能并修复错误。

Piotr

上周,我致力于 PHP LSP 集成,并继续进行 tree-sitter 的查找表工作。其 PR 已提交,那边的同事似乎很高兴看到这个变化(或者至少对此很热情)。下周,我计划回到 Zed,进一步完善我们的 UI。

基里尔

我继续致力于终端高亮显示和 Cmd+悬停导航,并发布了第一个迭代版本——我真的很想念 Zed 中的这一点,现在我是一个快乐的用户。很高兴看到人们对小问题提供反馈;希望他们的体验无论如何都得到了改善。

这个基本实现带来的一个巨大可能性是通向外部文件拖放的路径:我们现在拥有处理“尝试打开这个抽象事物”的逻辑,经过测试并准备好接受文件系统路径。

本周剩余的时间,我主要修复了 bug。最值得注意的是键绑定及其在系统和 Zed 菜单中的显示:现在它们可以正确显示用户覆盖。

Antonio

我们很高兴地开发 CRDB,我们设想它将成为我们平台的支柱,并最终开源(在此处查找更多详细信息:/blog/open-sourcing-zed-on-zed)。

最近,我们的注意力集中在探索操作的有向无环图(DAG)表示。使用 DAG 来封装操作使我们能够清楚地了解每个操作的因果历史。这对于无需版本向量即可有效地跟踪副本和分支之间的并发性至关重要。

康拉德

我专注于改进 Vim 模拟。本周的主要功能是搜索及相关命令:/?*#nN 现在都正常工作。Vim 搜索基于 Zed 内置搜索,使其感觉快捷且与其余体验融为一体。我还阅读并整理了所有我能找到的 Vim 相关反馈,并优先安排了下一组工作——敬请期待!

凯尔

本周我们继续推进语义索引。值得注意的是,Max 和我重新设计了我们的 tree-sitter 查询引擎,用于解析用于嵌入的符号对象,以包括在需要时折叠嵌套对象的选项。这大大减少了标记计数,同时保持了大的层次结构上下文,加快了嵌入时间,并减少了冗余搜索结果。除此之外,我们花了一些时间重构了我们的重新索引过程,以允许更细致的重新索引作业,并在重新设计的项目搜索 UI 中添加了语义搜索作为选项。

茱莉亚 (Julia)

除了查看各种崩溃问题之外,我本周处理的一件有趣的事情是将 Ctrl-点击视为右键单击。作为 macOS 应用程序的标准功能,我们从未注意到我们不支持它,因为团队中所有使用触控板的人都使用双指点击。这带来了一个有趣的难题,因为 Zed 最终旨在成为一个跨平台应用程序;Windows 和 Linux 都没有类似的约定。这意味着我们可能不想在应用程序代码中处理它,因为通常最好将平台特定的问题保留在平台层内,从而使核心应用程序代码与平台无关。因此,我进入了我们的 macOS 平台事件代码,并过滤掉了按下 Control 键的鼠标左键按下事件。现在,每当操作系统向我们发送此类事件时,我们都会将其替换为鼠标右键按下,然后是鼠标右键抬起,从而使核心应用程序代码只看到正常的右键单击。

马克斯

本周,当我不与 Kyle 合作进行语义搜索时,我处理了一个有趣的 bug,该 bug 在非常大的 git 仓库(例如 Webkit)中打开 Zed 时出现,其中 git status 运行时间很长。在这样的仓库中,缓慢的 git status 调用导致我们的文件系统扫描(通常在每个 CPU 核心上并行运行)由于某些锁争用而以单线程方式运行,这导致项目文件加载非常缓慢。我仍在研究如何以及何时检索文件的 git 状态,以避免在大型仓库中出现此问题。

Derek

本周,我主要与 Nathan 合作进行新的 GPUI 更新,并与 Mikayla 合作进行一些 UI 更新。我为文件图标和其他各种 UI 图标需求创建了初始图标集,并更新了新的搜索 UI 以支持大家正在开发的其他功能。敬请期待该 UI 在不久的将来登陆应用。

内森

我与 Derek 进一步合作,改进了我们的 UI 布局和样式方法,但我的大部分工程相关时间都与 Antonio 合作开发 CRDB。

一个值得分享的有趣方面是我们在网络和消息方面采取的方法。目前,Zed 中的协作依赖于 Protocol Buffers 处理所有网络消息,但维护外部 schema 在很多方面都有些笨拙。对于 CRDB,我很好奇我们是否能找到一种纯 Rust 的方法。因此,我们正在尝试一个名为 serde-bare 的 crate,它是 Bare Messages 的实现,这是一种紧凑的二进制序列化格式,允许我们的消息类型完全在 Rust 中定义。

我们有两种消息:一种将通过 LiveKit 数据通道广播,以最大程度地减少与协作者共享编辑时的延迟。另一种是请求,将发送到我们的服务器进行持久化。我们使用不同的信封枚举来包装每种消息,如下所示。

// These are sent to our server
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum RequestEnvelope {
    PublishRepo(PublishRepo),
    CloneRepo(CloneRepo),
    SyncRepo(SyncRepo),
    PublishOperations(PublishOperations),
}
 
// These are sent via LiveKit data channels.
// Currently we only implement one type of message, but this could expand.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum MessageEnvelope {
    Operation(Operation),
}

在每个枚举上,我们都实现了一个 unwrap 方法,该方法返回一个包含其内容的 Box<dyn Any>。然后,我们可以基于每个消息的 TypeId 使用动态调度将消息路由到适当的处理程序。反之,每个消息都实现 Into<RequestEnvelope>Into<MessageEnvelope>。大概我们可以用宏来覆盖这些。

网络问题解决后,我们实现了同步。这基于向量时钟,它将根据为存储库创建的副本总数进行扩展。每次操作都发送这些成本太高,但它们对于同步非常有用,同步本质上是两个副本所包含的操作集之间的分布式集合并集。与服务器同步时,客户端发送一个表示它所见的所有内容的向量时钟。然后服务器发回它拥有的客户端上不存在的任何操作,以及它自己的表示服务器拥有的内容的向量时钟。然后客户端跟进发送服务器缺少的内容。

同步完成后,我们开始应用操作,这很有趣。每个操作都关联一个父 RevisionId,它表示应用该操作的仓库版本。RevisionId 包含一个或多个并发的 OperationId。要应用操作,我们需要检索和/或构造一个匹配此 id 的版本。如果我们已经有一个匹配该 id 的缓存版本,我们可以直接使用它。如果没有,我们向后遍历 DAG,找到一个表示修订 id 中列出的所有操作的共同祖先的版本,该修订 id 包含我们缓存的快照,然后向前应用操作以重建它。

最初,我们将维护每个状态的快照,因此找到共同祖先应该非常便宜。随着时间的推移,我们将垃圾回收旧的快照,因此重建更早的状态可能会变得更昂贵,但这无论如何都是一个粗粒度的操作。

我们认为我们已经有了一个可用的版本重建,我们目前正在实现一个基于它的编辑操作。遍历和构建写时复制 B 树的乐趣时光。

感谢阅读!


正在寻找更好的编辑器吗?

您今天就可以在 macOS、Windows 或 Linux 上试用 Zed。立即下载


我们正在招聘!

如果您对我们博客中涵盖的主题充满热情,请考虑加入我们的团队,帮助我们实现软件开发的未来。