Backstage News

Fleet 后台探秘,第五部分 – 代码补全的故事

Read this post in other languages:

在本系列博文中,我们将以多个部分为您介绍构建 Fleet 这款由 JetBrains 打造的下一代 IDE。

在本系列的第三部分和第四部分中,我们探讨了状态管理中涉及的复杂抽象架构概念,以及它们如何在 Fleet 的分布式组件之间进行同步。接下来,我们将研究更熟悉的代码补全功能,看一看它是如何在 Fleet 中实现的。

Fleet 在代码补全领域并不是一项巨大突破,但其架构分布式特性在各处都留有印记。想象一下:一个典型 Fleet 用户,有幸(或不幸)使用多种编程语言。这类软件开发者会在 Go 和 JavaScript 上苦苦挣扎,更喜欢 Kotlin 和 Rust,还要在文本文件中记笔记。有没有哪种代码补全解决方案能够涵盖所有情况呢?我们来找出答案。

最终用户看到的代码补全

我们先来做一些文本记录。看一看到目前为止的代码补全:

这种行为内置于 Fleet 的编辑器中:它会分析文档,考虑哪些单词是代码补全的良好候选。听起来还算合理,虽然您可能以为是机器学习在根据您先前记下的其他笔记来施展魔法。

接下来,我们转向 Kotlin 编程。

这种补全行为并没有什么特别突出的地方。基本上就是文本记录中的相同算法。Fleet 建议我们启用智能模式。将其启用。

看起来好多了:我们得到了基于代码分析的准确建议。问题是,谁在负责提供这些补全项?显然不是编辑器,因为我们需要大量关于代码本身和它可能使用的库的信息。

注意,补全不一定追加代码。事实上,它会更改代码。在以下示例中,代码补全移除了点字符并添加了大括号:

使用代码补全也可以从预定义代码段生成通用代码段。在以下示例中,我们在智能模式下编写 Rust。输入 for 并按 Ctrl-空格键即可插入以下代码段:

补全结果是一些模板文本,带有几个由文本光标表示的占位符。我们可以使用 TAB 键继续移动。插入代码段相当枯燥。Fleet 无需分析代码即可执行此操作。此功能可以在编辑器中轻松实现。探索这些示例,可以看到补全结果在很大程度上依赖于智能模式不过,即使没有智能模式,我们还可以使用别的东西。我们还可以看到一些基本代码追加,以及代码重构或以占位符插入复杂代码段。现在,来看看后台发生了什么。

内部分析

下图很好地展示了 Fleet 中代码补全的工作原理。

在某些情况下,Fleet 的前端可以自己提供代码补全。有多个预定义 JSON 方案适用于补全,例如 settings.json,这是一个具有 Fleet 设置的文件。启用智能模式后,我们可能会开始看到由 IntelliJ IDEA 或 ReSharper 或大量 LSP(语言服务器协议)服务器驱动的全面补全引擎。Rust-analyzer 是 Fleet 支持的第一个 LSP 服务器。Fleet 项目进展顺利,因此未来还会有其他项目。

IntelliJ IDEA 后端和 LSP 服务器提供的代码补全是其服务的一部分。它们还提供许多其他服务,包括维护缩进规则和报告代码中的编程错误等。对于部分编程语言,拥有多个后端引擎没什么问题。未来,Fleet 用户将能够选择任何偏好的引擎。

代码补全可以分解为多个组件:

  • 对于特定源位置可用且合理的补全项。
  • 负责提供补全项的补全服务引擎。
  • 统一不同补全服务的补全 API。
  • 负责将补全项交付给用户并应用所选项的补全会话。

这些值得更深入的讨论。

补全项

补全项是指用于从当前源代码位置继续的一个可用选项。

Fleet 中有三种补全项:

  • 基本补全项是应该追加到当前光标位置的文本。
  • 代码段是带有占位符的模板。 插入源代码文件后,代码段支持占位符间导航,并基于其他占位符中输入的内容动态建议补全项。 这些代码段提供的功能与 IntelliJ IDEA 中的实时模板相同。
  • 声明式插入是一组编辑指令(如插入、移除、替换),应当应用于代码以完成代码补全操作。

