My nvim configuration
| 1 | ---@brief |
| 2 | --- |
| 3 | --- https://github.com/rust-lang/rust-analyzer |
| 4 | --- |
| 5 | --- rust-analyzer (aka rls 2.0), a language server for Rust |
| 6 | --- |
| 7 | --- |
| 8 | --- See [docs](https://rust-analyzer.github.io/book/configuration.html) for extra settings. The settings can be used like this: |
| 9 | --- ```lua |
| 10 | --- vim.lsp.config('rust_analyzer', { |
| 11 | --- settings = { |
| 12 | --- ['rust-analyzer'] = { |
| 13 | --- diagnostics = { |
| 14 | --- enable = false; |
| 15 | --- } |
| 16 | --- } |
| 17 | --- } |
| 18 | --- }) |
| 19 | --- ``` |
| 20 | --- |
| 21 | --- Note: do not set `init_options` for this LS config, it will be automatically populated by the contents of settings["rust-analyzer"] per |
| 22 | --- https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26. |
| 23 | |
| 24 | local |
| 25 | local clients = vim.lsp.get_clients({ bufnr = bufnr, name = "rust_analyzer" }) |
| 26 | for _, client in ipairs(clients) do |
| 27 | vim.notify("Reloading Cargo Workspace") |
| 28 | ---@diagnostic disable-next-line:param-type-mismatch |
| 29 | client:request("rust-analyzer/reloadWorkspace", nil, function(err) |
| 30 | if err then |
| 31 | error(tostring(err)) |
| 32 | end |
| 33 | vim.notify("Cargo workspace reloaded") |
| 34 | end, 0) |
| 35 | end |
| 36 | end |
| 37 | |
| 38 | local |
| 39 | local user_home = vim.fs.normalize(vim.env.HOME) |
| 40 | local cargo_home = os.getenv("CARGO_HOME") or user_home .. "/.cargo" |
| 41 | local registry = cargo_home .. "/registry/src" |
| 42 | local git_registry = cargo_home .. "/git/checkouts" |
| 43 | |
| 44 | local rustup_home = os.getenv("RUSTUP_HOME") or user_home .. "/.rustup" |
| 45 | local toolchains = rustup_home .. "/toolchains" |
| 46 | |
| 47 | for _, item in ipairs({ toolchains, registry, git_registry }) do |
| 48 | if vim.fs.relpath(item, fname) then |
| 49 | local clients = vim.lsp.get_clients({ name = "rust_analyzer" }) |
| 50 | return #clients > 0 and clients[#clients].config.root_dir or nil |
| 51 | end |
| 52 | end |
| 53 | end |
| 54 | |
| 55 | ---@type vim.lsp.Config |
| 56 | return { |
| 57 | cmd = { "rust-analyzer" }, |
| 58 | filetypes = { "rust" }, |
| 59 | root_dir = function(bufnr, on_dir) |
| 60 | local fname = vim.api.nvim_buf_get_name(bufnr) |
| 61 | local reused_dir = is_library(fname) |
| 62 | if reused_dir then |
| 63 | on_dir(reused_dir) |
| 64 | return |
| 65 | end |
| 66 | |
| 67 | local cargo_crate_dir = vim.fs.root(fname, { "Cargo.toml" }) |
| 68 | local cargo_workspace_root |
| 69 | |
| 70 | if cargo_crate_dir == nil then |
| 71 | on_dir( |
| 72 | vim.fs.root(fname, { "rust-project.json" }) |
| 73 | or vim.fs.dirname(vim.fs.find(".git", { path = fname, upward = true })[1]) |
| 74 | ) |
| 75 | return |
| 76 | end |
| 77 | |
| 78 | local cmd = { |
| 79 | "cargo", |
| 80 | "metadata", |
| 81 | "--no-deps", |
| 82 | "--format-version", |
| 83 | "1", |
| 84 | "--manifest-path", |
| 85 | cargo_crate_dir .. "/Cargo.toml", |
| 86 | } |
| 87 | |
| 88 | vim.system(cmd, { text = true }, function(output) |
| 89 | if output.code == 0 then |
| 90 | if output.stdout then |
| 91 | local result = vim.json.decode(output.stdout) |
| 92 | if result["workspace_root"] then |
| 93 | cargo_workspace_root = vim.fs.normalize(result["workspace_root"]) |
| 94 | end |
| 95 | end |
| 96 | |
| 97 | on_dir(cargo_workspace_root or cargo_crate_dir) |
| 98 | else |
| 99 | vim.schedule(function() |
| 100 | vim.notify( |
| 101 | ("[rust_analyzer] cmd failed with code %d: %s\n%s"):format(output.code, cmd, output.stderr) |
| 102 | ) |
| 103 | end) |
| 104 | end |
| 105 | end) |
| 106 | end, |
| 107 | capabilities = { |
| 108 | experimental = { |
| 109 | serverStatusNotification = true, |
| 110 | commands = { |
| 111 | commands = { |
| 112 | "rust-analyzer.showReferences", |
| 113 | "rust-analyzer.runSingle", |
| 114 | "rust-analyzer.debugSingle", |
| 115 | }, |
| 116 | }, |
| 117 | }, |
| 118 | }, |
| 119 | settings = { |
| 120 | ["rust-analyzer"] = { |
| 121 | cargo = { features = "all" }, |
| 122 | diagnostics = { |
| 123 | disabled = { "inactive-code" }, |
| 124 | }, |
| 125 | lens = { |
| 126 | debug = { enable = true }, |
| 127 | enable = true, |
| 128 | implementations = { enable = true }, |
| 129 | references = { |
| 130 | adt = { enable = true }, |
| 131 | enumVariant = { enable = true }, |
| 132 | method = { enable = true }, |
| 133 | trait = { enable = true }, |
| 134 | }, |
| 135 | run = { enable = true }, |
| 136 | updateTest = { enable = true }, |
| 137 | }, |
| 138 | }, |
| 139 | }, |
| 140 | before_init = function(init_params, config) |
| 141 | -- See https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26 |
| 142 | if config.settings and config.settings["rust-analyzer"] then |
| 143 | init_params.initializationOptions = config.settings["rust-analyzer"] |
| 144 | end |
| 145 | ---@param command table{ title: string, command: string, arguments: any[] } |
| 146 | vim.lsp.commands["rust-analyzer.runSingle"] = function(command) |
| 147 | local r = command.arguments[1] |
| 148 | local cmd = { "cargo", unpack(r.args.cargoArgs) } |
| 149 | if r.args.executableArgs and #r.args.executableArgs > 0 then |
| 150 | vim.list_extend(cmd, { "--", unpack(r.args.executableArgs) }) |
| 151 | end |
| 152 | |
| 153 | local proc = vim.system(cmd, { cwd = r.args.cwd }) |
| 154 | |
| 155 | local result = proc:wait() |
| 156 | |
| 157 | if result.code == 0 then |
| 158 | vim.notify(result.stdout, vim.log.levels.INFO) |
| 159 | else |
| 160 | vim.notify(result.stderr, vim.log.levels.ERROR) |
| 161 | end |
| 162 | end |
| 163 | end, |
| 164 | on_attach = function(client, bufnr) |
| 165 | local navic = require("nvim-navic") |
| 166 | if client.server_capabilities.documentSymbolProvider then |
| 167 | navic.attach(client, bufnr) |
| 168 | end |
| 169 | |
| 170 | -- Disable semantic token "string" highlight for Rust so treesitter |
| 171 | -- injections (// html raw string literals) are not overridden |
| 172 | vim.api.nvim_set_hl(0, "@lsp.type.string.rust", {}) |
| 173 | |
| 174 | vim.api.nvim_buf_create_user_command(bufnr, "LspCargoReload", function() |
| 175 | reload_workspace(bufnr) |
| 176 | end, { desc = "Reload current cargo workspace" }) |
| 177 | |
| 178 | end, |
| 179 | } |