← 返回博客

Zed Industries 本周动态:#15

2023 年 8 月 11 日


欢迎来到第十五期Zed Industries 本周动态!时间过得真快。随着夏季接近尾声,我们正接近内部 Channels Alpha 版本,进一步调整性能,并添加一些新的文本操作命令。让我们开始吧!

Piotr

本周,我专注于对项目和缓冲区搜索进行进一步的样式调整。另外,我还与 Julia 合作,调查项目搜索的各种性能怪癖。我们发现的一个值得注意的怪癖是在搜索过程中偶尔会出现短暂的锁定,并伴随一个“正在搜索...”标签,持续几秒钟。正如我们发现的那样,Zed 只有在完全完成搜索后才会显示搜索结果。哎呀!在过去的一周里,我们重写了项目搜索例程的本地部分,以便在获得搜索结果后立即报告。下周我计划继续与 Julia 一起研究我们的搜索实现,并根据新获得的见解加速其他类型的搜索。同时优化代码和社交真是太棒了!

Julia

我本周的大部分时间都在继续我们对 WASM 可扩展性的实验,目的是启用自定义语言服务器。我们仍然不清楚我们希望如何前进,但我们正在学习很多东西,并且正在就该主题进行大量的内部讨论。

剩下的时间与 Piotr 合作,加快了我们的项目搜索速度,这非常有趣和令人满意。我总是喜欢致力于提高性能,并且很高兴在此过程中更好地了解我的同事。

Joseph

在过去的几周里,我很高兴能够多写一些代码。在 Zed 中,我一直专注于添加行和文本操作命令,因为自从我全职切换到 Zed 以来,我一直很怀念这些命令。周末,我通过添加大小写转换(转换为烤串式大小写转换为大驼峰式大小写等)来完善文本操作命令。我对我们现在所处的文本操作命令感到满意 - 这是一个快速演示

各种文本操作命令的演示

我花了一些时间来重构我们的排名最高的 issue 脚本,因为它最初的编写方式效率非常低,并且使用的请求数量与跟踪器中的 issue 数量成线性增长;由于达到了 GitHub 的速率限制,该脚本更频繁地失败。现在,该脚本只需几秒钟即可运行,并且应该消耗恒定数量的请求。

最后,我开始开发一个实验性工具,一个 TUI 应用程序,用于将来自各种渠道的所有反馈提取到一个位置。几个月前我们开发了一个类似的工具,但由于与其基于 Web 的性质相关的各种问题而被放弃了。有机会使用 Python 工作总是很有趣的。

Kirill

这周中旬刚从假期回来,所以这次没什么可分享的。

首先,我已经决定暂时停止文件拖放工作:终端中的 cmd-click 提供了该功能的 80%,并且在我离开期间,一些任务变得更加重要。

说到其中一个任务,内嵌提示的性能结果证明不如预期的那么可扩展——Zed 凭借其整体流畅性设置了相当高的标准。我正在研究最早的想法之一,更严格地跟踪我们查询提示的编辑器范围。这让我在考虑未完成的提示实现博客文章时感觉好一点:有些事情肯定会朝着好的方向改变。

我还添加了一个小面板,用于切换提示和缓冲区搜索,这应该有助于功能发现和此类切换的键绑定。鉴于提示再次进行了大量工作,我开始思考在有牵引力的情况下,以“简单”的方式实现动态提示功能...

Nate

今天的简短更新 - 刚从上周休假回来。本周的大部分时间都与 Mikayla 一起致力于 Channels,深入了解实现细节,并了解如何使通用组件更好地与 Rust 的细微差别配合使用。除此之外,我一直在支持 Nathan 的主题探索和其他杂项事情。

Mikayla

本周,让我们来谈谈 Channels 安全模型以及我在编写上周的文章时发现的大规模权限提升漏洞。该系统中还有一个规则,我认为这是不言而喻的,所以我忘记提到了:在创建新的根通道时,您会自动获得该通道的管理员角色。为了说明问题,请以我给出的个人重组示例为例,并附上我的会员角色

- #my-freelance-design-business - (Admin)
  - > #crdb-industries/#design  - (Member)
  - > #zed/#design              - (Member)

通过级联角色系统,我在自己的私人组织中的管理员角色会覆盖 #crdb-industries#zed 授予我的成员角色,这是一个微不足道且无法克服的权限提升漏洞。

为了解决这个明显的错误,我匆忙地在发布后的句子后面添加了“具有减法干扰”的字样

An image of a GitHub commit diff, showing the addition
显示添加内容的 GitHub 提交差异图像

但是这个修复反而制造了完全相同的问题!如果我将我的同事之一添加为 #my-freelance-design-business 的成员,他们将失去他们对其自己组织的通道拥有的管理员角色!

这里的根本问题是,我们正在使用的安全模型(访问控制列表)无法以我们需要的方式组合在一起,以允许我上周描述的那种行为。值得庆幸的是,还有另一种可能的模型:对象能力 (OCaps)。自从我被介绍到 Spritely Institute 及其基于 Lisp 的 Goblins 框架以来,我对 OCaps 感到非常兴奋。如果您想深入了解对象能力并在此过程中学习一些 Lisp,他们的 Heart of Spritely paper 详细介绍了这一点。

就我们的目的而言,对象能力可以归结为一个简单的规则:访问即授权。如果你有访问权限,那么你可以做该访问权限允许你做的任何事情。请注意指定的权限记录所在的位置,不是在人身上,也不是在服务器上,而是在访问本身上。现在我上面描述的问题很容易解决。与其使用角色,不如想象一下可以在频道上执行的操作,读取和写入,它们对应于上面的成员和管理员角色,让我们去掉级联。现在我的频道看起来像这样