特定类型的补全项会定义:补全项列表中显示的内容,应用后对代码执行的操作,特定代码建议的优先级,等等。根据种类,补全项的应用可能多少有些琐碎。如果补全需要不能以声明式方式表达的复杂代码操作,Fleet 将要求后端在后端执行补全,而不是在 Fleet 的编辑器中执行。

应用补全项的过程不会更改文档内容。它只会生成一组应当应用于该内容的操作。这是通向 Fleet 状态管理的第一座桥梁。然后,这些文本操作将在考虑 Fleet 的分布式特性的情况下被应用。

补全项不会凭空出现。必须有服务根据当前源代码位置和当前文档版本生成它们。由于这样的服务可能不止一项,我们需要一个 API 来统一这些服务的访问。

补全 API 和补全服务

Fleet 的补全 API 很精简,可用于:

  • 获取给定源代码位置的补全项。
  • 补全当前语句,如果只有一种可行方法。
  • 为我们应用补全,如果我们有更重要的事情要做。
  • 清理补全引擎使用的资源。

Fleet 如何知道哪些补全服务可用?会使用发现服务吗?阅读过往期博文的读者可能会想到使用状态管理引擎。任何补全服务都只是被加载到 Fleet 状态的实体。事实上,通常 Fleet 中的任何功能都以某种实体的形式出现。

这些服务实体知道它们支持哪些文档类型。Fleet 会查找状态,选择第一个负责当前文档类型的状态。如果没有找到,Fleet 将使用预定义代码段或文档单词列表,如上图所示。通过加载和卸载这些实体(阅读:启用和禁用对应插件),最终用户可以控制补全代码的内容。

目前为止,Fleet 中有两个主要补全服务:

  • 一个负责从 IntelliJ IDEA 后端获取补全项。
  • 另一个从 LSP 连接获取补全项。

它们都会实现补全 API,可供 Fleet 统一地使用。我们不会在这里说明这些服务是如何产生补全项的,这不在 Fleet 的相关范围内。Fleet 终究只是一个文本编辑器。

补全会话

代码补全是一个具有时效性的过程。用户启动后,Fleet 就要找到合适的补全服务并将其启动。收到的项目必须在弹出窗口中显示。用户做出选择后,所选项必须被应用到所有位置(工作区和所有前端)。

实际过程通常更为复杂。首先,获取高质量的补全项需要时间,而等待它们全部准备就绪是不现实的。提供的形式是项目。Fleet 获得项目的新部分后,负责显示的组件就要被更新。

其次,用户可能会在请求代码补全后继续输入。从头开始重启补全操作效率太低。为每个输入的字符调用后端也成本过高。Fleet 可以通过筛选和重新排序来更新接收的补全项列表。重新排序相当麻烦。它是通过数个能够反映前缀匹配的额外优先级特征实现的。

最终,用户如果对建议不满意,可以取消补全。

补全会话是 Fleet 状态下的另一个实体,负责管理以上所有内容和其他内容。例如,它会处理可能已经过时的源代码位置,因为其他用户可能正在使用另一个 Fleet 前端同时编辑同一个文档。

有时候,真的很难相信这能奏效。再看第一部分的屏幕截图,注意代码补全会话附带的 UI 详细信息。完成代码补全以及在模板中编辑和填充占位符时,警告和错误不断出现。同时,大量数据流经工作区和前端之间的网络。分布式事务在下面展开。真的很神奇。

总结

代码补全只是 Fleet 功能的一小部分。现在,真的很难想象没有这个功能的世界。虽然有些人喜欢在 Google 文档中编写程序,特别是那些相信这能够锻炼编码能力的人。但我们其他人还是更乐意使用代码补全。

在大多数情况下 Fleet 都不够智能,不知道向用户建议什么。它将使用其他工具,例如 IntelliJ 后端或 LSP 服务器。Fleet 专注的是在以不同语言编辑源代码时提供最佳体验。它运用自身的所有状态管理机制来帮助用户更有效地工作。

这不是本系列的最后一篇博文。在下一部分中,我们将分析 Fleet 中使用的 UI 框架。我们叫它 Noria。敬请关注!

本博文英文原作者:

Sue

Vitaly Bragilevsky

Discover more

Fleet 后台探秘,第六部分 – UI 和 Noria

