欢迎来到第十五期《本周 Zed Industries》!时光飞逝。随着夏季进入尾声,我们正在逼近内部 Channels Alpha,进一步调整性能,并添加了一些新的文本操作命令。让我们深入了解一下!
Piotr
本周我专注于对项目和缓冲区搜索进行进一步的样式调整。为此,我还与 Julia 合作,调查了项目搜索的各种性能怪癖。我们发现的一个显著怪癖是,在搜索过程中偶尔会出现短暂的卡顿,伴随着“正在搜索...”的标签持续几秒钟。正如我们发现的,Zed 只有在完全完成搜索后才会显示搜索结果。天哪!在过去的一周里,我们重写了项目搜索例程的本地部分,以便在获得搜索结果后立即报告。下周我计划继续与 Julia 共同研究我们的搜索实现,并利用新获得的见解加快其他类型的搜索。同时优化代码和社交真是太棒了!
茱莉亚 (Julia)
我本周大部分时间都花在继续尝试 WASM 可扩展性上,目的是启用自定义语言服务器。我们仍然不清楚要如何推进,但我们正在学习很多东西,并就这个话题进行了大量的内部讨论。
我的其余时间都和 Piotr 一起致力于加快我们的项目搜索,这非常有趣且令人满足。我一直很喜欢改进性能的工作,并且在此过程中更好地了解了我的同事,这很棒。
约瑟夫
过去几周我很高兴能多写点代码。在 Zed 中,我一直专注于添加行和文本操作命令,因为自从全职切换到 Zed 后,我就一直很怀念这些命令。周末,我通过添加大小写转换(转换为 kebab case、转换为 upper camel case 等)来完善文本操作命令。我对我们目前的文本操作命令感到满意——这是一个快速演示
我花了一些时间重新编写了我们的热门问题脚本,因为它的原始编写方式效率非常低,并且使用的请求数量与跟踪器中的问题数量呈线性增长;由于我们达到了 GitHub 的速率限制,脚本经常失败。现在,该脚本只需几秒钟即可运行,并且应该消耗恒定数量的请求。
最后,我开始开发一个实验性工具,一个 TUI 应用程序,用于将来自各种渠道的所有反馈集中到一个位置。几个月前我们也开发过一个类似的工具,但由于其基于网络的性质导致各种问题而被放弃了。有机会用 Python 工作总是很有趣的。
基里尔
这周中旬我休假回来了,所以这次没什么可分享的。
首先,我决定暂时搁置文件拖放的尝试:终端中的 cmd-click 已经提供了 80% 的功能,而且在我离开期间,一些任务变得更加重要。
说到其中一个,内联提示的性能并没有达到预期的可扩展性——Zed 整体的流畅度将标准设定得很高。我正在研究最初的想法之一,更严格地跟踪我们查询提示的编辑器范围。这让我在考虑未完成的提示实现博文时感觉好一点:有些事情肯定会彻底改变。
我还添加了一个用于切换提示和缓冲区搜索的小面板,这应该有助于功能发现和这些切换的快捷键。鉴于提示方面又做了大量工作,我开始思考在有动力的时候,如何以“简单”的方式实现动态提示功能...
内特
今天的简短更新——上周我刚休假一周回来。这周大部分时间都在和 Mikayla 一起研究频道,深入了解实现细节,并理解如何让通用组件更好地适应 Rust 的细微差别。此外,我还支持了 Nathan 的主题探索,以及其他一些杂项。
米凯拉
本周,我们来谈谈 Channels 安全模型以及我在撰写上周文章时发现的一个严重权限提升漏洞。这个系统中有一个额外的规则,我曾认为它是如此不言而喻,以至于我忘记提及:当你创建一个新的根 Channel 时,你会自动获得该 Channel 的管理员角色。为了说明这个问题,请看我给出的个人重组示例,并标注了我的成员角色
- #my-freelance-design-business - (Admin)
- > #crdb-industries/#design - (Member)
- > #zed/#design - (Member)
借助级联角色系统,我在我自己的私人组织中的管理员角色覆盖了 #crdb-industries 和 #zed 授予我的成员角色,这是一个微不足道且无法击败的权限提升漏洞。
为了纠正这个明显的错误,我匆忙地在句子发布后立即添加了“with subtractive interference”的字样。

但这个修复却制造了完全相同的问题,只是方向相反!如果我将我的一个同事添加为 #my-freelance-design-business 的成员,他们就会失去对他们自己组织的频道拥有的管理员角色!
这里根本的问题在于,我们正在使用的安全模型,即访问控制列表(ACL),无法以我们所需的方式组合在一起,以允许我上周描述的那种行为。值得庆幸的是,存在另一种模型可以做到:对象能力(OCaps)。自从我接触到 Spritely Institute 及其基于 Lisp 的 Goblins 框架以来,我就一直对 OCaps 感到兴奋。如果您想深入了解对象能力并在此过程中学习一些 Lisp,他们的 Heart of Spritely 论文有详细介绍。
就我们的目的而言,对象能力归结为一个简单的规则:访问即授权。如果你有权访问某个东西,那么你就可以做该访问允许你做的任何事情。请注意权限记录所在的位置,它不在个人身上,也不在服务器上,而是在**访问本身**。现在,我上面描述的问题就迎刃而解了。与其使用角色,不如想象一下可以在频道上执行的操作,读取和写入,它们对应于上面的成员和管理员角色,然后我们取消级联。现在我的频道看起来是这样的
- #my-freelance-design-business - (Write)
- > #crdb-industries/#design - (Read)
- > #zed/#design - (Read)
问题迎刃而解了。我只对 #crdb-industries/#design 拥有读取权限,我对我的个人频道的写入权限完全独立于其子频道。太棒了!但这个解决方案有一个主要的缺点:我们不这样思考。
Zed 致力于将用户体验放在首位。我们努力消除你的意愿和计算机操作之间的障碍。人类世界目前是按照成员资格和角色来组织的。无论这是否是一件好事,人们都会以“我想让 Mikayla 成为 Zed 的成员”这样的词语来思考,我们需要让表达这一点变得简单。那么,为什么不同时使用 ACL 和 OCaps 呢?
如果我们要使用 ACL,我们就必须恢复级联成员角色。在我们这个用例中,成为一个频道的成员却不是其子频道的成员,这没有道理。但为了解决权限提升问题,我们可以在 DAG 的每个**边**上添加一种新的规则,在边创建时选择:Inherits up to _。让我们用这个新规则重新绘制我们的示例,这次在 FigJam 中,因为内容更多。

