组合的 Ruby LSP 包

在 Ruby 以外的语言生态系统中,将编辑器工具作为项目依赖项的一部分并不常见。通常,语言服务器是一个可执行文件,它会被下载,然后独立于你的项目运行。

在 Ruby 生态系统中,完全采用这种方法存在一些障碍

  1. 不使用 Ruby 编写语言服务器会使得与社区已有的用 Ruby 编写的工具(如测试框架、linter、格式化工具等)无缝集成变得具有挑战性
  2. 自动发现项目依赖项允许语言服务器检测磁盘上必须读取和索引的文件,以便我们可以从 gem 中提取声明,而无需用户进行任何配置。这意味着我们需要直接与 Bundler 集成
  3. Bundler 只允许加载设置为 $LOAD_PATH 一部分的 gem。如果 Ruby LSP 可执行文件独立于全局安装运行,那么 Ruby 进程将只能加载 Ruby LSP 自己的依赖项,而无法加载正在处理的项目使用的任何 gem。无法加载项目的依赖项会限制语言服务器可以自动与 linter、格式化工具、测试框架等进行的集成

为了克服这些限制,同时又不要求用户将 ruby-lsp 作为其项目的依赖项添加,Ruby LSP 使用了一种组合的包策略。执行流程如下

  1. 可执行文件作为 ruby-lsp 运行,不带 bundle exec,表示必须先配置组合的包
  2. 可执行文件在 your_project/.ruby-lsp 下设置一个组合的包。生成的 Gemfile 包括 ruby-lsp gem 以及项目 Gemfile 中的所有内容。它还可能包括 debugruby-lsp-rails
  3. 在组合的包完全设置好之后,原始的 ruby-lsp Ruby 进程会被 BUNDLE_GEMFILE=.ruby-lsp/Gemfile bundle exec ruby-lsp 完全替换,从而启动真正的语言服务器,该服务器可以访问项目的依赖项,而无需将 gem 添加到项目自己的 Gemfile 中

除了执行此设置之外,组合的包逻辑还会执行 bundle install 并尝试自动更新 ruby-lsp 语言服务器 gem,以确保快速分发 bug 修复和新功能。

设置组合的包需要与 Bundler 进行多次集成,并且有很多边缘情况需要考虑,例如如何处理配置或安装私有依赖项。如果您在组合的包设置中遇到问题,请通过报告问题告诉我们。