Ruby LSP does not work when using different versions of Ruby
I've searched open issues for similar requests
- [X] Yes
I've manually reviewed logs to find potential errors
- [X] Yes
I've recently downloaded the latest plugin version of mason.nvim
- [X] Yes
Problem description
First, I use rbenv to manage my ruby versions.
My system ruby is set to 3.3.4 (most current), and then in my project specific folders I am using different ruby versions using the .ruby-version file. When I install the ruby-lsp package using mason, it is installed with the system ruby version, that includes compiling the prism gem that is a natively built C extension that is being linked against the specific ruby version it was built with (so in this case 3.3.4)
When I now start neovim in one of my project folders that has a .ruby-version file, nvim tries to run ruby-lsp with the project specific ruby (i.e. 3.3.1), but since the installed version in ~/.local/share/nvim/mason/ruby-lsp was built against the system ruby (3.3.4 here). Log file shows:
~/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `require': linked to incompatible ~/.rbenv/versions/3.3.4/lib/libruby.3.3.dylib - ~/.local/share/nvim/mason/packages/ruby-lsp/gems/prism-0.30.0/lib/prism/prism.bundle (LoadError)
from ~/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `block (2 levels) in replace_require'
from ~/.local/share/nvim/mason/packages/ruby-lsp/gems/prism-0.30.0/lib/prism.rb:78:in `<top (required)>'
from ~/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `require'
from ~/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `block (2 levels) in replace_require'
from ~/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/ruby-lsp-0.17.15/lib/ruby_lsp/internal.rb:19:in `<top (required)>'
from ~/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `require'
from ~/.rbenv/versions/3.3.1/lib/ruby/3.3.0/bundled_gems.rb:74:in `block (2 levels) in replace_require'
from ~/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/ruby-lsp-0.17.15/exe/ruby-lsp:82:in `<top (required)>'
from ~/.rbenv/versions/3.3.1/bin/ruby-lsp:25:in `load'
from ~/.rbenv/versions/3.3.1/bin/ruby-lsp:25:in `<main>'
from ~/.rbenv/versions/3.3.1/bin/ruby_executable_hooks:22:in `eval'
from ~/.rbenv/versions/3.3.1/bin/ruby_executable_hooks:22:in `<main>'
I have currently worked around this in my neovim config, by hardcoding the paths for ruby-lsp and rubocop in my neovim-lspconfig.lua file, and skipping mason:
return {
"neovim/nvim-lspconfig",
opts = {
servers = {
ruby_lsp = {
mason = false,
cmd = { vim.fn.expand("~/.rbenv/shims/ruby-lsp") },
},
rubocop = {
mason = false,
cmd = { vim.fn.expand("~/.rbenv/shims/rubocop"), "--lsp" },
}
},
},
}
Expected behavior
Personally, I think that mason should support installing the ruby-lsp gem for multiple ruby versions, one way how this could be done, is that instead of installing the gem into ~/.local/share/nvim/mason/packages/ruby-lsp, the current ruby version could be included in the path i.e. ~/.local/share/nvim/mason/packages/ruby-3.3.4/ruby-lsp or similar. This way, the package would be reinstalled with a freshly compiled version of prism for the currently used ruby version for each project.
Steps to reproduce
- Switch to a specific ruby version (i.e. 3.3.4)
- Open Neovim and install the
ruby-lsppackage using mason - Close Neovim
- Switch to a different ruby version (i.e. 3.2.5, but every version works)
- Open Neovim and open a Ruby file
- See that LSP cannot work because the error message above is shown
Affected packages
ruby-lsp
Neovim version (>= 0.7)
NVIM v0.10.1 Build type: Release LuaJIT 2.1.1724512491
Operating system/version
Darwin mymachine.internal 23.6.0 Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6030 arm64
Healthcheck
==============================================================================
mason: require("mason.health").check()
mason.nvim ~
- OK mason.nvim version v1.10.0
- OK PATH: prepend
- OK Providers:
mason.providers.registry-api
mason.providers.client
- OK neovim version >= 0.7.0
mason.nvim [Registries] ~
- OK Registry `github.com/mason-org/mason-registry version: 2024-08-26-cute-join` is installed.
mason.nvim [Core utils] ~
- OK unzip: `UnZip 6.00 of 20 April 2009, by Info-ZIP. Maintained by C. Spieler. Send`
- OK wget: `GNU Wget 1.24.5 built on darwin23.2.0.`
- OK curl: `curl 8.7.1 (x86_64-apple-darwin23.0) libcurl/8.7.1 (SecureTransport) LibreSSL/3.3.6 zlib/1.2.12 nghttp2/1.61.0`
- OK gzip: `Apple gzip 430.140.2`
- OK tar: `bsdtar 3.5.3 - libarchive 3.5.3 zlib/1.2.12 liblzma/5.4.3 bz2lib/1.0.8 `
- OK bash: `GNU bash, version 5.2.32(1)-release (aarch64-apple-darwin23.4.0)`
- OK sh: `Ok`
mason.nvim [Languages] ~
- WARNING Go: not available
- ADVICE:
- spawn: go failed with exit code - and signal -. go is not executable
- WARNING Composer: not available
- ADVICE:
- spawn: composer failed with exit code - and signal -. composer is not executable
- WARNING PHP: not available
- ADVICE:
- spawn: php failed with exit code - and signal -. php is not executable
- WARNING cargo: not available
- ADVICE:
- spawn: cargo failed with exit code - and signal -. cargo is not executable
- WARNING luarocks: not available
- ADVICE:
- spawn: luarocks failed with exit code - and signal -. luarocks is not executable
- WARNING javac: not available
- ADVICE:
- spawn: javac failed with exit code 1 and signal 0. The operation couldn’t be completed. Unable to locate a Java Runtime.
Please visit http://www.java.com for information on installing Java.
- OK node: `v18.20.1`
- WARNING julia: not available
- ADVICE:
- spawn: julia failed with exit code - and signal -. julia is not executable
- OK python: `Python 3.12.5`
- WARNING java: not available
- ADVICE:
- spawn: java failed with exit code 1 and signal 0. The operation couldn’t be completed. Unable to locate a Java Runtime.
Please visit http://www.java.com for information on installing Java.
- OK Ruby: `ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]`
- OK RubyGem: `3.5.17`
- OK pip: `pip 24.2 from /opt/homebrew/lib/python3.12/site-packages/pip (python 3.12)`
- OK npm: `10.5.0`
- OK python venv: `Ok`
mason.nvim [GitHub] ~
- OK GitHub API rate limit. Used: 5. Remaining: 55. Limit: 60. Reset: Mon Aug 26 13:34:52 2024.
Install and authenticate via gh-cli to increase rate limit.
### Screenshots or recordings
_No response_
I'm struggling with this issue too
I guess you need to update mason to solve this issue.
:MasonUpdate solved it in my case
Was there an update to Mason that includes a fix for this?
@klaustopher I guess the update triggered a new installation of the dependencies, so in my case because I was already using a different ruby version it worked.
Ok, but manually running this every time is not really an option for me as I am often switching projects with different versions. So my fix mentioned above and manually installing ruby-lsp (or automating it with rbenv-default-gems plugin)
I override the BUNDLE_GEMFILE env to a global gemfile to prevent this issue.
ruby_lsp = {
cmd_env = { BUNDLE_GEMFILE = vim.fn.getenv('GLOBAL_GEMFILE')},
cmd = { 'ruby-lsp' },
filetypes = { 'ruby', 'eruby' },
root_dir = function()
return vim.loop.cwd()
end,
},
@Chrispycode's workaround worked for me with Lazy and mason-lspconfig.nvim. The config looks something like this:
{
'williamboman/mason-lspconfig.nvim',
lazy = true,
dependencies = { 'neovim/nvim-lspconfig' },
config = function()
require('mason').setup {}
require("mason-lspconfig").setup {}
require("mason-lspconfig").setup_handlers {
function(server_name)
require("lspconfig")[server_name].setup {}
end,
ruby_lsp = function()
require('lspconfig').ruby_lsp.setup {
cmd_env = { BUNDLE_GEMFILE = vim.fn.getenv('GLOBAL_GEMFILE') },
}
end,
end,
},
I have the same problem, trying to fix it. What do you put in your GLOBAL_GEMFILE ?
Encountered the same issue.
Not a long term fix, but I had a global ~/.config/mise/config.toml file where my global ruby version was set. I updated that value to the ruby version I had in my active project, then reinstalled ruby-lsp via Mason in the project, which worked.
For anyone who might be using AstroNvim, I was also able to solve this by changing the path to the ruby-lsp executable to point to my version manager shim:
-- astronvim/lua/plugins/astrolsp.lua
---@type LazySpec
return {
"AstroNvim/astrolsp",
---@type AstroLSPOpts
opts = {
config = {
ruby_lsp = {
cmd = {
"/Users/cbeach/.asdf/shims/ruby-lsp",
}
}
},
},
}
I'm still installing ruby-lsp the default AstroNvim way with Mason, and ruby-lsp operates as expected across different projects with different Ruby versions.
I'm using AstroNvim 5.1.3.
According to the ruby-lsp author (https://github.com/Shopify/ruby-lsp/issues/1248#issuecomment-1969404796), the LSP should be lauched from the ruby env of the project you work in, not from a 'global' ruby. So similarly to conradbeach above, pointing to the shims does the work. With rbenv and Lazyvim, it translated into the following config
{
"neovim/nvim-lspconfig",
opts = {
servers = {
...
ruby_lsp = {
cmd = { os.getenv("HOME") .. "/.rbenv/shims/ruby-lsp" },
},
...
}
}
}
Hi, I'm using Omakub, which defaults to Mise, NeoVim, and LazyVim with the LazyExtras Ruby language. This setup uses Mason under the hood to install Ruby-LSP and RuboCop, but fails when switching between projects with different Ruby versions. I needed to uninstall/reinstall Ruby-LSP and RuboCop every time I switched from project with different Ruby versions, which was annoying.
Based on @klaustopher's workaround, here is mine that seems to work fine in projects with different Ruby versions. Hopefully, it would help someone else:
~/.config/nvim/lua/plugins/lsp.lua
return {
"neovim/nvim-lspconfig",
opts = {
servers = {
ruby_lsp = {
mason = false,
cmd = { vim.fn.expand("~/.local/share/mise/shims/ruby-lsp") },
},
rubocop = {
mason = false,
cmd = { vim.fn.expand("~/.local/share/mise/shims/rubocop"), "--lsp" },
},
},
},
}
As this way skips Ruby-LSP and Rubocop installation via Mason we need to include ruby-lsp and rubocop gems in your project's Gemfile. Alternatively, you can install them by running gem install ruby-lsp rubocop. Ensure mise detects correctly your project ruby version before installing the gems.
This is my mise global config:
~/.config/mise/config.toml
[tools]
ruby = "3.2.2"
node = "lts"
[settings]
idiomatic_version_file_enable_tools = ["ruby"]
legacy_version_file = true
As this way skips Ruby-LSP and Rubocop installation via Mason we need to include
ruby-lspandrubocopgems in your project's Gemfile. Alternatively, you can install them by runninggem install ruby-lsp rubocop. Ensure mise detects correctly your project ruby version before installing the gems.
Thank you ❤️
This also works for asdf!
When manually installing the gems don't forget to reinstall the gems again 💎
While working on different projects that may include or not LSP dependencies in the Gemfile, I had to make some tweaks on my ruby language servers setup as follows.
Note: I'm using ASDF, but should with in a similar way with rbenv, rvm, etc.
- create a
lsp_utilsmodule with a function to check where the LSP server lives
-- Utility functions for LSP
local M = {}
---Finds the first executable command from a list of commands
---@param commands table[] List of command objects, each with 'cmd' and 'check' properties
---@return string First working command or fallback command
---
---Example:
---```lua
---local cmd = M.first_executable({
--- { cmd = "prettier", check = "command -v prettier" },
--- { cmd = "prettierd", check = "command -v prettierd" }
---})
---```
function M.check_executable(commands)
local cmd = 'exit 1'
for _, item in ipairs(commands or {}) do
cmd = item["cmd"]
local check = item["check"]
if os.execute(check .. ' &> /dev/null') == 0 then
return cmd
end
end
return cmd -- fallback to last command
end
return M
- When configuring ruby language servers, set the
cmdusing this function:
-- Ruby LSP
vim.lsp.config("ruby_lsp", {
cmd = lsp_utils.check_executable({
{ cmd = { "bundle", "exec", "ruby-lsp" }, check = "bundle exec ruby-lsp --version" },
{ cmd = { "asdf", "exec", "ruby-lsp" }, check = "asdf exec ruby-lsp --version" },
{ cmd = { "ruby-lsp" }, check = "ruby-lsp --version" },
}),
})
-- Rubocop
vim.lsp.config("rubocop", {
cmd = lsp_utils.check_executable({
{ cmd = { "bundle", "exec", "rubocop", "--lsp" }, check = "bundle exec rubocop --version" },
{ cmd = { "asdf", "exec", "rubocop", "--lsp" }, check = "asdf exec rubocop --version" },
{ cmd = { "rubocop", "--lsp" }, check = "rubocop --version" },
}),
})
-- Solargraph
vim.lsp.config("solargraph", {
cmd = lsp_utils.check_executable({
{ cmd = { "bundle", "exec", "solargraph", "stdio" }, check = "bundle exec solargraph --version" },
{ cmd = { "asdf", "exec", "solargraph", "stdio" }, check = "asdf exec solargraph --version" },
{ cmd = { "solargraph", "stdio" }, check = "solargraph --version" },
}),
})
-- Sorbet
vim.lsp.config("sorbet", {
cmd = lsp_utils.check_executable({
{ cmd = { "bundle", "exec", "srb", "tc", "--lsp", "--disable-watchman" }, check = "bundle exec srb --version" },
{ cmd = { "asdf", "exec", "srb", "tc", "--lsp", "--disable-watchman" }, check = "asdf exec srb --version" },
{ cmd = { "srb", "tc", "--lsp", "--disable-watchman" }, check = "srb --version" },
}),
})
This allows for conflicting setups, using the first available command among bundler installed, asdf shim, or the default mason installation.
Usage:
- Make sure you have a global ruby installation. It can be the system, or a default ruby set with
asdf set -u - Install the language server with
MasonInstallas normal. It will install using your global/user ruby - Configure LSPs with the settings above
- When available, Nvim will use the bundled version, asdf shim for current directory, or fallback to mason installed version.
Result of :checkhealth vim.lsp
vim.lsp: Active Clients ~
- rubocop (id: 1)
- Version: ? (no serverInfo.version response)
- Root directory: ~/Development/some-ruby-project
- Command: { "bundle", "exec", "rubocop", "--lsp" }
- Settings: {}
- Attached buffers: 4
- ruby_lsp (id: 2)
- Version: 0.26.1
- Root directory: ~/Development/some-ruby-project
- Command: { "asdf", "exec", "ruby-lsp" }
- Settings: {}
- Attached buffers: 4
- solargraph (id: 3)
- Version: ? (no serverInfo.version response)
- Root directory: ~/Development/some-ruby-project
- Command: { "asdf", "exec", "solargraph", "stdio" }
- Settings: {
solargraph = {
diagnostics = true
}
}
- Attached buffers: 4
- sorbet (id: 4)
- Version: ? (no serverInfo.version response)
- Root directory: ~/Development/some-ruby-project
- Command: { "bundle", "exec", "srb", "tc", "--lsp", "--disable-watchman" }
- Settings: {}
- Attached buffers: 4