在本系列博文中,我们将以多个部分为您介绍构建 Fleet 这款由 JetBrains 打造的下一代 IDE。 第一部分 – 架构概述 第二部分 – 编辑器详解 第三部分 – 状态管理 第四部分 – 分布式事务 第五部分 – 代码补全的故事 第六部分 – UI 和 Noria 在本系列的第五部分中,我们讨论了 Fleet 的一项服务 – 代码补全。 现在,该谈谈我们自己的 JVM 声明式 UI 框架 Noria 了。 我们使用 Noria 构建了 Fleet。 来看看 Noria 背后的想法、主要概念和其他精彩功能。 一切开始的地方:Noria 窗口 UI 是如何构建的? 首先,我们有一台具备图形功能的显示器,它能够呈现应用程序的状态,也负责使我们的 UI 成为 GUI。 我们可能还有一个或多个输入设备,例如键盘、鼠标或触控板,用来传递命令和控制应用程序的行为。 在这些布置中,应用程序实际上是一个事件循环,负责回应用户和其他计算机系统组件(计时器、文件系统、网络等)发起的事件。 首先,回应是屏幕上显示的可见变化。 除了事件循环之外,GUI 应用程序通常还有某种窗口、其负责的屏幕区域以及该区域的一些绘图功能。 窗口通常由底层操作系统或它上面的窗口管理器提供。 操作系统可以提供图形 API 或将绘图委托给图形框架。 Fleet 既是 GUI 应用程序,也是 JVM 应用程序。 它可以

介绍 Fleet 公共预览

自从最初宣布 Fleet 以来,我们得到了来自大家的极大兴趣,有超过 137,000 人报名参加私人预览。我们之所以从封闭式预览开始,是为了能够以渐进的方式处理反馈。我们要感谢每一个参加这次私人预览的人,我们也想向没有收到邀请的人致以歉意。幸运的是,您不再需要等待了。 Fleet 仍处于起步阶段,还有大量的工作要做。然而,今天我们宣布首次公共预览 Fleet,所有人都可以使用。我们向公众开放预览的原因有两个方面。首先,我们认为让所有注册者再等下去是不对的,但单独邀请这么多人对我们来说也缺乏意义。面向公众开放预览对我们来说更容易。第二,也是最重要的,我们一直是一家以开放态度打造产品的公司。我们不希望 Fleet 在这方面有任何不同。 不过,在您下载它之前,请继续阅读完这篇文章,因为有一些重要的事情您应该知道。 Fleet 是什么 对于那些之前可能没有听说过它的人来说,Fleet 是我们新的分布式多语言编辑器和 IDE。它是基于我们在后端的 IntelliJ 平台,采用了全新的用户界面和分布式架构,从头开始构建的。要了解更多详情,请查看 Fleet 产品页面。 现在可以期待什么 由于我们计划支持的技术清单很长,我们准备了一个图表,显示 Fleet 目前提供支持的语言和技术,以及每个技术的状态。这应该能让您更好地了解接下来可

Fleet 后台探秘,第三部分 — 状态管理

在本系列博文中,我们将以多个部分为您介绍构建 Fleet 这款由 JetBrains 打造的下一代 IDE。 第一部分 – 架构概述第二部分 – 编辑器详解第三部分 – 状态管理 在本系列的前几部分中,我们介绍了 Fleet 的总体架构,并探讨了编辑器后台用到的算法和数据结构。 在这一部分中,我们将介绍实现状态管理的方式。 这是一个复杂的主题,因此我们特别准备了多篇博文。 本篇的重点是应用程序状态元素的表示和存储, 下一部分将更细致地探讨 Fleet 中围绕状态管理的事务机制。 Fleet 有很多移动部件,也执行着许多不同的操作,包括: 呈现 UI 元素并与用户互动。与其他服务交互以获取数据和更新 UI 元素。处理文件,例如保存、加载、解析文件以及显示其差异。编排处理代码洞察、补全和搜索结果的后端。 许多操作较为复杂,可能会降低界面的响应能力。 同时,由于 Fleet 是分布式应用程序,可能有多个分布在网络上的前端,使整个过程更加复杂。 尽管如此,我们还是必须持续为用户正确显示所有信息,确保用户可以在前端之间稳定地工作。 在状态管理上,操作分为读取状态和更新状态。 UI 元素读取状态后向用户提供实际数据,用户则通过编辑文档和移动内容来更新状态。 这