Skip to content

Commit caf7768

Browse files
Merge pull request rust/rustc-dev-guide#2814 from erfanio/main
Update neovim instructions for neovim 0.11+
2 parents ed46021 + 4885fed commit caf7768

1 file changed

Lines changed: 62 additions & 54 deletions

File tree

src/building/suggested.md

Lines changed: 62 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -144,77 +144,82 @@ If running `./x check` on save is inconvenient, in VS Code you can use a [Build
144144

145145
### Neovim
146146

147-
For Neovim users, there are a few options.
148-
The easiest way is by using [neoconf.nvim](https://github.com/folke/neoconf.nvim/),
149-
which allows for project-local configuration files with the native LSP.
150-
The steps for how to use it are below.
151-
Note that they require rust-analyzer to already be configured with Neovim.
152-
Steps for this can be [found here](https://rust-analyzer.github.io/manual.html#nvim-lsp).
147+
For Neovim users, there are a few options:
148+
149+
1. The easiest way is using [neoconf.nvim][neoconf.nvim] but it uses the
150+
deprecated `require('lspconfig')` API which displays a warning on neovim 0.11+.
151+
2. Using `coc.nvim` is another option but it requires node.js to be installed.
152+
3. Using a custom script to load rust-analyzer settings.
153+
154+
#### neoconf.nvim
155+
156+
[neoconf.nvim][neoconf.nvim] allows for project-local configuration
157+
files with the native LSP. The steps for how to use it are below. Note that they require
158+
rust-analyzer to already be configured with Neovim. Steps for this can be
159+
[found here][r-a nvim lsp].
153160

154161
1. First install the plugin.
155162
This can be done by following the steps in the README.
156163
2. Run `./x setup editor`, and select `vscode` to create a `.vscode/settings.json` file.
157164
`neoconf` is able to read and update
158165
rust-analyzer settings automatically when the project is opened when this file is detected.
159166

167+
#### coc.nvim
168+
160169
If you're using `coc.nvim`, you can run `./x setup editor` and select `vim` to
161170
create a `.vim/coc-settings.json`.
162171
The settings can be edited with `:CocLocalConfig`.
163172
The recommended settings live at [`src/etc/rust_analyzer_settings.json`].
164173

165-
Another way is without a plugin, and creating your own logic in your configuration.
166-
The following code will work for any checkout of rust-lang/rust (newer than February 2025):
174+
#### Custom LSP settings
175+
176+
If you're running neovim 0.11+, you can configure rust-analyzer with just
177+
[nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) and a custom script.
178+
179+
1. Make sure [rust-analyzer LSP][r-a nvim lsp] is set up
180+
2. Create `$HOME/.config/nvim/after/plugged/rust_analyzer.lua` with the following content:
167181

168182
```lua
169-
local function expand_config_variables(option)
170-
local var_placeholders = {
171-
['${workspaceFolder}'] = function(_)
172-
return vim.lsp.buf.list_workspace_folders()[1]
173-
end,
174-
}
175-
176-
if type(option) == "table" then
177-
local mt = getmetatable(option)
178-
local result = {}
179-
for k, v in pairs(option) do
180-
result[expand_config_variables(k)] = expand_config_variables(v)
183+
-- Capture the default functions from nvim-lspconfig/lsp/rust_analyzer.lua before overriding it.
184+
-- This file is in after/plugin to guarantee nvim-lspconfig has been initialised already.
185+
local default_root_dir = vim.lsp.config["rust_analyzer"].root_dir
186+
local default_before_init = vim.lsp.config["rust_analyzer"].before_init
187+
188+
vim.lsp.config("rust_analyzer", {
189+
cmd = { "rust-analyzer" },
190+
filetypes = { "rust" },
191+
-- To support rust-lang/rust, we need to detect when we're in the rust repo and use the git root
192+
-- instead of cargo project root.
193+
root_dir = function(bufnr, on_dir)
194+
local git_root = vim.fs.root(bufnr, { ".git" })
195+
if git_root then
196+
if vim.uv.fs_stat(vim.fs.joinpath(git_root, "src/etc/rust_analyzer_zed.json")) then
197+
on_dir(git_root)
198+
return
199+
end
181200
end
182-
return setmetatable(result, mt)
183-
end
184-
if type(option) ~= "string" then
185-
return option
186-
end
187-
local ret = option
188-
for key, fn in pairs(var_placeholders) do
189-
ret = ret:gsub(key, fn)
190-
end
191-
return ret
192-
end
193-
lspconfig.rust_analyzer.setup {
194-
root_dir = function()
195-
local default = lspconfig.rust_analyzer.config_def.default_config.root_dir()
196-
-- the default root detection uses the cargo workspace root.
197-
-- but for rust-lang/rust, the standard library is in its own workspace.
198-
-- use the git root instead.
199-
local compiler_config = vim.fs.joinpath(default, "../src/bootstrap/defaults/config.compiler.toml")
200-
if vim.fs.basename(default) == "library" and vim.uv.fs_stat(compiler_config) then
201-
return vim.fs.dirname(default)
202-
end
203-
return default
201+
-- For anything that doesn't match rust-lang/rust, fallback to default root_dir
202+
default_root_dir(bufnr, on_dir)
204203
end,
205-
on_init = function(client)
206-
local path = client.workspace_folders[1].name
207-
local config = vim.fs.joinpath(path, "src/etc/rust_analyzer_zed.json")
208-
if vim.uv.fs_stat(config) then
209-
-- load rust-lang/rust settings
210-
local file = io.open(config)
211-
local json = vim.json.decode(file:read("*a"))
212-
client.config.settings["rust-analyzer"] = expand_config_variables(json.lsp["rust-analyzer"].initialization_options)
213-
client.notify("workspace/didChangeConfiguration", { settings = client.config.settings })
204+
before_init = function(init_params, config)
205+
-- When inside rust-lang/rust, we need to use the special rust-analyzer settings.
206+
local settings = vim.fs.joinpath(config.root_dir, "src/etc/rust_analyzer_zed.json")
207+
if vim.uv.fs_stat(settings) then
208+
local file = io.open(settings)
209+
-- nvim 0.12+ supports comments otherwise you'll need content:gsub("//[^\n]*", "").
210+
local json = vim.json.decode(file:read("*a"), { skip_comments = true })
211+
file:close()
212+
config.settings["rust-analyzer"] = vim.tbl_deep_extend(
213+
"force", -- Overwrite with the special settings when there is a conflict.
214+
config.settings["rust-analyzer"] or {},
215+
json.lsp["rust-analyzer"].initialization_options
216+
)
214217
end
215-
return true
216-
end
217-
}
218+
default_before_init(init_params, config)
219+
end,
220+
})
221+
222+
vim.lsp.enable("rust_analyzer")
218223
```
219224

220225
If you would like to use the build task that is described above, you may either
@@ -223,6 +228,9 @@ make your own command in your config, or you can install a plugin such as
223228
files](https://github.com/stevearc/overseer.nvim/blob/master/doc/guides.md#vs-code-tasks),
224229
and follow the same instructions as above.
225230

231+
[neoconf.nvim]: https://github.com/folke/neoconf.nvim/
232+
[r-a nvim lsp]: https://rust-analyzer.github.io/book/other_editors.html#nvim-lsp
233+
226234
### Emacs
227235

228236
Emacs provides support for rust-analyzer with project-local configuration

0 commit comments

Comments
 (0)