在此设置中,边的继承规则充当级联角色的过滤器,而不是权限本身(纯 OCap 解决方案)。这解决了权限提升攻击,因为我在我自己的频道上的管理员角色在沿 #my-freelance-design-business -> #design 边传递时转换为简单的成员角色。将 #zed -> #design 边标记为 Inherits up to Admin 也解决了窃取我同事管理员权限的反向问题。在 DAG 中存在一条路径,其中 Nathan 的管理员角色未被过滤掉,因此他是 #design 的管理员。他在 Mikayla 频道中的成员资格不会干扰他在 Zed 的管理员角色。
更棒的是,我们也可以用这个系统模拟**安全的**权限提升。假设我在业余时间成为了 Rust Analyzer 的核心贡献者,并且我们有一个这样的频道设置

由于 Rust Analyzer 和 Zed 共同管理一个频道,并且我在 #rust-analyzer 中拥有管理员角色,这些规则允许我的角色仅在 #zed-rust-analyzer 中升级为管理员。由于成员资格是向下级联,而不是向上级联,我在所有其他 #zed 频道中的成员角色得以维持,正如它应该的那样。
现在我们可以表达我们都习惯的简单、以人为本的成员角色,而无需牺牲灵活性和表达能力,也无需打开微不足道的安全漏洞。🎉🎉🎉
希望我们下周能开始对 Channels 功能进行内部测试。我很高兴看到它在实践中如何运作!特别感谢 Fission 的 Quinn Wilton,在上次我们讨论权限问题时与我一起完成了这个综合方案。
内森
对我来说是激动人心的一周。
今天我整理了这份插件提案,并与 Julia 分享了它。一旦我们让基础功能运行起来,我很乐意做一些宏功能,使 Rust 对象作为托管对象在 V8 中暴露出来变得相当容易。
我在提高 Zed 样式设置效率方面也取得了进展。动态加载的效果不是特别好,但我最终选择了一个编译速度非常快的沙盒,将 UI 更新的反馈时间缩短到几秒钟。我们最终可以做得更好,但相比之下,这个循环已经相当可用了。下一步是允许在运行时调整我们最常见元素的样式。结合打印表示,我们有可能相当高效地将其往返于代码。所有这一切都是因为我们需要 Zed 成为设计师编写代码的地方。我认为这对每个人来说都更高效。
展望未来,我希望尝试将 Zed 的主题建立在 Rosé Pine 系列的基础上。这是他们的模式,但我们用领域特定、色相中性的名称替换了他们可爱命名的颜色
pub struct ThemeColors {
pub base: Range<Hsla>,
pub surface: Range<Hsla>,
pub overlay: Range<Hsla>,
pub muted: Range<Hsla>,
pub subtle: Range<Hsla>,
pub text: Range<Hsla>,
pub highlight_low: Range<Hsla>,
pub highlight_med: Range<Hsla>,
pub highlight_high: Range<Hsla>,
pub success: Range<Hsla>,
pub warning: Range<Hsla>,
pub error: Range<Hsla>,
pub inserted: Range<Hsla>,
pub deleted: Range<Hsla>,
pub modified: Range<Hsla>,
}这些范围是两种颜色,可以在 0.0 到 1.0 之间采样,在 HSL 颜色空间中,在给定范围的起点和终点之间进行线性插值。
我目前采样的值有点随意,但这段代码能让你体会到它的精髓。值得注意的是,组件对于一个类型 V 是通用的,该类型 V 包含跨多个帧存在的数据。
impl<V> Playground<V> {
pub fn new() -> Self {
Self(PhantomData)
}
pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> impl Element<V> {
workspace(&rose_pine::dawn())
}
}
fn workspace<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
column()
.size(auto())
.fill(theme.base(0.5))
.text_color(theme.text(0.5))
.child(title_bar(theme))
.child(stage(theme))
.child(status_bar(theme))
}
fn title_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
row()
.fill(theme.base(0.2))
.justify(0.)
.width(auto())
.child(text("Zed Playground"))
}
fn stage<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
row().fill(theme.surface(0.9))
}
fn status_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
row().fill(theme.surface(0.1))
}我计划探索 taffy 作为布局模型,因此上述某些语法可能会更改,以更强地与它所模仿的 Web 概念保持一致。熟悉度是好事。我们只需要速度。