From 8c52c7a881b03116f7fe9516b9603e6cd686af60 Mon Sep 17 00:00:00 2001 From: Pratik Tripathy Date: Tue, 29 Jul 2025 11:25:08 +0530 Subject: [PATCH] feat(neovim-rust): Rust LSP, debugging, testing and keymaps - `rustaceanvim` for LSP config - Rust ft specific keymaps in `after/ftplugin` - `nvim-dap` for Rust debugging with codelldb - `rustaceanvim.neotest` for Rust testing - `rcasia/neotest-bash` for Bash testing - Better keymaps for running tests - Inlay hint UI improvements --- common/.config/nvim/after/ftplugin/rust.lua | 27 +-- common/.config/nvim/lua/core/lsp.lua | 3 +- .../.config/nvim/lua/plugins/code-debug.lua | 173 +++++++++++++++++- common/.config/nvim/lua/plugins/code-lsp.lua | 52 +++++- .../.config/nvim/lua/plugins/code-testing.lua | 20 +- .../.config/nvim/lua/plugins/completion.lua | 6 + .../nvim/lua/plugins/utility-plugs.lua | 1 + 7 files changed, 250 insertions(+), 32 deletions(-) diff --git a/common/.config/nvim/after/ftplugin/rust.lua b/common/.config/nvim/after/ftplugin/rust.lua index 3a878c8..bbac929 100644 --- a/common/.config/nvim/after/ftplugin/rust.lua +++ b/common/.config/nvim/after/ftplugin/rust.lua @@ -1,23 +1,10 @@ --- TODO: Map all native Nvim 0.11 LSP commands to corresponding rustaceanvim ones --- grn -> Rename --- grr -> References --- gri -> Implementation --- gO -> document_symbol --- gra -> code_action --- Mine --- F2 -> Rename --- gD -> Go to definition --- cr -> References --- co -> document_symbol - if pcall(require, "rustaceanvim") then local bufnr = vim.api.nvim_get_current_buf() - - vim.keymap.set("n", "", function() - vim.cmd.RustLsp("codeAction") - end, { silent = true, buffer = bufnr }) - - vim.keymap.set("n", "K", function() - vim.cmd.RustLsp({ "hover", "actions" }) - end, { silent = true, buffer = bufnr }) + vim.keymap.set("n", "gC", "RustLsp openCargo", { desc = "Open Cargo.toml", buffer = bufnr }) + vim.keymap.set("n", "rm", "RustLsp expandMacro", { desc = "Expand Macro", buffer = bufnr }) + vim.keymap.set("n", "rp", "RustLsp parentModule", { desc = "Parent Module", buffer = bufnr }) + vim.keymap.set("n", "rJ", "RustLsp joinLines", { desc = "Join Lines", buffer = bufnr }) + vim.keymap.set("n", "rh", "RustLsp openDocs", { desc = "Open docs.rs Documentation" }) + vim.keymap.set("n", "rM", "RustLsp view mir", { desc = "View Mid-Level IR", buffer = bufnr }) + vim.keymap.set("n", "rH", "RustLsp view hir", { desc = "View High-Level IR", buffer = bufnr }) end diff --git a/common/.config/nvim/lua/core/lsp.lua b/common/.config/nvim/lua/core/lsp.lua index e55488e..0d717c7 100644 --- a/common/.config/nvim/lua/core/lsp.lua +++ b/common/.config/nvim/lua/core/lsp.lua @@ -102,7 +102,8 @@ vim.api.nvim_create_autocmd("LspAttach", { -- Native lsp inline virtual text / inlay hints if client and client.supports_method(vim.lsp.protocol.Methods.textDocument_inlayHint) then - vim.lsp.inlay_hint.enable() + vim.lsp.inlay_hint.enable(true) + vim.api.nvim_set_hl(0, "LspInlayHint", { fg = "#5c7086", bg = "NONE" }) map("ch", function() vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = event.buf })) diff --git a/common/.config/nvim/lua/plugins/code-debug.lua b/common/.config/nvim/lua/plugins/code-debug.lua index a564707..b1e65fe 100644 --- a/common/.config/nvim/lua/plugins/code-debug.lua +++ b/common/.config/nvim/lua/plugins/code-debug.lua @@ -1 +1,172 @@ -return {} +return { + "mfussenegger/nvim-dap", + dependencies = { + -- Creates a beautiful debugger UI + "rcarriga/nvim-dap-ui", + + -- Required dependency for nvim-dap-ui + "nvim-neotest/nvim-nio", + + -- Auto-installs debugger adapters + "mason-org/mason.nvim", + "jay-babu/mason-nvim-dap.nvim", + + -- Shows variable values inline as virtual text + "theHamsta/nvim-dap-virtual-text", + }, + keys = { + { + "", + function() + require("dap").continue() + end, + desc = "Debug: Start/Continue", + }, + { + "Ds", + function() + require("dap").continue() + end, + desc = "Debug: Start/Continue", + }, + { + "", + function() + require("dap").step_into() + end, + desc = "Debug: Step Into", + }, + { + "Di", + function() + require("dap").step_into() + end, + desc = "Debug: Step Into", + }, + { + "", + function() + require("dap").step_over() + end, + desc = "Debug: Step Over", + }, + { + "Do", + function() + require("dap").step_over() + end, + desc = "Debug: Step Over", + }, + { + "", + function() + require("dap").step_out() + end, + desc = "Debug: Step Out", + }, + { + "DO", + function() + require("dap").step_out() + end, + desc = "Debug: Step Out", + }, + { + "", + function() + require("dap").toggle_breakpoint() + end, + desc = "Debug: Toggle Breakpoint", + }, + { + "Db", + function() + require("dap").toggle_breakpoint() + end, + desc = "Debug: Toggle Breakpoint", + }, + { + "DB", + function() + require("dap").set_breakpoint(vim.fn.input("Breakpoint condition: ")) + end, + desc = "Debug: Set Conditional Breakpoint", + }, + { + "Dt", + function() + require("dapui").toggle() + end, + desc = "Debug: Toggle UI", + }, + { + "Dl", + function() + require("dap").run_last() + end, + desc = "Debug: Run Last Configuration", + }, + }, + config = function() + local dap = require("dap") + local dapui = require("dapui") + + -- optional + require("mason-nvim-dap").setup({ + automatic_installation = true, + handlers = {}, + ensure_installed = { + "codelldb", + }, + }) + + -- Dap UI setup + dapui.setup({ + icons = { expanded = "▾", collapsed = "▸", current_frame = "*" }, + controls = { + icons = { + pause = "⏸", + play = "▶", + step_into = "⏎", + step_over = "⏭", + step_out = "⏮", + step_back = "b", + run_last = "▶▶", + terminate = "⏹", + disconnect = "⏏", + }, + }, + }) + + -- Automatically open/close DAP UI + dap.listeners.after.event_initialized["dapui_config"] = dapui.open + dap.listeners.before.event_terminated["dapui_config"] = dapui.close + dap.listeners.before.event_exited["dapui_config"] = dapui.close + + -- Setup virtual text to show variable values inline + require("nvim-dap-virtual-text").setup() + + -- Setup Rust & React DAPs here + dap.adapters.codelldb = { + type = "server", + port = "${port}", + executable = { + command = vim.fn.exepath("codelldb"), + args = { "--port", "${port}" }, + }, + } + + dap.configurations.rust = { + { + name = "Launch file", + type = "codelldb", + request = "launch", + program = function() + return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/target/debug/", "file") + end, + cwd = "${workspaceFolder}", + stopOnEntry = false, + }, + } + end, +} diff --git a/common/.config/nvim/lua/plugins/code-lsp.lua b/common/.config/nvim/lua/plugins/code-lsp.lua index 1337286..398f9ed 100644 --- a/common/.config/nvim/lua/plugins/code-lsp.lua +++ b/common/.config/nvim/lua/plugins/code-lsp.lua @@ -58,12 +58,60 @@ return { }, }, + -- NeoVim + { "folke/lazydev.nvim", ft = "lua" }, + -- Rust { "mrcjkb/rustaceanvim", version = "^6", - config = function() - vim.g.rusteceanvim = { + lazy = false, + init = function() + vim.g.rustaceanvim = { + server = { + -- Keymaps in ../../after/ftplugin/rust.lua + + -- LSP configuration + default_settings = { + ["rust-analyzer"] = { + cargo = { allFeatures = true }, + diagnostics = { + enable = true, + enableExperimental = true, + }, + completion = { + addCallArgumentSnippets = true, + addCallParenthesis = true, + postfix = { enable = true }, + autoimport = { enable = true }, + }, + }, + inlayHints = { + bindingModeHints = { enable = false }, + chainingHints = { enable = true }, + closingBraceHints = { enable = true, minLines = 25 }, + closureReturnTypeHints = { enable = "never" }, + lifetimeElisionHints = { enable = "never", useParameterNames = false }, + maxLength = 25, + parameterHints = { enable = true }, + reborrowHints = { enable = "never" }, + renderColons = true, + typeHints = { + enable = true, + hideClosureInitialization = false, + hideNamedConstructor = false, + }, + }, + procMacro = { + enable = true, + ignored = { + ["async-trait"] = { "async_trait" }, + ["napi-derive"] = { "napi" }, + ["async-recursion"] = { "async_recursion" }, + }, + }, + }, + }, dap = { adapter = { type = "executable", diff --git a/common/.config/nvim/lua/plugins/code-testing.lua b/common/.config/nvim/lua/plugins/code-testing.lua index 959339d..6c467e0 100644 --- a/common/.config/nvim/lua/plugins/code-testing.lua +++ b/common/.config/nvim/lua/plugins/code-testing.lua @@ -5,14 +5,17 @@ return { "nvim-neotest/neotest", cond = require("config.util").is_not_vscode(), dependencies = { + "nvim-neotest/nvim-nio", "nvim-lua/plenary.nvim", "antoinemadec/FixCursorHold.nvim", "nvim-treesitter/nvim-treesitter", - "marilari88/neotest-vitest", + + "marilari88/neotest-vitest", -- JS/TS/React/Vue + "rcasia/neotest-bash", -- bash }, opts = { -- Do NOT add adapters here - -- Add it to opts.adapters inside config function + -- Add it to opts.adapters inside config function below adapters = {}, status = { virtual_text = true }, output = { open_on_run = true }, @@ -38,7 +41,6 @@ return { }, }, neotest_ns) - -- WARN: Change the following code if we change lazy.nvim if require("lazy.core.config").spec.plugins["trouble.nvim"] ~= nil then opts.consumers = opts.consumers or {} -- Refresh and auto close trouble after running tests @@ -72,6 +74,7 @@ return { -- TIP: Add adapters here table.insert(opts.adapters, require("neotest-vitest")) + table.insert(opts.adapters, require("rustaceanvim.neotest")) if opts.adapters then local adapters = {} @@ -107,14 +110,15 @@ return { -- r: Run the selected test -- a: Attaches to the test result (output) -- i: Jumps to the code of the test - { "tt", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "Test: Run File" }, + { "tt", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "Test: Run All Test in Current File" }, { "tT", function() require("neotest").run.run(vim.loop.cwd()) end, desc = "Test: Run All Test Files" }, - { "tr", function() require("neotest").run.run() end, desc = "Test: Run Nearest" }, - { "tl", function() require("neotest").run.run_last() end, desc = "Test: Run Last" }, - { "ts", function() require("neotest").summary.toggle() end, desc = "Test: Toggle Summary" }, + { "tn", function() require("neotest").run.run() end, desc = "Test: Run Nearest" }, + { "tR", function() require("neotest").run.run_last() end, desc = "Test: Re-Run Last" }, + { "ts", function() require("neotest").run.stop() end, desc = "Test: Stop" }, + { "to", function() require("neotest").output.open({ enter = true, auto_close = true }) end, desc = "Test: Show Output" }, { "tO", function() require("neotest").output_panel.toggle() end, desc = "Test: Toggle Output Panel" }, - { "tS", function() require("neotest").run.stop() end, desc = "Test: Stop" }, + { "tS", function() require("neotest").summary.toggle() end, desc = "Test: Toggle Summary Panel" }, }, }, } diff --git a/common/.config/nvim/lua/plugins/completion.lua b/common/.config/nvim/lua/plugins/completion.lua index 25f77bc..5ed15a8 100644 --- a/common/.config/nvim/lua/plugins/completion.lua +++ b/common/.config/nvim/lua/plugins/completion.lua @@ -111,6 +111,7 @@ return { sources = { default = { + "lazydev", "lsp", "buffer", "path", @@ -143,6 +144,11 @@ return { return vim.tbl_contains({ "gitcommit", "markdown" }, vim.o.filetype) end, }, + lazydev = { + name = "LazyDev", + module = "lazydev.integrations.blink", + score_offset = 1001, + }, lsp = { score_offset = 1000, }, diff --git a/common/.config/nvim/lua/plugins/utility-plugs.lua b/common/.config/nvim/lua/plugins/utility-plugs.lua index 6fdc811..4f3619f 100644 --- a/common/.config/nvim/lua/plugins/utility-plugs.lua +++ b/common/.config/nvim/lua/plugins/utility-plugs.lua @@ -225,6 +225,7 @@ return { { "h", group = "Help", icon = { icon = "󰞋", color = "orange" } }, { "n", group = "Neovim Things", icon = { icon = "", color = "orange" } }, { "q", group = "Database", icon = { icon = "", color = "orange" } }, + { "r", group = "Rust", icon = { icon = "󱘗", color = "orange" } }, { "s", group = "Search/Grep", icon = { icon = "", color = "orange" } }, { "t", group = "Unit Test" }, { "x", group = "Delete/Disable/Remove", icon = { icon = "", color = "orange" } },