← 返回博客

Zed Weekly:#26

2023年11月10日


The current state of Zed under the GPUI2 port
GPUI2 端口下 Zed 的当前状态

Nathan

我们向 GPUI 2 的过渡正在继续,并且取得了良好进展。 编辑器是我们最复杂的 UI 组件之一,几乎已完全移植。 正如预期的那样,新的框架在使用到我们的应用程序中时需要进行一些调整。

本周,我们在处理文本输入和键盘事件方面取得了重大改进。 这是一个草图,说明如何为包含项目列表的菜单视图实现一些简单的键盘处理。 解释如下。

struct Menu {
    items: Vec<SharedString>,
    selected_index: usize,
}
 
actions!(MoveUp, MoveDown);
 
impl Render for Menu {
    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component {
        div()
            .id("menu")
            .focusable()
            .key_context("menu")
            .on_action(Menu::move_up)
            .on_action(Menu::move_down)
            .children(self.items.iter().cloned().enumerate().map(|(ix, item)| {
                div()
                    .when(ix == self.selected_index, |div| {
                        div.bg(cx.theme().colors().selected)
                    })
                    .child(item)
            }))
    }
}
 
impl Menu {
    fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
        if self.selected_index > 0 {
            self.selected_index -= 1;
            cx.notify();
        }
    }
 
    fn move_down(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
        if self.selected_index < self.items.len() - 1 {
            self.selected_index += 1;
            cx.notify();
        }
    }
}

为了使 Menu 结构体成为一个视图,我们在其上实现了 Render。 然后,我们通过声明 “menu” 的 key_context,使此视图能够根据 keymap 匹配按键事件。 这将导致我们匹配 keymap 中需要此上下文的绑定。 然后,我们为 MoveUpMoveDown 操作添加侦听器,这些操作会改变 selected_index 并通知应用程序此视图已更改。

期待分享更多。

Julia

本周早些时候,我一直在追查我们看到的一些问题,即 NPM(我们下载它来安装基于 Node 的语言服务器,例如 TSServer)似乎正在将服务器文件安装到用户的 home 目录下,这时我偶然发现了一个更糟糕的问题。 我们的 tsserver 集成完全坏了,这意味着 JS 和 TS 文件没有自动完成/诊断/代码操作等。

TypeScript 语言服务器本身并不原生支持我们依赖的语言服务器协议 (LSP)。 这是因为 tsserver 实际上早于 LSP 规范本身。 为了避免必须实现 tsserver 所使用的定制协议,我们使用了一个社区项目,该项目包装在 tsserver 周围,充当使用 LSP 的编辑器和使用其自身协议的 tsserver 之间的中介。

使用现有项目的妙处在于,我们不必过多考虑细节,其他人已经在考虑了。 这是代码共享和可重用库的核心原则,每个人都在做自己的事情的网络效应总体上可以更有效率。 然而,为了使系统发挥作用,各个项目必须能够做出必要的改进,从而使整个系统受益。

这正是这里发生的事情; 周三,这个包装器项目标记了一个新版本,其中删除了一组我们遗憾地仍在使用的已弃用的命令行标志。 一旦我弄清楚了这一点,我就能够更新我们的用法,提高我们这方面的一些正确性,并且服务器又恢复了生机!

重要的一点是,每当依赖外部项目和代码时,始终要了解即将发生的更改,尤其是在您无法合理地固定所依赖的组件的版本的情况下。 如果我们更了解发生了什么,我们本可以在弃用成为重大更改之前更新我们的用法。

Kirill

GPUI2 正在发生很多事情,所以我尝试做与它相关的事情看起来并没有什么影响。 所以相反,我决定继续改进我们的错误和功能,因为总得有人做。 有趣的是,在此过程中,我确实发现了一些我可以帮助将旧事物移植到 GPUI2 的地方,我将尝试接下来做这件事。

到目前为止 — 主要有两条工作线

  • Prettier 及其周围的错误 — 现在它更加稳定,错误更少,并且支持 NPM 工作区。 很高兴看到对这些功能的反馈,并知道它现在应该可以帮助很多人。

  • 诊断 UI 元素及其周围的错误 — 我们之前的模型已经优雅地退化了,并且没有为某些语言服务器以“动态”方式返回文件诊断做好充分准备。 我已经修复了最明显的痛点,但还有更多需要处理。

当我在寻找更好的方法来(不)显示 git 排除文件中的诊断信息时,我对如何搜索和索引排除文件有了更多的想法,我正在尝试追求这些想法。

Nate

gpui2 设计+组件磨合和学习 Rust 的磨合都在继续。 我开始感觉我正在掌握这门语言——至少足以让我不必一直找人帮忙,这感觉很棒。

现在更多的人正在加入为 gpui2/zed2 重写 crate 的行列,我们迄今为止构建的组件正在迅速受到压力测试; 我们正在寻找有效的方式,并找到存在差距的地方。

能够为 UI 组件创建故事以隔离测试它们,这确实使迭代它们变得容易。 这是一个故事的例子,其中包含所有可能的玩家颜色

Player Colors
玩家颜色

目前,所有主题都实现了同一组玩家颜色,但这很快就会改变。

这是一个非常粗略的故事工作方式的示例

Player Colors Story Example
玩家颜色故事示例

