From 6f553184fa40e32ec7a5c395a4a73b9bab414825 Mon Sep 17 00:00:00 2001 From: erfanio Date: Tue, 31 Mar 2026 22:28:45 +1100 Subject: [PATCH 1/5] Update neovim instructions for neovim 0.11+ --- src/building/suggested.md | 112 ++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/src/building/suggested.md b/src/building/suggested.md index 959d0687b..1f0630978 100644 --- a/src/building/suggested.md +++ b/src/building/suggested.md @@ -144,12 +144,19 @@ If running `./x check` on save is inconvenient, in VS Code you can use a [Build ### Neovim -For Neovim users, there are a few options. -The easiest way is by using [neoconf.nvim](https://github.com/folke/neoconf.nvim/), -which allows for project-local configuration files with the native LSP. -The steps for how to use it are below. -Note that they require rust-analyzer to already be configured with Neovim. -Steps for this can be [found here](https://rust-analyzer.github.io/manual.html#nvim-lsp). +For Neovim users, there are a few options: + +1. The easiest way is using [neoconf.nvim](https://github.com/folke/neoconf.nvim/) but it uses the + deprecated `require('lspconfig')` API which displays a warning on neovim 0.11+. +2. Using `coc.nvim` is another option but it requires node.js to be installed. +3. Using a custom script to load rust-analyzer settings. + +#### neoconf.nvim + +[neoconf.nvim](https://github.com/folke/neoconf.nvim/) allows for project-local configuration +files with the native LSP. The steps for how to use it are below. Note that they require +rust-analyzer to already be configured with Neovim. Steps for this can be +[found here](https://rust-analyzer.github.io/book/other_editors.html#nvim-lsp). 1. First install the plugin. This can be done by following the steps in the README. @@ -157,64 +164,61 @@ Steps for this can be [found here](https://rust-analyzer.github.io/manual.html#n `neoconf` is able to read and update rust-analyzer settings automatically when the project is opened when this file is detected. +#### coc.nvim + If you're using `coc.nvim`, you can run `./x setup editor` and select `vim` to create a `.vim/coc-settings.json`. The settings can be edited with `:CocLocalConfig`. The recommended settings live at [`src/etc/rust_analyzer_settings.json`]. -Another way is without a plugin, and creating your own logic in your configuration. -The following code will work for any checkout of rust-lang/rust (newer than February 2025): +#### Custom LSP settings + +If you're running neovim 0.11+, you can configure rust-analyzer with just +[nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) and a custom script. + +1. Make sure rust-analyzer LSP is set up + +2. Create `$HOME/.config/nvim/after/plugged/rust_analyzer.lua` with the following content: ```lua -local function expand_config_variables(option) - local var_placeholders = { - ['${workspaceFolder}'] = function(_) - return vim.lsp.buf.list_workspace_folders()[1] - end, - } - - if type(option) == "table" then - local mt = getmetatable(option) - local result = {} - for k, v in pairs(option) do - result[expand_config_variables(k)] = expand_config_variables(v) - end - return setmetatable(result, mt) - end - if type(option) ~= "string" then - return option - end - local ret = option - for key, fn in pairs(var_placeholders) do - ret = ret:gsub(key, fn) - end - return ret -end -lspconfig.rust_analyzer.setup { - root_dir = function() - local default = lspconfig.rust_analyzer.config_def.default_config.root_dir() - -- the default root detection uses the cargo workspace root. - -- but for rust-lang/rust, the standard library is in its own workspace. - -- use the git root instead. - local compiler_config = vim.fs.joinpath(default, "../src/bootstrap/defaults/config.compiler.toml") - if vim.fs.basename(default) == "library" and vim.uv.fs_stat(compiler_config) then - return vim.fs.dirname(default) +-- Capture the default functions from nvim-lspconfig/lsp/rust_analyzer.lua before overriding it. +-- This file is in after/plugin to guarantee nvim-lspconfig has been initialised already. +local default_root_dir = vim.lsp.config['rust_analyzer'].root_dir +local default_before_init = vim.lsp.config['rust_analyzer'].before_init + +vim.lsp.config('rust_analyzer', { + cmd = { 'rust-analyzer' }, + filetypes = { 'rust' }, + -- To support rust_lang/rust, we need to detect when we're in the rust repo and use the git root + -- instead of cargo project root. + root_dir = function(bufnr, on_dir) + local git_root = vim.fs.root(bufnr, { '.git' }) + if git_root then + if vim.uv.fs_stat(vim.fs.joinpath(git_root, "src/etc/rust_analyzer_zed.json")) then + on_dir(git_root) + return + end end - return default + -- For anything that doesn't match rust-lang/rust, fallback to default root_dir + default_root_dir(bufnr, on_dir) end, - on_init = function(client) - local path = client.workspace_folders[1].name - local config = vim.fs.joinpath(path, "src/etc/rust_analyzer_zed.json") - if vim.uv.fs_stat(config) then - -- load rust-lang/rust settings - local file = io.open(config) - local json = vim.json.decode(file:read("*a")) - client.config.settings["rust-analyzer"] = expand_config_variables(json.lsp["rust-analyzer"].initialization_options) - client.notify("workspace/didChangeConfiguration", { settings = client.config.settings }) + before_init = function(init_params, config) + -- When inside rust_lang/rust, we need to use the special rust_analyzer settings. + local settings = vim.fs.joinpath(config.root_dir, "src/etc/rust_analyzer_zed.json") + if vim.uv.fs_stat(settings) then + local file = io.open(settings) + local content = file:read("*a") + file:close() + -- vim.json.decode doesn't support JSONC so we need strip out comments. + content = content:gsub("//[^\n]*", "") + local json = vim.json.decode(content) + config.settings["rust-analyzer"] = json.lsp["rust-analyzer"].initialization_options end - return true - end -} + default_before_init(init_params, config) + end, +}) + +vim.lsp.enable('rust_analyzer') ``` If you would like to use the build task that is described above, you may either From 8638f543ab7eb4849a15b0687748af86a538a37d Mon Sep 17 00:00:00 2001 From: erfanio Date: Mon, 6 Apr 2026 11:45:05 +1000 Subject: [PATCH 2/5] Address code review comments --- src/building/suggested.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/building/suggested.md b/src/building/suggested.md index 1f0630978..f56f05adc 100644 --- a/src/building/suggested.md +++ b/src/building/suggested.md @@ -146,17 +146,17 @@ If running `./x check` on save is inconvenient, in VS Code you can use a [Build For Neovim users, there are a few options: -1. The easiest way is using [neoconf.nvim](https://github.com/folke/neoconf.nvim/) but it uses the +1. The easiest way is using [neoconf.nvim][neoconf.nvim] but it uses the deprecated `require('lspconfig')` API which displays a warning on neovim 0.11+. 2. Using `coc.nvim` is another option but it requires node.js to be installed. 3. Using a custom script to load rust-analyzer settings. #### neoconf.nvim -[neoconf.nvim](https://github.com/folke/neoconf.nvim/) allows for project-local configuration +[neoconf.nvim][neoconf.nvim] allows for project-local configuration files with the native LSP. The steps for how to use it are below. Note that they require rust-analyzer to already be configured with Neovim. Steps for this can be -[found here](https://rust-analyzer.github.io/book/other_editors.html#nvim-lsp). +[found here][r-a nvim lsp]. 1. First install the plugin. This can be done by following the steps in the README. @@ -176,8 +176,7 @@ The recommended settings live at [`src/etc/rust_analyzer_settings.json`]. If you're running neovim 0.11+, you can configure rust-analyzer with just [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) and a custom script. -1. Make sure rust-analyzer LSP is set up - +1. Make sure [rust-analyzer LSP][r-a nvim lsp] is set up 2. Create `$HOME/.config/nvim/after/plugged/rust_analyzer.lua` with the following content: ```lua @@ -189,7 +188,7 @@ local default_before_init = vim.lsp.config['rust_analyzer'].before_init vim.lsp.config('rust_analyzer', { cmd = { 'rust-analyzer' }, filetypes = { 'rust' }, - -- To support rust_lang/rust, we need to detect when we're in the rust repo and use the git root + -- To support rust-lang/rust, we need to detect when we're in the rust repo and use the git root -- instead of cargo project root. root_dir = function(bufnr, on_dir) local git_root = vim.fs.root(bufnr, { '.git' }) @@ -203,7 +202,7 @@ vim.lsp.config('rust_analyzer', { default_root_dir(bufnr, on_dir) end, before_init = function(init_params, config) - -- When inside rust_lang/rust, we need to use the special rust_analyzer settings. + -- When inside rust-lang/rust, we need to use the special rust_analyzer settings. local settings = vim.fs.joinpath(config.root_dir, "src/etc/rust_analyzer_zed.json") if vim.uv.fs_stat(settings) then local file = io.open(settings) @@ -227,6 +226,9 @@ make your own command in your config, or you can install a plugin such as files](https://github.com/stevearc/overseer.nvim/blob/master/doc/guides.md#vs-code-tasks), and follow the same instructions as above. +[neoconf.nvim]: https://github.com/folke/neoconf.nvim/ +[r-a nvim lsp]: https://rust-analyzer.github.io/book/other_editors.html#nvim-lsp + ### Emacs Emacs provides support for rust-analyzer with project-local configuration From e9afbb6dd5a328401a56c2d34ab3242ba92525de Mon Sep 17 00:00:00 2001 From: erfanio Date: Mon, 6 Apr 2026 11:55:39 +1000 Subject: [PATCH 3/5] Use skip_comments to strip comments --- src/building/suggested.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/building/suggested.md b/src/building/suggested.md index f56f05adc..54de2a06f 100644 --- a/src/building/suggested.md +++ b/src/building/suggested.md @@ -206,11 +206,9 @@ vim.lsp.config('rust_analyzer', { local settings = vim.fs.joinpath(config.root_dir, "src/etc/rust_analyzer_zed.json") if vim.uv.fs_stat(settings) then local file = io.open(settings) - local content = file:read("*a") + -- nvim 0.12+ supports comments otherwise you'll need content:gsub("//[^\n]*", ""). + local json = vim.json.decode(file:read("*a"), { skip_comments = true }) file:close() - -- vim.json.decode doesn't support JSONC so we need strip out comments. - content = content:gsub("//[^\n]*", "") - local json = vim.json.decode(content) config.settings["rust-analyzer"] = json.lsp["rust-analyzer"].initialization_options end default_before_init(init_params, config) From 1aac1bee4647dc0e20001d081fb57ef38169a0e4 Mon Sep 17 00:00:00 2001 From: erfanio Date: Mon, 6 Apr 2026 12:11:50 +1000 Subject: [PATCH 4/5] Use double quotes consistently --- src/building/suggested.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/building/suggested.md b/src/building/suggested.md index 54de2a06f..87f8dc4ef 100644 --- a/src/building/suggested.md +++ b/src/building/suggested.md @@ -182,16 +182,16 @@ If you're running neovim 0.11+, you can configure rust-analyzer with just ```lua -- Capture the default functions from nvim-lspconfig/lsp/rust_analyzer.lua before overriding it. -- This file is in after/plugin to guarantee nvim-lspconfig has been initialised already. -local default_root_dir = vim.lsp.config['rust_analyzer'].root_dir -local default_before_init = vim.lsp.config['rust_analyzer'].before_init +local default_root_dir = vim.lsp.config["rust_analyzer"].root_dir +local default_before_init = vim.lsp.config["rust_analyzer"].before_init -vim.lsp.config('rust_analyzer', { - cmd = { 'rust-analyzer' }, - filetypes = { 'rust' }, +vim.lsp.config("rust_analyzer", { + cmd = { "rust-analyzer" }, + filetypes = { "rust" }, -- To support rust-lang/rust, we need to detect when we're in the rust repo and use the git root -- instead of cargo project root. root_dir = function(bufnr, on_dir) - local git_root = vim.fs.root(bufnr, { '.git' }) + local git_root = vim.fs.root(bufnr, { ".git" }) if git_root then if vim.uv.fs_stat(vim.fs.joinpath(git_root, "src/etc/rust_analyzer_zed.json")) then on_dir(git_root) @@ -215,7 +215,7 @@ vim.lsp.config('rust_analyzer', { end, }) -vim.lsp.enable('rust_analyzer') +vim.lsp.enable("rust_analyzer") ``` If you would like to use the build task that is described above, you may either From 4885fed778f6543cd9cc09cc69a9800777aa7f4d Mon Sep 17 00:00:00 2001 From: erfanio Date: Mon, 6 Apr 2026 12:38:23 +1000 Subject: [PATCH 5/5] Merge rust-analyzer settings instead of overwriting --- src/building/suggested.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/building/suggested.md b/src/building/suggested.md index 87f8dc4ef..d9921cbb1 100644 --- a/src/building/suggested.md +++ b/src/building/suggested.md @@ -202,14 +202,18 @@ vim.lsp.config("rust_analyzer", { default_root_dir(bufnr, on_dir) end, before_init = function(init_params, config) - -- When inside rust-lang/rust, we need to use the special rust_analyzer settings. + -- When inside rust-lang/rust, we need to use the special rust-analyzer settings. local settings = vim.fs.joinpath(config.root_dir, "src/etc/rust_analyzer_zed.json") if vim.uv.fs_stat(settings) then local file = io.open(settings) -- nvim 0.12+ supports comments otherwise you'll need content:gsub("//[^\n]*", ""). local json = vim.json.decode(file:read("*a"), { skip_comments = true }) file:close() - config.settings["rust-analyzer"] = json.lsp["rust-analyzer"].initialization_options + config.settings["rust-analyzer"] = vim.tbl_deep_extend( + "force", -- Overwrite with the special settings when there is a conflict. + config.settings["rust-analyzer"] or {}, + json.lsp["rust-analyzer"].initialization_options + ) end default_before_init(init_params, config) end,