Ruby LSP
Ruby LSP 是 语言服务器协议 的 Ruby 实现,用于改进编辑器中的丰富功能。它是更广泛目标的一部分,旨在为 Ruby 开发人员提供使用现代跨编辑器功能、文档和调试标准的先进体验。
想讨论 Ruby 开发人员的体验吗?请考虑加入公开的 Ruby DX Slack 工作区。
目录
使用方法
在 VS Code 中
如果使用 VS Code,您只需安装 Ruby LSP 扩展即可在编辑器中获得额外功能。请勿手动安装 ruby-lsp
gem。
有关使用和配置扩展的更多信息,请参阅扩展的 README.md。
在其他编辑器中
有关设置 Ruby LSP 的社区说明,请参阅编辑器,目前包括 Emacs、Neovim、Sublime Text 和 Zed。
可以通过执行以下操作安装 gem
gem install ruby-lsp
并且可以通过运行 ruby-lsp
(不带 bundle exec,以便正确挂钩到项目的依赖项)启动语言服务器。
组合式 Ruby LSP 包
Ruby LSP 可执行文件会生成一个组合包,目的是不要求用户将 ruby-lsp
gem 添加到其 Gemfile 中,同时能够挂钩到项目依赖项。了解更多。
依赖兼容性
如果项目中存在 RuboCop,Ruby LSP 可以提供增强的体验。虽然使用它是可选的,但如果您选择包含 RuboCop,请确保它是 1.4.0 或更高版本。
插件
Ruby LSP 提供了一个插件系统,允许其他 gem 通过更多编辑器功能增强基本功能。这是为以下插件提供支持的机制,例如
此外,某些工具可能直接包含 Ruby LSP 插件,例如
其他社区驱动的插件可以在 rubygems 中找到,通过搜索 ruby-lsp
前缀。
有关如何创建插件的说明,请参阅插件文档。
通用功能
请注意,本节中的所有功能都不是 Ruby 特有的;它们对于所有编程语言都是通用的。熟悉它们将增强您有效使用编辑器的能力。
如果您正在使用 VS Code,我们建议您参考他们出色的指南和文档,以了解有关编辑器的理念和功能集的更多信息。
悬停提示
当光标悬停在目标常量或方法上时,悬停功能会显示目标常量或方法的注释或文档。
在 VS Code 中,如果您在按住 Command
键的同时悬停,它还会发送一个 definition
请求以查找可能的目标源。如果只找到一个源(例如,该类未在多个位置重新打开),它将显示目标源代码。
跳转到定义
跳转到定义允许用户导航到目标常量或方法的定义,无论它们是在您的项目还是其依赖项中定义的。
在 VS Code 中,可以通过以下方法之一触发此功能
- 在目标上
右键单击
,然后选择跳转到定义
- 将光标放在目标上,然后按
F12
Command + 单击
目标
只有一个定义
用户会被直接带到源代码。
有多个定义
用户会看到一个带有所有源的下拉列表,以及侧面的预览窗口。
代码补全
当用户键入的文本与某些索引组件匹配时,代码补全功能会向用户提供补全候选项。这通过减少键入完整方法名称或常量的需要来帮助加快编码速度。它还允许开发人员发现可供他们使用的常量或方法。
只有在知道接收者的类型时才能提供方法调用的补全。例如,当键入 foo.
时,只有在知道 foo
的类型时才有可能显示方法补全候选项。由于 Ruby LSP 不要求用户采用类型系统,因此只有在即使没有注释也可以确定类型时(例如:在字面量、常量、使用 new
直接实例化的对象上调用的方法),才能提供方法补全。
如果您希望获得更准确的补全,请考虑采用类型系统。
签名帮助
签名帮助通常在用户完成键入方法后立即出现,提供有关方法参数的提示。此功能对于理解预期参数和提高代码准确性非常有价值。
代码镜头
代码镜头是根据代码上下文自动添加的按钮。Ruby LSP 支持单元测试的代码镜头,允许您使用 VS Code 的测试资源管理器运行测试,在终端中运行测试或启动调试器。
代码镜头请求需要在编辑器中实现特定命令才能工作。对于 VS Code,这包含在 Ruby LSP 扩展中。如果您使用的是其他编辑器,请查看编辑器的文档,了解如何定义所需的命令。
文档符号
文档符号允许用户模糊搜索当前文件内的声明。它还用于填充面包屑和轮廓。
工作区符号
工作区符号是文档符号的项目范围版本。它允许用户模糊搜索整个项目中的任何声明。
文档链接
文档链接使神奇的 source
链接可点击。这用于方便地连接两个声明。请注意,只有当链接紧挨着声明上方而不是代码中的任何位置时才会处理链接。
文档高亮
文档高亮会显示光标下的实体的出现和声明。
折叠范围
折叠范围允许用户在源代码的相关范围内折叠代码。
语义高亮
语义高亮消除了语言中的歧义,以实现一致的编辑器高亮。例如,仅使用 TextMate 语法,局部变量和没有接收者或括号的方法调用可能会混淆,通常会导致不正确的高亮。
Ruby LSP 的语义高亮策略是返回尽可能少的标记,以确保准确高亮。处理大量标记对于编辑器来说开销很大,可能会导致延迟。
语义高亮只是通知编辑器文件中存在什么类型的标记。例如,Ruby LSP 会告诉编辑器“这是一个局部变量”或“这是一个方法调用”。但是,这并不意味着主题必须利用该信息或它们支持语义高亮。
Ruby 扩展包扩展包含 Spinel 主题,该主题通过充分利用 Ruby LSP 的所有语义信息,为使用 Ruby 语言量身定制。
如果您希望为其他主题添加更好的 Ruby 支持,请参阅主题的语义高亮文档。
诊断
诊断是指基于当前代码状态显示的语法检查、错误、警告以及任何其他类型的信息。Ruby LSP 原生支持语法错误,并且还支持显示语法检查错误。
您可以配置要使用的语法检查器,只要它们集成了 Ruby LSP 即可。请查看可用的配置。
格式化
格式化允许在保存时自动格式化文档,或者如果编辑器支持,也可以手动格式化。
代码操作
快速修复
Ruby LSP 支持通过快速修复来解决违规问题。
重构
Ruby LSP 支持一些代码重构,例如提取到变量、提取到方法和切换代码块样式。
内嵌提示
内嵌提示会向用户显式显示隐式信息。目标是使隐式行为更易于发现和可见。
默认情况下,仅显示隐式 rescue 提示。VS Code 用户可以使用以下设置自定义内嵌提示行为
{
// Enable all hints
"rubyLsp.featuresConfiguration.inlayHint.enableAll": true,
// Enable implicit rescue (defaults to true)
"rubyLsp.featuresConfiguration.inlayHint.implicitRescue": true,
// Enable implicit hash values (omitted hash values)
"rubyLsp.featuresConfiguration.inlayHint.implicitHashValue": true
}
要配置其他编辑器,请参阅初始化选项。
输入时格式化
输入时格式化会在用户键入时将更改应用于代码。例如,当换行时,Ruby LSP 会自动补全 end
标记。
在 VS Code 中,默认禁用输入时格式化。您可以使用 "editor.formatOnType": true
启用它。
范围格式化
范围格式化允许用户在编辑器中格式化所选内容,而无需格式化整个文件。它也是启用粘贴时格式化功能的基础。
在 VS Code 中,默认禁用粘贴时格式化。您可以使用 "editor.formatOnPaste": true
启用它。
目前,只有 Syntax Tree 格式化程序支持部分格式化文件。支持 RuboCop 或 Standard 的范围格式化需要公开新的 API,以便 Ruby LSP 可以告知格式化程序选择位置的基本缩进。此外,格式化程序只能应用对文档部分有意义的更正。
选择范围
选择范围(或智能范围)会根据代码的结构扩展或缩小选择。在 VS Code 中,可以使用 CTRL + SHIFT + 左/右箭头
分别触发扩展/缩小。
显示语法树
显示语法树会显示当前 Ruby 文档的抽象语法树 (AST)。此自定义功能可以显示整个文档的 AST 或选择内容的 AST。
此功能不是语言服务器规范的一部分。它是一个自定义功能,在 Ruby LSP 的 VS Code 扩展中实现。其他编辑器可以实现类似的方法来实现相同的功能
ERB 支持
Ruby LSP 可以处理 ERB 文件,并处理文件中嵌入的 Ruby 和宿主语言部分。对于嵌入的 Ruby 部分,Ruby LSP 会响应您在常规 Ruby 文件中通常看到的所有 Ruby 功能。对于宿主语言(如 HTML)的功能,Ruby LSP 会将请求委托给注册处理该文件类型的语言服务。
请求委托尚未正式作为 LSP 规范的一部分。因此,这需要在客户端(编辑器)端进行自定义代码。Ruby LSP VS Code 扩展附带了该自定义实现,但其他编辑器需要实现相同的功能才能支持这些功能
某些 JavaScript 功能的委托工作不完整。例如,onclick
属性内的补全有时会显示不正确的候选项。我们认为这可能是请求委托的总体限制,并且我们已经与 VS Code 进行了讨论,以更好地理解它。
推测类型
推测类型是一项功能,其中 Ruby LSP 尝试根据接收者的标识符来识别其类型。例如
# The receiver identifier here is `user` and so the Ruby LSP will assign to it the `User` type if that class exists
user.name
# Similarly, the receiver identifier here is `post` and so the LSP searches for the `Post` class
@post.like!
此实验的目的是了解我们是否可以为已有的代码获得更高的准确性。假设是,相当多的代码已经使用了示例中的模式,并且在这些情况下,我们可以获得更好的结果。
但是,标识符不是正确类型注释的理想媒介。不可能表达任何复杂的内容,例如联合、交集或泛型。此外,通过简单地使用与其真实类型不匹配的类型名称来命名变量,很容易欺骗类型猜测。
pathname = something_that_returns_an_integer
# This will show methods available in `Pathname`, despite the variable being an Integer
pathname.a
我们不建议仅为了获得更高的准确性而重命名方法、实例变量或局部变量 - 可读性应始终放在首位。例如
# It would not be a good idea to name every string "string" for the sake of getting better accuracy.
# Using descriptive names will outweigh the benefits of the more accurate editor experience
# don't
string = something.other_thing
# do
title = something.other_thing
name = foo
也就是说,此功能还可以用于快速浏览类中可用的方法。只需键入类的小写名称,补全即可显示可用的方法。
# Any class name as an identifier
pathname.a
integer.a
file.a
为了推测类型,Ruby LSP 将首先尝试基于接收者标识符和当前嵌套来解析常量。如果无法识别任何有效类型,则会回退到基于非限定类型名称的第一个匹配项进行匹配。例如
module Admin
class User
end
# Will match to `Admin::User` because the `user` reference is inside the `Admin` namespace
user.a
end
module Blog
class User
end
# Will match to `Blog::User` because the `user` reference is inside the `Blog` namespace
user.a
end
# Will match to the first class that has the unqualified name of `User`. This may return `Admin::User` or `Blog::User`
# randomly
user.a
重命名符号
重命名允许开发人员重命名整个项目中光标下实体的所有出现位置。在 VS Code 中,可以通过右键单击要重命名的实体或按 F2 键来触发重命名。您还可以在键入所需的新名称后按 CTRL/CMD + Enter 来预览将应用的编辑。
查找引用
查找引用请求允许用户查看引用列表或跳转到引用位置。请注意,目前仅支持常量,但计划支持方法、实例变量和局部变量。
VS Code 功能
以下功能均为 VS Code 定制。
依赖项视图
Ruby LSP 提供了一个自定义的依赖项视图面板,允许用户导航其项目的依赖项。
Rails 生成器集成
Ruby LSP 与 Rails 生成器集成,可以通过 UI 调用。所有生成的文件都会自动打开并使用项目的格式化配置进行格式化。
调试客户端
Ruby LSP 附带了 debug gem 的客户端。该客户端允许 代码镜头 等功能,还可以为使用可视化调试器启动进程或附加到现有服务器启用启动配置。
版本管理器集成
当在具有不同 Ruby 版本的多个项目上工作时,Ruby LSP 需要知道正在使用的 Ruby 版本以及 gem 的安装位置,以便支持自动依赖项检测和索引。
我们支持与以下版本管理器的自定义集成,以便自动切换版本,而无需任何用户操作
此外,如果自定义集成不够,我们还提供了以下转义方法
- 自定义:定义一个自定义 shell 脚本,以在任何项目上激活 Ruby 环境
- 无:不执行任何操作,并依赖于 VS Code 继承的环境
大多数版本管理器都有一些 shell 组件,以便在终端中更改用户的环境并指向正确的 Ruby 版本。因此,VS Code 扩展必须从运行它的 NodeJS 进程中调用用户的 shell - 否则,版本管理器将无法用于集成。
这有时会导致 Ruby 环境激活问题。例如,某些 shell 插件期望终端设置的变量存在,如果不存在,则会失败。运行扩展的 NodeJS 进程将不会设置这些变量,因此很可能会失败。
由于操作系统、shell、插件和版本管理器的不同组合数量,找到此问题的一般解决方案并不容易。最重要的是,人们配置其 shell 环境的方式也不同。例如,某些用户可能会在 ~/.zshrc
中加载其版本管理器,而其他用户则会在 ~/.zshenv
或 ~/.zprofile
中加载。
如果遇到问题,请记住 shell 配置可能会造成干扰,请查看故障排除。如果列出的解决方案均无效,请报告问题。
测试资源管理器
Ruby LSP 会在 VS Code 的测试资源管理器视图中填充当前文件的测试。有关另一个演示,请参阅代码镜头。
Ruby LSP 有意不索引代码库中的每个测试并将其显示在测试资源管理器中。在大型代码库中,这样做会导致性能问题、过多的内存使用和导航困难(由于测试数量庞大)。我们可能会在未来重新考虑这个问题,但这需要确保它满足我们的性能要求。
实验性功能
Ruby LSP 还提供默认未启用的实验性功能。如果您对这些功能有反馈,可以在DX Slack中告知我们,或者创建问题。
祖先层级请求
祖先层级请求功能旨在更好地理解 Ruby 代码中的继承层级。此功能可帮助开发人员追踪其类和模块的谱系,从而更容易:
- 可视化类和模块的继承层级。
- 快速导航继承链。
为什么是实验性的?
此功能由类型层级超类型 LSP 请求支持。在实现过程中,我们发现将其应用于 Ruby 时存在一些歧义。例如:
- 列表应仅包含类(纯继承链),还是也应包含模块(当前行为)?
- 单例类的继承链应如何触发和显示?
- 如果一个类或模块被多次重新打开,它将在列表中出现多次。在实际应用中,这可能会使列表变得非常长。
我们创建了一个问题,以寻求 LSP 维护者的澄清。我们将根据他们的回复和您的反馈调整此功能的设计和行为。
Copilot 聊天参与者
Ruby LSP 包含一个 Copilot 聊天参与者,该参与者具有 Ruby 和 Rails 命令的内置知识,可帮助您高效地构建这些命令。
配置
配置代码索引
默认情况下,Ruby LSP 索引当前项目及其所有依赖项中定义的所有 Ruby 文件,包括默认 gem,但以下情况除外:
- 仅出现在
:development
组下的 Gem test/**/*.rb
下的所有 Ruby 文件
此行为可以被覆盖和调整。了解如何在 VS Code 中配置。
请注意,依赖于索引的行为(例如定义、悬停、完成或工作区符号)将受到配置更改的影响。
使用 .index.yml
文件的旧方法已被弃用,将在未来的版本中删除。
# Exclude files based on a given pattern. Often used to exclude test files or fixtures
excluded_patterns:
- "**/spec/**/*.rb"
# Include files based on a given pattern. Can be used to index Ruby files that use different extensions
included_patterns:
- "**/bin/*"
# Exclude gems by name. If a gem is never referenced in the project's code and is only used as a tool, excluding it will
# speed up indexing and reduce the amount of results in features like definition or completion
excluded_gems:
- rubocop
- pathname
# Include gems by name. Normally used to include development gems that are excluded by default
included_gems:
- prism
额外资源
- RubyConf 2022:使用语言服务器改善开发体验(Vinicius Stock)
- 远程 Ruby:使用 Vinicius Stock 的 Ruby 语言服务器
- RubyKaigi 2023:代码索引 - 语言服务器如何理解我们的代码(Vinicius Stock)