From f0a0b79e60ea25a305d3323d8c31c40bc9b4d65d Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Wed, 3 Jun 2026 01:31:44 +0100 Subject: [PATCH 01/22] feat(rust): enable rustaceanvim --- configuration.nix | 1 + modules/plugins/lsp/presets/rust-analyzer.nix | 193 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 modules/plugins/lsp/presets/rust-analyzer.nix diff --git a/configuration.nix b/configuration.nix index 8831dab7..3a29e8fe 100644 --- a/configuration.nix +++ b/configuration.nix @@ -74,6 +74,7 @@ isMaximal: { typst.enable = isMaximal; rust = { enable = isMaximal; + extensions.rustaceanvim.enable = isMaximal; extensions.crates-nvim.enable = isMaximal; }; toml.enable = isMaximal; diff --git a/modules/plugins/lsp/presets/rust-analyzer.nix b/modules/plugins/lsp/presets/rust-analyzer.nix new file mode 100644 index 00000000..0bd4d130 --- /dev/null +++ b/modules/plugins/lsp/presets/rust-analyzer.nix @@ -0,0 +1,193 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit (lib.meta) getExe; + inherit (lib.modules) mkIf; + inherit (lib.nvim.types) mkLspPresetEnableOption; + inherit (lib.nvim.dag) entryBefore; + inherit (lib.generators) mkLuaInline; + + cfg = config.vim.lsp.presets.rust-analyzer; +in { + options.vim.lsp.presets.rust-analyzer = { + enable = mkLspPresetEnableOption "rust-analyzer" "rust-analyzer" []; + }; + + # Note: do not set `init_options` for this LS config, it will be automatically populated by the contents of settings["rust-analyzer"] per + # https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26. + + config = mkIf cfg.enable { + luaConfigRC.rust-util = entryBefore ["lsp-servers"] '' + local function rust_reload_workspace(bufnr) + local clients = vim.lsp.get_clients { bufnr = bufnr, name = 'rust-analyzer' } + for _, client in ipairs(clients) do + vim.notify 'Reloading Cargo Workspace' + ---@diagnostic disable-next-line:param-type-mismatch + client:request('rust-analyzer/reloadWorkspace', nil, function(err) + if err then + error(tostring(err)) + end + vim.notify 'Cargo workspace reloaded' + end, 0) + end + end + + local function rust_user_sysroot_src() + return vim.tbl_get(vim.lsp.config['rust-analyzer'], 'settings', 'rust-analyzer', 'cargo', 'sysrootSrc') + end + + local function rust_default_sysroot_src() + local sysroot = vim.tbl_get(vim.lsp.config['rust-analyzer'], 'settings', 'rust-analyzer', 'cargo', 'sysroot') + if not sysroot then + local rustc = ${getExe pkgs.rustc} + local result = vim.system({ rustc, '--print', 'sysroot' }, { text = true }):wait() + + local stdout = result.stdout + if result.code == 0 and stdout then + if string.sub(stdout, #stdout) == '\n' then + if #stdout > 1 then + sysroot = string.sub(stdout, 1, #stdout - 1) + else + sysroot = ''''''; + end + else + sysroot = stdout + end + end + end + + return sysroot and vim.fs.joinpath(sysroot, 'lib/rustlib/src/rust/library') or nil + end + + local function rust_is_library(fname) + local user_home = vim.fs.normalize(vim.env.HOME) + local cargo_home = os.getenv 'CARGO_HOME' or user_home .. '/.cargo' + local registry = cargo_home .. '/registry/src' + local git_registry = cargo_home .. '/git/checkouts' + + local rustup_home = os.getenv 'RUSTUP_HOME' or user_home .. '/.rustup' + local toolchains = rustup_home .. '/toolchains' + + local sysroot_src = rust_user_sysroot_src() or default_sysroot_src() + + for _, item in ipairs { toolchains, registry, git_registry, sysroot_src } do + if item and vim.fs.relpath(item, fname) then + local clients = vim.lsp.get_clients { name = 'rust-analyzer' } + return #clients > 0 and clients[#clients].config.root_dir or nil + end + end + end + ''; + + vim.lsp.servers.rust-analyzer = { + enable = true; + cmd = [(getExe pkgs.rust-analyzer)]; + + on_attach = mkLuaInline '' + function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspCargoReload', function() + rust_reload_workspace(bufnr) + end, { desc = 'Reload current cargo workspace' }) + end + ''; + + root_dir = mkLuaInline '' + function(bufnr, on_dir) + local fname = vim.api.nvim_buf_get_name(bufnr) + local reused_dir = rust_is_library(fname) + if reused_dir then + on_dir(reused_dir) + return + end + + local cargo_crate_dir = vim.fs.root(fname, { 'Cargo.toml' }) + local cargo_workspace_root + + if cargo_crate_dir == nil then + on_dir( + vim.fs.root(fname, { 'rust-project.json' }) + or vim.fs.dirname(vim.fs.find('.git', { path = fname, upward = true })[1]) + ) + return + end + + local cmd = { + '${getExe pkgs.cargo}', + 'metadata', + '--no-deps', + '--format-version', + '1', + '--manifest-path', + cargo_crate_dir .. '/Cargo.toml', + } + + vim.system(cmd, { text = true }, function(output) + if output.code == 0 then + if output.stdout then + local result = vim.json.decode(output.stdout) + if result['workspace_root'] then + cargo_workspace_root = vim.fs.normalize(result['workspace_root']) + end + end + + on_dir(cargo_workspace_root or cargo_crate_dir) + else + vim.schedule(function() + vim.notify(('[rust_analyzer] cmd failed with code %d: %s\n%s'):format(output.code, cmd, output.stderr)) + end) + end + end) + end + ''; + + capabilities = { + experimental = { + serverStatusNotification = true; + commands = { + commands = [ + "rust-analyzer.showReferences" + "rust-analyzer.runSingle" + "rust-analyzer.debugSingle" + ]; + }; + }; + }; + settings = { + rust-analyzer = { + lens = { + debug = { + enable = true; + }; + enable = true; + implementations = { + enable = true; + }; + references = { + adt = { + enable = true; + }; + enumVariant = { + enable = true; + }; + method = { + enable = true; + }; + trait = { + enable = true; + }; + }; + run = { + enable = true; + }; + updateTest = { + enable = true; + }; + }; + }; + }; + }; + }; +} From 0a3b0cc33475fd8d789c9a833cc70802aed75f9a Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Wed, 3 Jun 2026 01:34:27 +0100 Subject: [PATCH 02/22] feat(rust): cleanup preset --- modules/plugins/lsp/presets/rust-analyzer.nix | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/plugins/lsp/presets/rust-analyzer.nix b/modules/plugins/lsp/presets/rust-analyzer.nix index 0bd4d130..41585d20 100644 --- a/modules/plugins/lsp/presets/rust-analyzer.nix +++ b/modules/plugins/lsp/presets/rust-analyzer.nix @@ -16,11 +16,13 @@ in { enable = mkLspPresetEnableOption "rust-analyzer" "rust-analyzer" []; }; + # TODO: rust-analyzer depends heavily on various other components such as rustc and cargo. This means that without these components available in path, rust-analyzer has minimal function other than highlighting. Should we add a minimal rustc/cargo in linked to stable for the meantime? Or set a warning up? The same is true for the config below, as they include references to cargo and rustc. + # Note: do not set `init_options` for this LS config, it will be automatically populated by the contents of settings["rust-analyzer"] per # https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26. config = mkIf cfg.enable { - luaConfigRC.rust-util = entryBefore ["lsp-servers"] '' + vim.luaConfigRC.rust-util = entryBefore ["lsp-servers"] '' local function rust_reload_workspace(bufnr) local clients = vim.lsp.get_clients { bufnr = bufnr, name = 'rust-analyzer' } for _, client in ipairs(clients) do @@ -42,7 +44,7 @@ in { local function rust_default_sysroot_src() local sysroot = vim.tbl_get(vim.lsp.config['rust-analyzer'], 'settings', 'rust-analyzer', 'cargo', 'sysroot') if not sysroot then - local rustc = ${getExe pkgs.rustc} + local rustc = '${getExe pkgs.rustc}' local result = vim.system({ rustc, '--print', 'sysroot' }, { text = true }):wait() local stdout = result.stdout @@ -51,7 +53,7 @@ in { if #stdout > 1 then sysroot = string.sub(stdout, 1, #stdout - 1) else - sysroot = ''''''; + sysroot = ''' end else sysroot = stdout @@ -71,7 +73,7 @@ in { local rustup_home = os.getenv 'RUSTUP_HOME' or user_home .. '/.rustup' local toolchains = rustup_home .. '/toolchains' - local sysroot_src = rust_user_sysroot_src() or default_sysroot_src() + local sysroot_src = rust_user_sysroot_src() or rust_default_sysroot_src() for _, item in ipairs { toolchains, registry, git_registry, sysroot_src } do if item and vim.fs.relpath(item, fname) then From 911f4426a4c6844d525f13ee92cd536e224ebc93 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Wed, 3 Jun 2026 01:34:47 +0100 Subject: [PATCH 03/22] feat(rust): add rust preset to list --- modules/plugins/lsp/presets/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/plugins/lsp/presets/default.nix b/modules/plugins/lsp/presets/default.nix index 4827f598..1dcace15 100644 --- a/modules/plugins/lsp/presets/default.nix +++ b/modules/plugins/lsp/presets/default.nix @@ -56,6 +56,7 @@ ./ruby-lsp.nix ./ruff.nix ./rumdl.nix + ./rust-analyzer.nix ./solargraph.nix ./some-sass-language-server.nix ./sqls.nix From 23b3413ee0fda8179f44f7036d93462b58155c58 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Wed, 3 Jun 2026 01:38:57 +0100 Subject: [PATCH 04/22] feat(rust): update main rust module --- modules/plugins/languages/rust.nix | 225 +++++++++++++++++------------ 1 file changed, 129 insertions(+), 96 deletions(-) diff --git a/modules/plugins/languages/rust.nix b/modules/plugins/languages/rust.nix index db12f033..1c40cfa4 100644 --- a/modules/plugins/languages/rust.nix +++ b/modules/plugins/languages/rust.nix @@ -5,19 +5,23 @@ ... }: let inherit (lib.meta) getExe; + inherit (lib) genAttrs; inherit (lib.modules) mkIf mkMerge; inherit (lib.options) mkOption mkEnableOption literalMD literalExpression; - inherit (lib.strings) optionalString; - inherit (lib.lists) isList; inherit (lib.attrsets) attrNames; - inherit (lib.types) bool package str listOf either enum int; - inherit (lib.nvim.lua) toLuaObject; + inherit (lib.types) bool package listOf enum int; inherit (lib.nvim.attrsets) mapListToAttrs; - inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption deprecatedSingleOrListOf; inherit (lib.nvim.dag) entryAfter entryAnywhere; + inherit (lib.nvim.lua) toLuaObject; + inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption deprecatedSingleOrListOf; + inherit (lib.strings) optionalString; + inherit (lib.generators) mkLuaInline; cfg = config.vim.languages.rust; + servers = ["rust-analyzer"]; + defaultServers = ["rust-analyzer"]; + defaultFormat = ["rustfmt"]; formats = { rustfmt = { @@ -40,31 +44,16 @@ in { lsp = { enable = - mkEnableOption "Rust LSP support (rust-analyzer with extra tools)" + mkEnableOption "Rust LSP support" // { default = config.vim.lsp.enable; defaultText = literalExpression "config.vim.lsp.enable"; }; - package = mkOption { - description = "rust-analyzer package, or the command to run as a list of strings"; - example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]''; - type = either package (listOf str); - default = pkgs.rust-analyzer; - }; - opts = mkOption { - description = "Options to pass to rust analyzer"; - type = str; - default = ""; - example = '' - ['rust-analyzer'] = { - cargo = {allFeature = true}, - checkOnSave = true, - procMacro = { - enable = true, - }, - }, - ''; + servers = mkOption { + type = listOf (enum servers); + default = defaultServers; + description = "Rust LSP server to use"; }; }; @@ -166,6 +155,96 @@ in { }; }; }; + + rustaceanvim = { + enable = mkEnableOption "additional rust support [rustaceanvim]"; + setupOpts = mkPluginSetupOption "rustaceanvim" { + tools = mkOption { + description = "Plugin configuration"; + default = { + hover_actions = { + replace_builtin_hover = false; + }; + }; + example = { + hover_actions = { + replace_builtin_hover = true; + }; + }; + }; + server = mkOption { + description = "LSP configuration"; + default = { + # For some reason rustaceanvim needs the command set explicitly, and does not pick up on vim.lsp.config settings. + cmd = [(getExe pkgs.rust-analyzer)]; + + on_attach = mkLuaInline '' + function(client, bufnr) + default_on_attach(client, bufnr) + local opts = { noremap=true, silent=true, buffer = bufnr } + + ${optionalString config.vim.vendoredKeymaps.enable '' + vim.keymap.set("n", "rr", ":RustLsp runnables", opts) + vim.keymap.set("n", "rp", ":RustLsp parentModule", opts) + vim.keymap.set("n", "rm", ":RustLsp expandMacro", opts) + vim.keymap.set("n", "rc", ":RustLsp openCargo", opts) + vim.keymap.set("n", "rg", ":RustLsp crateGraph x11", opts) + ''} + + ${optionalString (cfg.dap.enable && config.vim.vendoredKeymaps.enable) '' + vim.keymap.set("n", "rd", ":RustLsp debuggables", opts) + ''} + + ${optionalString (cfg.dap.enable && config.vim.debugger.nvim-dap.mappings.continue != null) '' + vim.keymap.set( + "n", "${config.vim.debugger.nvim-dap.mappings.continue}", + function() + local dap = require("dap") + if dap.status() == "" then + vim.cmd "RustLsp debuggables" + else + dap.continue() + end + end, + opts + ) + ''} + end + ''; + }; + example = { + on_attach = mkLuaInline '' + function(client, bufnr) + -- you can also put keymaps in here + end, + ''; + }; + }; + dap = mkOption { + description = "DAP configuration"; + default = { + adapter = + if cfg.dap.adapter == "lldb-dap" + then + mkLuaInline '' + { + type = "executable", + command = "${cfg.dap.package}/bin/lldb-dap", + name = "rustacean_lldb", + }'' + else let + codelldb = pkgs.vscode-extensions.vadimcn.vscode-lldb.adapter; + codelldbPath = "${codelldb}/bin/codelldb"; + liblldbPath = "${codelldb}/share/lldb/lib/liblldb.so"; + in + mkLuaInline '' + require("rustaceanvim.config").get_codelldb_adapter("${codelldbPath}", "${liblldbPath}") + ''; + }; + example = {}; + }; + }; + }; }; }; @@ -190,84 +269,38 @@ in { }; }) - (mkIf (cfg.lsp.enable || cfg.dap.enable) { + (mkIf cfg.lsp.enable { + vim.lsp = { + presets = genAttrs cfg.lsp.servers (_: {enable = true;}); + servers = genAttrs cfg.lsp.servers (_: { + filetypes = [ + "rust" + ]; + }); + }; + }) + + (mkIf cfg.extensions.rustaceanvim.enable { vim = { + lsp.servers.rust-analyzer.enable = lib.mkForce false; + lsp.servers.rust-analyzer.root_dir = lib.mkForce null; # let rustaceanvim determine root + lsp.servers.rust-analyzer.on_attach = lib.mkForce null; # let rustaceanvim determine attach funcs + startPlugins = ["rustaceanvim"]; pluginRC.rustaceanvim = entryAfter ["lsp-setup"] '' - vim.g.rustaceanvim = { - ${optionalString cfg.lsp.enable '' - -- LSP - tools = { - hover_actions = { - replace_builtin_hover = false - }, - }, - server = { - cmd = ${ - if isList cfg.lsp.package - then toLuaObject cfg.lsp.package - else ''{"${cfg.lsp.package}/bin/rust-analyzer"}'' - }, - default_settings = { - ${cfg.lsp.opts} - }, - on_attach = function(client, bufnr) - default_on_attach(client, bufnr) - local opts = { noremap=true, silent=true, buffer = bufnr } - - ${optionalString config.vim.vendoredKeymaps.enable '' - vim.keymap.set("n", "rr", ":RustLsp runnables", opts) - vim.keymap.set("n", "rp", ":RustLsp parentModule", opts) - vim.keymap.set("n", "rm", ":RustLsp expandMacro", opts) - vim.keymap.set("n", "rc", ":RustLsp openCargo", opts) - vim.keymap.set("n", "rg", ":RustLsp crateGraph x11", opts) - ''} - - ${optionalString (cfg.dap.enable && config.vim.vendoredKeymaps.enable) '' - vim.keymap.set("n", "rd", ":RustLsp debuggables", opts) - ''} - - ${optionalString (cfg.dap.enable && config.vim.debugger.nvim-dap.mappings.continue != null) '' - vim.keymap.set( - "n", "${config.vim.debugger.nvim-dap.mappings.continue}", - function() - local dap = require("dap") - if dap.status() == "" then - vim.cmd "RustLsp debuggables" - else - dap.continue() - end - end, - opts - ) - ''} - end - }, - ''} - - ${optionalString cfg.dap.enable '' - dap = { - adapter = ${ - if cfg.dap.adapter == "lldb-dap" - then '' - { - type = "executable", - command = "${cfg.dap.package}/bin/lldb-dap", - name = "rustacean_lldb", - }'' - else let - codelldb = pkgs.vscode-extensions.vadimcn.vscode-lldb.adapter; - codelldbPath = "${codelldb}/bin/codelldb"; - liblldbPath = "${codelldb}/share/lldb/lib/liblldb.so"; - in ''require("rustaceanvim.config").get_codelldb_adapter("${codelldbPath}", "${liblldbPath}")'' - }, - }, - ''} - } + vim.g.rustaceanvim = function() + return { + tools = ${toLuaObject cfg.extensions.rustaceanvim.setupOpts.tools}, + server = ${toLuaObject cfg.extensions.rustaceanvim.setupOpts.server}, + dap = ${toLuaObject cfg.extensions.rustaceanvim.setupOpts.dap}, + } + end ''; }; }) + (mkIf cfg.extensions.rustaceanvim {}) + (mkIf cfg.extensions.crates-nvim.enable { vim = mkMerge [ { From cc926bac934d05161cfa1720852ce00b869def9c Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Wed, 3 Jun 2026 01:47:16 +0100 Subject: [PATCH 05/22] fmt --- docs/manual/release-notes/rl-0.9.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/manual/release-notes/rl-0.9.md b/docs/manual/release-notes/rl-0.9.md index b819ba83..90fee6fc 100644 --- a/docs/manual/release-notes/rl-0.9.md +++ b/docs/manual/release-notes/rl-0.9.md @@ -128,6 +128,12 @@ - Turned `omnisharp-extended-lsp-nvim` into an extension disabled by default - Turned `csharpls-extended-lsp-nvim` into an extension disabled by default +[sjcobb2022](https://github.com/caueanjos) + +- Rust module is now no longer dependant on `rustaceanvim` by default. Use + `vim.languages.rust.extensions.rustaceanvim.enable` if it is necessary in the + workflow. + ## Changelog {#sec-release-0-9-changelog} [ErinaYip](https://github.com/ErinaYip): @@ -580,4 +586,10 @@ https://github.com/gorbit99/codewindow.nvim - Add `prettier` and `prettierd` as supported formatters to `vim.languages.json`. +[sjcobb2022](https://github.com/caueanjos) + +- Modernize rust toolchain by defaulting to a `rustaceanvim` free default + configuration. Enabled via configuration + `vim.languages.rust.extensions.rustaceanvim.enable`. + From 6044473226bf0c5123a63c6ade6fff581368ea7c Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Wed, 3 Jun 2026 01:50:55 +0100 Subject: [PATCH 06/22] feat(rust): update rl-notes --- docs/manual/release-notes/rl-0.9.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/manual/release-notes/rl-0.9.md b/docs/manual/release-notes/rl-0.9.md index 90fee6fc..fd57e404 100644 --- a/docs/manual/release-notes/rl-0.9.md +++ b/docs/manual/release-notes/rl-0.9.md @@ -131,8 +131,7 @@ [sjcobb2022](https://github.com/caueanjos) - Rust module is now no longer dependant on `rustaceanvim` by default. Use - `vim.languages.rust.extensions.rustaceanvim.enable` if it is necessary in the - workflow. + `vim.languages.rust.extensions.rustaceanvim.enable` if needed. ## Changelog {#sec-release-0-9-changelog} @@ -586,7 +585,7 @@ https://github.com/gorbit99/codewindow.nvim - Add `prettier` and `prettierd` as supported formatters to `vim.languages.json`. -[sjcobb2022](https://github.com/caueanjos) +[sjcobb2022](https://github.com/sjcobb2022) - Modernize rust toolchain by defaulting to a `rustaceanvim` free default configuration. Enabled via configuration From fc9950a5bffede4dfd7075fa9bf0b77ba514430b Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Wed, 3 Jun 2026 01:54:01 +0100 Subject: [PATCH 07/22] feat(rust): some more comments --- modules/plugins/languages/rust.nix | 1 + modules/plugins/lsp/presets/rust-analyzer.nix | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/plugins/languages/rust.nix b/modules/plugins/languages/rust.nix index 1c40cfa4..fdac0131 100644 --- a/modules/plugins/languages/rust.nix +++ b/modules/plugins/languages/rust.nix @@ -282,6 +282,7 @@ in { (mkIf cfg.extensions.rustaceanvim.enable { vim = { + # TODO: Determine if mkForce is the best thing here, and if we should consider a warning instead? Or use priority settings. lsp.servers.rust-analyzer.enable = lib.mkForce false; lsp.servers.rust-analyzer.root_dir = lib.mkForce null; # let rustaceanvim determine root lsp.servers.rust-analyzer.on_attach = lib.mkForce null; # let rustaceanvim determine attach funcs diff --git a/modules/plugins/lsp/presets/rust-analyzer.nix b/modules/plugins/lsp/presets/rust-analyzer.nix index 41585d20..56200e24 100644 --- a/modules/plugins/lsp/presets/rust-analyzer.nix +++ b/modules/plugins/lsp/presets/rust-analyzer.nix @@ -22,6 +22,7 @@ in { # https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26. config = mkIf cfg.enable { + # Taken from https://github.com/neovim/nvim-lspconfig/blob/07dff35e7c95288861200b788ef32d6103f107f0/lsp/rust_analyzer.lua vim.luaConfigRC.rust-util = entryBefore ["lsp-servers"] '' local function rust_reload_workspace(bufnr) local clients = vim.lsp.get_clients { bufnr = bufnr, name = 'rust-analyzer' } From 8a9bc1ae06d36579b18d9bb95cec131e08d2634b Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Wed, 3 Jun 2026 02:25:38 +0100 Subject: [PATCH 08/22] feat(rust): remove unused import --- modules/plugins/languages/rust.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/plugins/languages/rust.nix b/modules/plugins/languages/rust.nix index fdac0131..803717df 100644 --- a/modules/plugins/languages/rust.nix +++ b/modules/plugins/languages/rust.nix @@ -11,7 +11,7 @@ inherit (lib.attrsets) attrNames; inherit (lib.types) bool package listOf enum int; inherit (lib.nvim.attrsets) mapListToAttrs; - inherit (lib.nvim.dag) entryAfter entryAnywhere; + inherit (lib.nvim.dag) entryAfter; inherit (lib.nvim.lua) toLuaObject; inherit (lib.nvim.types) mkGrammarOption mkPluginSetupOption deprecatedSingleOrListOf; inherit (lib.strings) optionalString; From 18548e06a58af0da9d6bfb8d51a78a68b9a70365 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Fri, 5 Jun 2026 03:25:40 +0100 Subject: [PATCH 09/22] feat(rust): fix most nits --- modules/plugins/lsp/presets/rust-analyzer.nix | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/modules/plugins/lsp/presets/rust-analyzer.nix b/modules/plugins/lsp/presets/rust-analyzer.nix index 56200e24..2745964c 100644 --- a/modules/plugins/lsp/presets/rust-analyzer.nix +++ b/modules/plugins/lsp/presets/rust-analyzer.nix @@ -13,17 +13,12 @@ cfg = config.vim.lsp.presets.rust-analyzer; in { options.vim.lsp.presets.rust-analyzer = { - enable = mkLspPresetEnableOption "rust-analyzer" "rust-analyzer" []; + enable = mkLspPresetEnableOption "rust-analyzer" "Rust Analyzer" []; }; - # TODO: rust-analyzer depends heavily on various other components such as rustc and cargo. This means that without these components available in path, rust-analyzer has minimal function other than highlighting. Should we add a minimal rustc/cargo in linked to stable for the meantime? Or set a warning up? The same is true for the config below, as they include references to cargo and rustc. - - # Note: do not set `init_options` for this LS config, it will be automatically populated by the contents of settings["rust-analyzer"] per - # https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26. - config = mkIf cfg.enable { # Taken from https://github.com/neovim/nvim-lspconfig/blob/07dff35e7c95288861200b788ef32d6103f107f0/lsp/rust_analyzer.lua - vim.luaConfigRC.rust-util = entryBefore ["lsp-servers"] '' + vim.luaConfigRC.rust-analyzer = entryBefore ["lsp-servers"] '' local function rust_reload_workspace(bufnr) local clients = vim.lsp.get_clients { bufnr = bufnr, name = 'rust-analyzer' } for _, client in ipairs(clients) do From 32ed208492631ef891eb57507e53773cf14e0fee Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Fri, 5 Jun 2026 03:31:18 +0100 Subject: [PATCH 10/22] feat(rust): clean up core rust module --- modules/plugins/languages/rust.nix | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/modules/plugins/languages/rust.nix b/modules/plugins/languages/rust.nix index 803717df..e20d8629 100644 --- a/modules/plugins/languages/rust.nix +++ b/modules/plugins/languages/rust.nix @@ -290,18 +290,12 @@ in { startPlugins = ["rustaceanvim"]; pluginRC.rustaceanvim = entryAfter ["lsp-setup"] '' vim.g.rustaceanvim = function() - return { - tools = ${toLuaObject cfg.extensions.rustaceanvim.setupOpts.tools}, - server = ${toLuaObject cfg.extensions.rustaceanvim.setupOpts.server}, - dap = ${toLuaObject cfg.extensions.rustaceanvim.setupOpts.dap}, - } + return ${toLuaObject cfg.extensions.rustaceanvim.setupOpts} end ''; }; }) - (mkIf cfg.extensions.rustaceanvim {}) - (mkIf cfg.extensions.crates-nvim.enable { vim = mkMerge [ { From 84a1b66f2becc2c0cd27dfe5c86b56728c78ca6a Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Fri, 5 Jun 2026 03:31:28 +0100 Subject: [PATCH 11/22] feat(rust): add to deprecations --- modules/extra/deprecations.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/extra/deprecations.nix b/modules/extra/deprecations.nix index 6ad2e633..33384716 100644 --- a/modules/extra/deprecations.nix +++ b/modules/extra/deprecations.nix @@ -378,5 +378,11 @@ in { [ (mkRenamedOptionModule ["vim" "languages" "typescript" "treesitter" "tsxPackage"] ["vim" "languages" "tsx" "treesitter" "package"]) ] + + # 2026-06-05 + [ + (mkRemovedLspPackage "rust") + (mkRemovedLspOpt "rust") + ] ]; } From 7109ec7284424f5b7e1f416840c92238e0d447b7 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Fri, 5 Jun 2026 03:39:29 +0100 Subject: [PATCH 12/22] feat(rust): remove now default settings --- modules/plugins/lsp/presets/rust-analyzer.nix | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/modules/plugins/lsp/presets/rust-analyzer.nix b/modules/plugins/lsp/presets/rust-analyzer.nix index 2745964c..027fde80 100644 --- a/modules/plugins/lsp/presets/rust-analyzer.nix +++ b/modules/plugins/lsp/presets/rust-analyzer.nix @@ -153,39 +153,6 @@ in { }; }; }; - settings = { - rust-analyzer = { - lens = { - debug = { - enable = true; - }; - enable = true; - implementations = { - enable = true; - }; - references = { - adt = { - enable = true; - }; - enumVariant = { - enable = true; - }; - method = { - enable = true; - }; - trait = { - enable = true; - }; - }; - run = { - enable = true; - }; - updateTest = { - enable = true; - }; - }; - }; - }; }; }; } From ac5827a619af64d668cd10132005b118340d56de Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Fri, 5 Jun 2026 03:53:23 +0100 Subject: [PATCH 13/22] feat(rust): comment more thoroughly --- modules/plugins/lsp/presets/rust-analyzer.nix | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/modules/plugins/lsp/presets/rust-analyzer.nix b/modules/plugins/lsp/presets/rust-analyzer.nix index 027fde80..135985ba 100644 --- a/modules/plugins/lsp/presets/rust-analyzer.nix +++ b/modules/plugins/lsp/presets/rust-analyzer.nix @@ -18,6 +18,8 @@ in { config = mkIf cfg.enable { # Taken from https://github.com/neovim/nvim-lspconfig/blob/07dff35e7c95288861200b788ef32d6103f107f0/lsp/rust_analyzer.lua + + # This code provides utility for cargo workspace functionality, as it is not wired by default. vim.luaConfigRC.rust-analyzer = entryBefore ["lsp-servers"] '' local function rust_reload_workspace(bufnr) local clients = vim.lsp.get_clients { bufnr = bufnr, name = 'rust-analyzer' } @@ -37,6 +39,7 @@ in { return vim.tbl_get(vim.lsp.config['rust-analyzer'], 'settings', 'rust-analyzer', 'cargo', 'sysrootSrc') end + -- Determine location of sysroot for stdlib local function rust_default_sysroot_src() local sysroot = vim.tbl_get(vim.lsp.config['rust-analyzer'], 'settings', 'rust-analyzer', 'cargo', 'sysroot') if not sysroot then @@ -60,6 +63,7 @@ in { return sysroot and vim.fs.joinpath(sysroot, 'lib/rustlib/src/rust/library') or nil end + -- Determine if a given file belongs to an external library or our own code. local function rust_is_library(fname) local user_home = vim.fs.normalize(vim.env.HOME) local cargo_home = os.getenv 'CARGO_HOME' or user_home .. '/.cargo' @@ -92,6 +96,36 @@ in { end ''; + # Sends init_params beforehand according to rust-analyzer spec. + before_init = mkLuaInline '' + function(init_params, config) + See https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26 + if config.settings and config.settings['rust-analyzer'] then + init_params.initializationOptions = config.settings['rust-analyzer'] + end + + -- Allow for a single run of a program + vim.lsp.commands['rust-analyzer.runSingle'] = function(command) + local r = command.arguments[1] + local cmd = { 'cargo', unpack(r.args.cargoArgs) } + if r.args.executableArgs and #r.args.executableArgs > 0 then + vim.list_extend(cmd, { '--', unpack(r.args.executableArgs) }) + end + + local proc = vim.system(cmd, { cwd = r.args.cwd, env = r.args.environment }) + + local result = proc:wait() + + if result.code == 0 then + vim.notify(result.stdout, vim.log.levels.INFO) + else + vim.notify(result.stderr, vim.log.levels.ERROR) + end + end + end + ''; + + # eval root dir taking workspaces into consideration root_dir = mkLuaInline '' function(bufnr, on_dir) local fname = vim.api.nvim_buf_get_name(bufnr) From ad13549beb0bfb18e7205a789026dd127049fe99 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Fri, 5 Jun 2026 04:06:23 +0100 Subject: [PATCH 14/22] feat(rust): add assertion and comment on configuration --- configuration.nix | 3 ++- modules/plugins/languages/rust.nix | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/configuration.nix b/configuration.nix index 3a29e8fe..8182b06e 100644 --- a/configuration.nix +++ b/configuration.nix @@ -74,7 +74,8 @@ isMaximal: { typst.enable = isMaximal; rust = { enable = isMaximal; - extensions.rustaceanvim.enable = isMaximal; + # Can only be enabled if lsp.enable = false + extensions.rustaceanvim.enable = false; extensions.crates-nvim.enable = isMaximal; }; toml.enable = isMaximal; diff --git a/modules/plugins/languages/rust.nix b/modules/plugins/languages/rust.nix index e20d8629..136a01b6 100644 --- a/modules/plugins/languages/rust.nix +++ b/modules/plugins/languages/rust.nix @@ -282,11 +282,6 @@ in { (mkIf cfg.extensions.rustaceanvim.enable { vim = { - # TODO: Determine if mkForce is the best thing here, and if we should consider a warning instead? Or use priority settings. - lsp.servers.rust-analyzer.enable = lib.mkForce false; - lsp.servers.rust-analyzer.root_dir = lib.mkForce null; # let rustaceanvim determine root - lsp.servers.rust-analyzer.on_attach = lib.mkForce null; # let rustaceanvim determine attach funcs - startPlugins = ["rustaceanvim"]; pluginRC.rustaceanvim = entryAfter ["lsp-setup"] '' vim.g.rustaceanvim = function() @@ -294,6 +289,13 @@ in { end ''; }; + + assertions = [ + { + assertion = !cfg.lsp.enable; + message = "rustaceanvim and vim.languages.rust.lsp.enable are mutually exclusive. Please ensure `vim.lsp.rust-analyzer.enable` is false, or disable `vim.languages.rust.lsp.enable`."; + } + ]; }) (mkIf cfg.extensions.crates-nvim.enable { From 1e9fe53cba6cc636d6d3f975316bd8d17a286728 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Fri, 5 Jun 2026 15:53:34 +0100 Subject: [PATCH 15/22] feat(rust): update comment --- modules/plugins/languages/rust.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/plugins/languages/rust.nix b/modules/plugins/languages/rust.nix index 136a01b6..d23837d9 100644 --- a/modules/plugins/languages/rust.nix +++ b/modules/plugins/languages/rust.nix @@ -293,7 +293,7 @@ in { assertions = [ { assertion = !cfg.lsp.enable; - message = "rustaceanvim and vim.languages.rust.lsp.enable are mutually exclusive. Please ensure `vim.lsp.rust-analyzer.enable` is false, or disable `vim.languages.rust.lsp.enable`."; + message = "rustaceanvim and `vim.languages.rust.lsp.enable` are mutually exclusive. Please ensure `vim.lsp.rust-analyzer.enable` is false, or disable `vim.languages.rust.lsp.enable`."; } ]; }) From 9852b32d4199d2f94f885265fabd7e388950afee Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Sat, 6 Jun 2026 00:55:31 +0100 Subject: [PATCH 16/22] feat(rust): add mkLspPresetOptionWithDesc and be more explicit with punctuation --- lib/types/lsp.nix | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/types/lsp.nix b/lib/types/lsp.nix index d05b8783..be2a994e 100644 --- a/lib/types/lsp.nix +++ b/lib/types/lsp.nix @@ -1,12 +1,23 @@ {lib}: let - inherit (lib.options) mkEnableOption; + inherit (lib.options) mkOption; mkLspPresetEnableOption = option: display: fileTypes: - mkEnableOption '' - the ${display} Language Server. - Default `filetypes = ${lib.generators.toPretty {} fileTypes}`. - Use {option}`vim.lsp.servers.${option}` for customization - ''; + mkLspPresetEnableOptionWithDesc option display fileTypes ""; + + mkLspPresetEnableOptionWithDesc = option: display: fileTypes: description: + mkOption { + type = lib.types.bool; + default = false; + description = lib.removeSuffix "\n" ('' + the ${display} Language Server. + Default `filetypes = ${lib.generators.toPretty {} fileTypes}`. + Use {option}`vim.lsp.servers.${option}` for customization. + '' + + lib.optionalString (description != "") '' + + ${description} + ''); + }; in { - inherit mkLspPresetEnableOption; + inherit mkLspPresetEnableOption mkLspPresetEnableOptionWithDesc; } From dedbe5e9db0853b4221407de860c69f727262a89 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Sat, 6 Jun 2026 00:56:01 +0100 Subject: [PATCH 17/22] feat(rust): export properly --- lib/types/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/types/default.nix b/lib/types/default.nix index 57fb43fe..d4247df9 100644 --- a/lib/types/default.nix +++ b/lib/types/default.nix @@ -11,6 +11,6 @@ in { inherit (typesDag) dagOf; inherit (typesPlugin) pluginsOpt extraPluginType mkPluginSetupOption luaInline pluginType borderType; inherit (typesLanguage) diagnostics mkGrammarOption mkTreesitterGrammarOption; - inherit (typesLsp) mkLspPresetEnableOption; + inherit (typesLsp) mkLspPresetEnableOption mkLspPresetEnableOptionWithDesc; inherit (customTypes) char hexColor mergelessListOf deprecatedSingleOrListOf enumWithRename; } From 612c2d1386c749463e937997d8df9787ffbe7c24 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Sat, 6 Jun 2026 00:56:25 +0100 Subject: [PATCH 18/22] feat(rust): add better assertions --- modules/plugins/languages/rust.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/plugins/languages/rust.nix b/modules/plugins/languages/rust.nix index d23837d9..c61ff7c8 100644 --- a/modules/plugins/languages/rust.nix +++ b/modules/plugins/languages/rust.nix @@ -292,8 +292,8 @@ in { assertions = [ { - assertion = !cfg.lsp.enable; - message = "rustaceanvim and `vim.languages.rust.lsp.enable` are mutually exclusive. Please ensure `vim.lsp.rust-analyzer.enable` is false, or disable `vim.languages.rust.lsp.enable`."; + assertion = !(builtins.elem "rust-analyzer" cfg.lsp.server) && !config.vim.lsp.rust-analyzer.enable; + message = "rustaceanvim and rust-analyzer are mutually exclusive. Please ensure that rust-analyzer is disabled."; } ]; }) From 3382ea0930a57816512f67428df065f0c4918ed9 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Sat, 6 Jun 2026 00:57:43 +0100 Subject: [PATCH 19/22] feat(rust): update capitilization --- lib/types/lsp.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/types/lsp.nix b/lib/types/lsp.nix index be2a994e..7e9ce1ba 100644 --- a/lib/types/lsp.nix +++ b/lib/types/lsp.nix @@ -9,7 +9,7 @@ type = lib.types.bool; default = false; description = lib.removeSuffix "\n" ('' - the ${display} Language Server. + The ${display} Language Server. Default `filetypes = ${lib.generators.toPretty {} fileTypes}`. Use {option}`vim.lsp.servers.${option}` for customization. '' From eaf6d63da923a63966f08776d48ad3cd846cf0a9 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Sat, 6 Jun 2026 00:58:24 +0100 Subject: [PATCH 20/22] feat(rust): remove hard dependency on cargo and rustc, use updated option --- modules/plugins/lsp/presets/rust-analyzer.nix | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/plugins/lsp/presets/rust-analyzer.nix b/modules/plugins/lsp/presets/rust-analyzer.nix index 135985ba..df985160 100644 --- a/modules/plugins/lsp/presets/rust-analyzer.nix +++ b/modules/plugins/lsp/presets/rust-analyzer.nix @@ -6,14 +6,15 @@ }: let inherit (lib.meta) getExe; inherit (lib.modules) mkIf; - inherit (lib.nvim.types) mkLspPresetEnableOption; + inherit (lib.nvim.types) mkLspPresetEnableOptionWithDesc; inherit (lib.nvim.dag) entryBefore; inherit (lib.generators) mkLuaInline; cfg = config.vim.lsp.presets.rust-analyzer; in { options.vim.lsp.presets.rust-analyzer = { - enable = mkLspPresetEnableOption "rust-analyzer" "Rust Analyzer" []; + enable = + mkLspPresetEnableOptionWithDesc "rust-analyzer" "Rust Analyzer" [] ''Note: do not set `init_options` for this LS config, it will be automatically populated by the contents of settings["rust-analyzer"] per https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26''; }; config = mkIf cfg.enable { @@ -43,7 +44,7 @@ in { local function rust_default_sysroot_src() local sysroot = vim.tbl_get(vim.lsp.config['rust-analyzer'], 'settings', 'rust-analyzer', 'cargo', 'sysroot') if not sysroot then - local rustc = '${getExe pkgs.rustc}' + local rustc = os.getenv 'RUSTC' or 'rustc' local result = vim.system({ rustc, '--print', 'sysroot' }, { text = true }):wait() local stdout = result.stdout @@ -97,9 +98,9 @@ in { ''; # Sends init_params beforehand according to rust-analyzer spec. + # See https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26 before_init = mkLuaInline '' function(init_params, config) - See https://github.com/rust-lang/rust-analyzer/blob/eb5da56d839ae0a9e9f50774fa3eb78eb0964550/docs/dev/lsp-extensions.md?plain=1#L26 if config.settings and config.settings['rust-analyzer'] then init_params.initializationOptions = config.settings['rust-analyzer'] end @@ -147,7 +148,7 @@ in { end local cmd = { - '${getExe pkgs.cargo}', + 'cargo', 'metadata', '--no-deps', '--format-version', From 9e3818e16e9d4e5e36a016f1a01893c49deea355 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Sat, 6 Jun 2026 15:36:17 +0100 Subject: [PATCH 21/22] feat(rust): clean assertion --- modules/plugins/languages/rust.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/plugins/languages/rust.nix b/modules/plugins/languages/rust.nix index c61ff7c8..aa0b711f 100644 --- a/modules/plugins/languages/rust.nix +++ b/modules/plugins/languages/rust.nix @@ -293,7 +293,10 @@ in { assertions = [ { assertion = !(builtins.elem "rust-analyzer" cfg.lsp.server) && !config.vim.lsp.rust-analyzer.enable; - message = "rustaceanvim and rust-analyzer are mutually exclusive. Please ensure that rust-analyzer is disabled."; + message = '' + Rustaceanvim fully manages its own rust-analyzer. + Therefore you can't use vim.langauges.rust.extensions.rustaceanvim.enable with rust-analyzer enabled. + ''; } ]; }) From ac4547b2e9f57681a77a6107af4594149ca73176 Mon Sep 17 00:00:00 2001 From: Samuel Cobb Date: Wed, 10 Jun 2026 00:14:17 +0100 Subject: [PATCH 22/22] feat(rust): fmt --- modules/extra/deprecations.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/extra/deprecations.nix b/modules/extra/deprecations.nix index 44f61893..88cacb28 100644 --- a/modules/extra/deprecations.nix +++ b/modules/extra/deprecations.nix @@ -388,7 +388,7 @@ in { nvim-tree.lua removed system_open and now uses Neovim's vim.ui.open(). '') ] - + # 2026-06-05 [ (mkRemovedLspPackage "rust")