在这种情况下,“故事组件”主要只是主题颜色,但您可以在那里渲染任何组件。

当我们继续构建主题时,能够调出一个故事来检查事物在特定状态下的外观非常有用。

新主题的开发也在继续进行。 Marshall 在使主题达到我想要的状态方面提供了巨大的帮助。 使用 Rust 的复杂性之一是它对主题的格式/构建主题时必须提供的内容以及它是否可以在编译时可用非常严格。

因此,我们一直在构建一些系统来区分系统主题(完全用 Rust 构建并烘焙到应用程序中)、第三方默认主题(我们导入并动态构建为 Rust 文件以烘焙到应用程序中的外部主题)和最终用户主题(在构建时不可用,需要反序列化并在运行时可用。)

这项工作的一部分促使我们构建了一个正在开发的 VSCode 主题导入器(Marshall 在下面对此进行了更多介绍。)

有很多要谈的,但同样有很多要做的,所以我就说到这里。

我们非常高兴能尽快在新的 gpui 上运行 Zed,并且同样高兴让人们看到我们一直在 gpui 上所做的工作。

下次再见!

Mikayla

我一直在四处奔波,为我理解我们的新 UI 框架做最后的润色,并尝试使用新框架恢复大约 30,000 行的测试和服务器代码。 我还与 max 合作构建了一个自定义元素,用于有效地显示统一元素大小的列表,并且正在努力在旧金山的 Aaron Schwartz Day 举办我的职业生涯的第一次演讲! 下一个大目标:让 Zed 可以作为我们的日常驱动程序使用 :D

Joseph

这周,我开始构建一个AI仪表盘,以便我们可以看到用户与我们内置的AI工具交互的频率。在遥测方面,我也更新了我们的文档,非常明确地说明了我们发送的事件类型以及每个事件中存储的数据。

顺便提一下,我们现在持续看到超过1000个每周活跃安装。

Weekly active installations
每周活跃安装

如果一个安装程序在当天发送了一个编辑器事件,则该安装程序被认为是“活跃的”。如果该安装程序在一周内至少活跃3天,则该周被认为是“活跃的”。

我从Zed的pre-alpha阶段就开始使用它了。那时,可能只有5-10个活跃用户,包括创始人,而Zed看起来像这样

Zed, circa July 5th, 2021
Zed,大约在2021年7月5日

看到新用户冒险尝试不同的东西非常令人兴奋。在Zed成为适用于所有类型开发人员的通用编辑器之前,我们还有很多工作要做,但对于那些一直积极使用Zed,甚至只是偶尔尝试的用户来说,你们的支持对我们意义重大。看到Zed的家庭成长,并见证这个编辑器变得比我们任何一个人都更强大,这真是令人鼓舞。

Conrad

我一直在努力使新的UI框架的各个部件真正将应用程序组合在一起!这周大部分时间都花在了重建用于“转到行”的 ctrl-g。这个组件本身应该只需要几个小时,但是作为第一个使用该框架各个方面的功能的乐趣在于找到所有的“陷阱”,调试它们,并弄清楚我们想如何修复它们。

这包括从简单的疏忽(在当前聚焦的元素上调用“focus”不应崩溃),到测试框架中细微的并发错误(调用析构函数不应导致您的测试死锁!)的所有内容。

我期待着回到在新的和改进的基础上让Zed变得更好。我们将不再像最近的非现场会议上创造的短语那样“以火腿的速度编码”。

Marshall

除了与同事一起进行各种GPUI 2迁移工作之外,我本周还花了一些时间继续改进我们的新主题系统。

为了简要概述我们在GPUI 2中的当前主题管道,我们有一个theme_importer CLI工具,它可以摄取表示为JSON的VS Code主题,并将它们作为表示为Rust源代码的Zed主题发出。然后,这些主题被编译到二进制文件中进行分发。

以前我们发出一个完整的Zed主题,这意味着VS Code主题未指定的颜色值将被默认Zed主题的值替换。虽然这是我们希望在运行时看到的行为,但将默认值复制到每个主题中并不理想,并且很难看出哪些颜色来自主题本身,哪些颜色来自默认主题。

我重做了我们的用户主题,将它们建模为基础主题之上的覆盖层。现在,每个主题只指定它需要从基础主题覆盖哪些值。然后在运行时,我们可以加载一个用户主题,将其应用于基础主题之上,并获得一个完整的Zed主题。这种机制也为加载默认情况下未与Zed一起分发的主题奠定了基础。

我还花了一些时间来简化我们的主题导入过程。

theme_importer依赖于包含主题定义的theme2 crate,但theme2也包含theme_importer发出的主题。这创建了一个循环依赖,其中已发出主题中的编译错误会导致theme_importer编译失败。在迭代theme_importer时,这非常烦人,因此很明显我们需要一个更好的解决方案。

我得出的解决方案是使用Cargo功能来有条件地编译生成的主题。theme2 crate定义了一个importing-themes功能,并且只有在禁用importing-features时才会编译包含所有主题的themes模块

#[cfg(not(feature = "importing-themes"))]
mod themes;

然后在theme_importerCargo.toml中,我们将importing-themes功能设置为指示我们将要导入主题

theme = { package = "theme2", path = "../theme2", features = ["importing-themes"] }

即使themes模块处于无法编译的状态,这也使我们能够继续运行theme_importer