过去几周过得跌宕起伏,我们一直在深入重写 Zed 的 UI 框架,并将整个应用程序升级到新的基础。
内森
为什么要现在做?毕竟我们不应该添加对 X、Y、Z 的支持吗?这被称为软件“开发”是有原因的。一个只会增长的代码库无法真正发展,有时变革是必要的。在这种情况下,我们希望在 Zed 开源之前完成这场变革。
GPUI 2 的出现着实令人兴奋。GPUI 的第一个版本使 Zed 成为您今天使用的产品,我们为此感到非常自豪。GPUI 1 的优点是 Zed 成为今天这样子的重要原因。但 GPUI 1 的缺点也阻碍了 Zed 的发展。
GPUI 2 延续了第一个版本的性能和可靠性优势,同时彻底改善了阻碍我们前进的人机工程学问题。Zed 的任何贡献者都需要学习这个框架,因此在人机工程学方面的投资回报很高。
我将分享一些核心理念。
在 GPUI 中,所有应用程序状态都由一个名为 AppContext 的单一对象拥有。模型是状态的一种。给定一个 AppContext,您可以按如下方式创建模型:
let cx: &mut AppContext = todo!("keep reading");
cx.new_model(|cx| Document {
path: "/journal/2023/11/3.md".into(),
text: "I'm tired, but also inspired!"
})将其封装在一个构造函数中可能会很有帮助,如下所示:
struct Document {
pub fn new(path: Option<SharedPath>, cx: &mut AppContext) -> Model<Document> {
cx.new_model(|cx| {
let text = Rope::new(cx);
text.subscribe(cx, |this, text, edit: &Patch<u32>, cx| {
for mention in self.scan_mentions(text, edit, cx) {
cx.toast(mention);
}
});
Self { path, text }
)
}
}在上述代码中,当我们创建一个文档时,我们创建一个 Rope 来存储其文本,它也是一个模型,具有一个类似的接受上下文的构造函数。然后我们订阅文本上的编辑事件,这些事件以“补丁”的形式表示。我们调用 scan_mentions 方法,然后如果编辑包含新的提及,则向用户显示一个提示。
Model<Document> 在某些方面类似于 Rust 标准库中的引用类型,例如 Box 或 Rc。不同之处在于模型存储在 AppContext 中,如果仔细观察,它有点像应用程序特定的堆。要解引用 Model<T>,您需要传递一个拥有其状态的上下文。
当模型被构建或更新时,它们可以与此上下文交互,以发出和订阅事件,通知其他模型它们已更改,以及访问其他应用程序范围的 API。
除了 Model<T>,还有 View<T>。视图包装模型,但它们强制 T 实现 Render 特征,该特征有一个单一方法,将 T 中的状态映射到元素树。
pub struct View<V: Render> {
pub(crate) model: Model<V>,
}
pub trait Render: 'static + Sized {
type Element: Element<Self> + 'static;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element;
}实际上,render 实际上需要返回任何 impl Component。一个 Component 是任何可以转换为元素树的类型。它也有一个 render 方法,但它移动自身而不是借用自身。
/// The core stateless component trait, simply rendering an element tree
pub trait Component {
fn render<V: 'static>(self, cx: &mut ViewContext<V>) -> AnyElement<V>;
}当特性中的 impl Trait 稳定版发布(Rust 1.75.0)时,Render 特性将看起来像这样:
pub trait Render: 'static + Sized {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component;
}😽🍝 感谢 Rust 语言开发者。
最后,这里是一个完整但最小的 GPUI 2 应用程序示例。
struct Hello {
user: SharedString,
}
impl Render for Hello {
type Element = Div<Self>;
//
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let color = cx.theme().colors();
div()
// Flex properties
.flex()
.flex_col()
.gap_2()
// Size properties
.w_96()
.p_4()
// Set width to 384px
// Add 16px of padding on all sides
// Color properties
.bg(color.surface)
// Set background color
.text_color(color.text) // Set text color
// Border properties
.rounded_md()
// Add 4px of border radius
.border()
// Add a 1px border
.border_color(color.border)
.child(format!("Hello, {}!", self.user))
}
}
impl Hello {
fn new(cx: &mut WindowContext) -> View<Self> {
cx.build_view(|_| Hello {
user: "world".into(),
})
}
}
fn main2() {
gpui2::App::production(Arc::new(Assets)).run(|cx| {
settings::init(cx);
theme::init(cx);
cx.open_window(WindowOptions::default(), |cx| Hello::new(cx));
});
}这次过渡非常紧张。有点像航空母舰全速右转。我们认为这将是值得的。
Marshall 和 Mikayla 的调度将随后进行,但每个人都投入很深,所以我们其他人将在下周跟进!
马歇尔
延续共同的主题,我本周一直在帮助推进 GPUI2 重写。这让我接触到了代码库的各个角落,也是我第一次在 GPUI2 和我们的 UI/storybook 代码之外接触 Zed。看到使用我们新的 UI 组件在真实的 Zed 工作区中渲染出第一个像素真是太棒了。
我重点关注的一个具体领域是与 Nate 一起构建新的主题系统。我们现在有了基于颜色比例构建主题的结构,可以轻松构建视觉一致的主题。最终,这个新的主题系统将使得在 Zed 中暴露更多自定义选项成为可能,而无需从头开始构建整个主题。
另外,我很高兴上周有机会参加 Zed 峰会。我很高兴能与团队的其他成员面对面交流,并一起开发 Zed 一整周!
米凯拉
过去几周过得真是疯狂!我们上周都参加了峰会,并着手进行了我们迄今为止最具雄心的项目之一:在我们开源 Zed 之前,用我们一直称之为“GPUI2”的技术重写整个应用程序。GPUI2 是我们内部 UI 框架的重写,旨在使我们能够编写可组合的 UI 代码,以便我们真正能够使用。我们拥有新的布局引擎、一个时髦的新执行器以及基于 tailwind CSS 库的一堆样式辅助工具。
对我来说,我一直专注于学习新系统,并帮助构建 GPUI2 所需但尚未连接的功能。这是一段疯狂的旅程,因为几乎每天都会发现 GPUI2 的问题,而我们的应用程序又依赖于这些问题。Rust 的类型系统使这种重构成为可能,我们都花了很多时间重命名函数并替换新的类型。
在我们的依赖图中,有几个大型 crate 处于高位:
project,它连接了我们的核心应用程序代码workspace,它将项目与我们所有其他 UI 结合在一起editor,它为 Zed 中的每一个文本输入提供支持。
由于这些瓶颈,重新输入 Zed 的同步性远高于我们所希望的,这对一个大部分时间远程工作的 10 人团队来说是个坏消息。值得庆幸的是,我们每天都使用这个很棒的同步协作工具:Zed!在峰会上,我们有一些令人难以置信的时刻,4 个人围坐在一张会议桌旁,都从自己的笔记本电脑上操作一个人的机器,他们流畅地分开和重新加入,以便在构建图的不同部分可用时进行工作。
自峰会以来,这种做法仍在继续,有 2 或 3 个团队在力所能及的范围内并行工作。如果没有 Zed 的同步编程能力和 Rust 类型系统的指导,这将是一段更加艰难的旅程。
现在,重写的第二周,我们已经完成了大约 1/3 到 1/2 的应用程序,并进入了大规模并行化的部分,希望我们很快就能发布 Zed 的新预览版!
Nathan 再次发文
感谢各位阅读,也感谢大家在这几周里,除了我们的代码,还和我们一起移动了我们的“原子”。很快,我们将更好地了解彼此。🙇🏻♂️✌️