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