- #my-freelance-design-business - (Write)
  - > #crdb-industries/#design  - (Read)
  - > #zed/#design              - (Read)

问题已经解决了。我只有读取 #crdb-industries/#design 的权限,而我对个人频道的写入权限与它的子频道完全独立。万岁!但这个解决方案有一个主要的缺点:我们不是这样思考的。

Zed 将用户体验放在首位。我们努力消除你的意愿和计算机行为之间的障碍。人类世界目前是根据成员资格和角色来组织的。不管这是否是一件好事,人们会想到“我想让 Mikayla 成为 Zed 的成员”之类的事情,我们需要让表达这一点变得简单。那么为什么不同时做 ACL 和 OCap 呢?

如果我们要做 ACL,我们必须恢复级联成员角色。成为一个频道的成员而不是其子频道的成员是没有意义的(在我们的用例中)。但是为了解决权限提升问题,我们可以向 DAG 的每个添加一种新的规则,在创建边时选择:继承至_。让我们用这个新规则重绘我们的例子,这次在 FigJam 中,因为还有很多事情要做

A diagram showing two root channels, #zed and #my-freelance-design-business, and two members in each channel: Nathan and Mikayla. In #zed, Nathan is an Admin, and Mikayla is a member. In #my-freelance-design-business, Nathan is a Member and Mikayla is an Admin. There is a third channel, called #design, with arrows connecting it to the other two channels. The arrow to #zed is tagged with Inherits up to Admin and the arrow to #my-freelance-design-business is tagged with Inherits up to Member
一张图表,显示了两个根频道,#zed 和 #my-freelance-design-business,以及每个频道中的两个成员:Nathan 和 Mikayla。在 #zed 中,Nathan 是管理员,Mikayla 是成员。在 #my-freelance-design-business 中,Nathan 是成员,Mikayla 是管理员。还有一个名为 #design 的第三个频道,它用箭头连接到其他两个频道。指向 #zed 的箭头标记为“继承至管理员”,指向 #my-freelance-design-business 的箭头标记为“继承至成员”

在这种设置中,边的继承规则就像是级联角色的过滤器,而不是权限本身(纯 OCap 解决方案)。这解决了权限提升攻击,因为我在我自己的频道上的管理员角色在沿着 #my-freelance-design-business -> #design 边传播时,被转换为简单的成员角色。将 #zed -> #design 边标记为 继承至管理员 也解决了窃取我同事的管理员访问权限的反向问题。在 DAG 中存在一条 Nathan 的管理员角色尚未被过滤掉的路径,因此他是 #design 中的管理员。他在 Mikayla 的频道中的成员资格不会干扰他在 Zed 的管理员角色。

更棒的是,我们也可以用这个系统对安全的权限提升进行建模。假设我在业余时间成为了 Rust Analyzer 的核心贡献者,并且我们有这样的频道设置

A diagram showing two root channels, #zed and #rust-analyzer, and Mikayla's membership in each channel: In #zed Mikayla is a member. In #rust-analyzer, Mikayla is an Admin. Below #zed is another channel, #open-source, and below that channel another #zed-rust-analyzer which is tagged with Inherits up to Admin. There is also an edge between #rust-analyzer and #zed-rust-analyzer also with an Inherits up to Admin tag. Mikayla's membership role in #zed-rust-analyzer is shown to be Admin.
一张图表,显示了两个根频道,#zed 和 #rust-analyzer,以及 Mikayla 在每个频道中的成员资格:在 #zed 中,Mikayla 是成员。在 #rust-analyzer 中,Mikayla 是管理员。在 #zed 下面是另一个频道 #open-source,在该频道下面是另一个 #zed-rust-analyzer,它被标记为“继承至管理员”。#rust-analyzer 和 #zed-rust-analyzer 之间也有一条边,也带有“继承至管理员”标签。Mikayla 在 #zed-rust-analyzer 中的成员角色显示为管理员。

由于 Rust Analyzer 和 Zed 共同管理一个频道,并且我在 #rust-analyzer 中拥有管理员角色,因此这些规则允许我的角色在 #zed-rust-analyzer 中升级为管理员。由于成员资格向下级联,从不向上级联,因此我在所有其他 #zed 频道中的成员角色都保持不变,就像应该的那样。

现在我们可以表达我们都习惯的简单、以人为本的成员角色,而无需权衡灵活性和表现力,也无需打开微不足道的安全漏洞。🎉🎉🎉

希望我们可以在下周开始对频道功能进行内部测试。我很高兴看到它在实践中如何运作!特别感谢 Fission 的 Quinn Wilton 在我们讨论上周的权限问题时与我一起完成了这个综合。

Nathan

对我来说是令人兴奋的一周。

今天我整理了这个关于插件的提案并与 Julia 分享了它。一旦我们让基础功能工作起来,我很乐意做一些宏魔法,使将 Rust 对象作为托管对象暴露在 V8 中变得相当容易。

我也在使 Zed 的样式设置更高效方面取得了进展。动态加载效果不是很好,但我已经确定了一个编译速度非常快的 Playground,将 UI 更新的反馈时间缩短到几秒钟。我们最终可以做得更好,但相比之下,这个循环非常可用。下一步将是允许在运行时调整我们最常见元素的样式。与打印的表示形式配对,我们可能会相当有效地将其往返到代码中。这一切都是因为我们需要 Zed 成为设计师编写代码的地方。我认为这对每个人来说都更有效率。

展望未来,我想尝试基于 Rosé Pine 系列来设计 Zed 的主题。这是他们的方案,但我们将他们可爱命名的颜色替换为特定于域的、色调中性的名称

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 包含跨多个帧存在的数据。

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 中的概念对齐。熟悉是一件好事。我们只需要速度。