Explore the docs »
Report Bug
·
Request Feature
·
Ask Question
Supercharge your Rust experience in Neovim!
A heavily modified fork of rust-tools.nvim
🦀
Note
- Just works. No need to call
setup
! - No dependency on
lspconfig
. - Lazy initialization by design.
- 📝 Prerequisites
- 📥 Installation
- ⚡ Quick setup
- 📚 Usage / Features
- ⚙️ Advanced configuration
- 🩺 Troubleshooting
- 🗨️ FAQ
- 🚣 Migrating from rust-tools
If you are starting out with Rust, nvim-lspconfig.rust_analyzer
is probably enough for you.
It provides the lowest common denominator of LSP support.
This plugin is for those who would like additional non-standard features
that are specific to rust-analyzer.
neovim >= 0.9
rust-analyzer
dot
fromgraphviz
, for crate graphs.cargo
, required for Cargo projects.- A debug adapter (e.g.
lldb
orcodelldb
) andnvim-dap
, required for debugging. - A tree-sitter parser for Rust (required for the
:Rustc unpretty
command). Can be installed using nvim-treesitter, which also provides highlights, etc.
This plugin is available on LuaRocks:
Example using lazy.nvim
:
{
'mrcjkb/rustaceanvim',
version = '^4', -- Recommended
lazy = false, -- This plugin is already lazy
}
Tip
It is suggested to pin to tagged releases if you would like to avoid breaking changes.
To manually generate documentation, use :helptags ALL
.
Note
For NixOS users with flakes enabled, this project provides outputs in the
form of a package and an overlay; use it as you wish in your NixOS or
home-manager configuration.
It is also available in nixpkgs
.
Look at the configuration information below to get started.
This plugin automatically configures the rust-analyzer
builtin LSP
client and integrates with other Rust tools.
See the Usage / Features section for more info.
Warning
Do not call the nvim-lspconfig.rust_analyzer
setup or set up the LSP client for rust-analyzer
manually,
as doing so may cause conflicts.
This is a filetype plugin that works out of the box,
so there is no need to call a setup
function or configure anything
to get this plugin working.
You will most likely want to add some keymaps.
Most keymaps are only useful in rust files,
so I suggest you define them in ~/.config/nvim/after/ftplugin/rust.lua
1
Example:
local bufnr = vim.api.nvim_get_current_buf()
vim.keymap.set(
"n",
"<leader>a",
function()
vim.cmd.RustLsp('codeAction') -- supports rust-analyzer's grouping
-- or vim.lsp.buf.codeAction() if you don't want grouping.
end,
{ silent = true, buffer = bufnr }
)
Tip
- For more LSP related keymaps, see the
nvim-lspconfig
suggestions. - If you want to share keymaps with
nvim-lspconfig
, you can also use thevim.g.rustaceanvim.server.on_attach
function, or anLspAttach
autocommand. - See the Advanced configuration section
or
:h rustaceanvim.config
for more configuration options.
Important
- Do not set
vim.g.rustaceanvim
inafter/ftplugin/rust.lua
, as the file is sourced after the plugin is initialized.
Debugging
debuggables
opens a prompt to select from available targets.debug
searches for a target at the current cursor position.
:RustLsp[!] debuggables {args[]}?
:RustLsp[!] debug {args[]}?
vim.cmd.RustLsp('debug')
vim.cmd.RustLsp('debuggables')
-- or, to run the previous debuggable:
vim.cmd.RustLsp { 'debuggables', bang = true }
-- or, to override the executable's args:
vim.cmd.RustLsp {'debuggables', 'arg1', 'arg2' }
Calling the command with a bang !
will rerun the last debuggable.
Requires:
By default, this plugin will silently attempt to autoload nvim-dap
configurations when the LSP client attaches.
You can call them with require('dap').continue()
or :DapContinue
once
they have been loaded. The feature can be disabled by setting
vim.g.rustaceanvim.dap.autoload_configurations = false
.
It is disabled by default in neovim 0.9, as it can block the UI.
:RustLsp debuggables
will only load debug configurations created byrust-analyzer
.require('dap').continue()
will load all Rust debug configurations, including those specified in a.vscode/launch.json
(see:h dap-launch.json
).- Note that rustaceanvim may only be able to load DAP configurations when rust-analyzer has finished initializing (which may be after the client attaches, in large projects). This means that the DAP configurations may not be loaded immediately upon startup.
Runnables
runnables
opens a prompt to select from available targets.run
searches for a target at the current cursor position.
:RustLsp[!] runnables {args[]}?
:RustLsp[!] run {args[]}?
vim.cmd.RustLsp('run')
vim.cmd.RustLsp('runnables')
-- or, to run the previous runnable:
vim.cmd.RustLsp { 'runnables', bang = true }
-- or, to override the executable's args:
vim.cmd.RustLsp {'runnables', 'arg1', 'arg2' }
Calling the command with a bang !
will rerun the last runnable.
Testables and failed test diagnostics
If you are using Neovim >= 0.10, you can set the vim.g.rustaceanvim.tools.test_executor
option to 'background'
, and this plugin will run tests in the background,
parse the results, and - if possible - display failed tests as diagnostics.
This is also possible in Neovim 0.9, but tests won't be run in the background, and will block the UI.
:RustLsp[!] testables {args[]}?
vim.cmd.RustLsp('testables')
-- or, to run the previous testables:
vim.cmd.RustLsp { 'testables', bang = true }
-- or, to override the executable's args:
vim.cmd.RustLsp {'testables', 'arg1', 'arg2' }
Calling the command with a bang !
will rerun the last testable.
Neotest integration
This plugin provides a neotest adapter, which you can add to neotest as follows:
require('neotest').setup {
-- ...,
adapters = {
-- ...,
require('rustaceanvim.neotest')
},
}
Note: If you use rustaceanvim's neotest adapter, do not add neotest-rust.
Here is a comparison between rustaceanvim's adapter and neotest-rust:
rustaceanvim | neotest-rust | |
---|---|---|
Test discovery | rust-analyzer (LSP) | tree-sitter |
Command construction | rust-analyzer (LSP) | tree-sitter |
DAP strategy | Automatic DAP detection (reuses debuggables ); overridable with vim.g.rustaceanvim.dap |
Defaults to codelldb ; manual configuration |
Test runner | cargo or cargo-nextest , if detected |
cargo-nextest |
If you configure rustaceanvim to use neotest, the tools.test_executor
will default to using neotest for testables
and runnables
that are tests.
Rebuild proc macros
:RustLsp rebuildProcMacros
vim.cmd.RustLsp('rebuildProcMacros')
Move item up/down
:RustLsp moveItem {up|down}
vim.cmd.RustLsp { 'moveItem', 'up' }
vim.cmd.RustLsp { 'moveItem', 'down' }
Grouped code actions
Sometimes, rust-analyzer groups code actions by category,
which is not supported by Neovim's built-in vim.lsp.buf.codeAction
.
This plugin provides a command with a UI that does:
:RustLsp codeAction
vim.cmd.RustLsp('codeAction')
If you set the option vim.g.rustaceanvim.tools.code_actions.ui_select_fallback
to true
(defaults to false
), it will fall back to vim.ui.select
if there are no grouped code actions.
Hover actions
Note: To activate hover actions, run the command twice.
This will move you into the window, then press enter on the selection you want.
Alternatively, you can set auto_focus
to true
in your config and you will
automatically enter the hover actions window.
:RustLsp hover actions
vim.cmd.RustLsp { 'hover', 'actions' }
By default, this plugin replaces Neovim's built-in hover handler with hover
actions, so you can also use vim.lsp.buf.hover()
.
Hover range
:RustLsp hover range
vim.cmd.RustLsp { 'hover', 'range' }
Explain errors
Display a hover window with explanations from the rust error codes index over error diagnostics (if they have an error code).
:RustLsp explainError {cycle?|current?}
vim.cmd.RustLsp('explainError') -- default to 'cycle'
vim.cmd.RustLsp({ 'explainError', 'cycle' })
vim.cmd.RustLsp({ 'explainError', 'current' })
-
If called with
cycle
or no args: Likevim.diagnostic.goto_next
,explainError
will cycle diagnostics, starting at the cursor position, until it can find a diagnostic with an error code. -
If called with
current
: Searches for diagnostics only in the current cursor line.
Render diagnostics
Display a hover window with the rendered diagnostic, as displayed
during cargo build
.
Useful for solving bugs around borrowing and generics,
as it consolidates the important bits (sometimes across files)
together.
:RustLsp renderDiagnostic {cycle?|current?}
vim.cmd.RustLsp('renderDiagnostic') -- defaults to 'cycle'
vim.cmd.RustLsp({ 'renderDiagnostic', 'cycle' })
vim.cmd.RustLsp({ 'renderDiagnostic', 'current' })
-
If called with
cycle
or no args: Likevim.diagnostic.goto_next
,renderDiagnostic
will cycle diagnostics, starting at the cursor position, until it can find a diagnostic with rendered data. -
If called with
current
: Searches for diagnostics only in the current cursor line.
Open Cargo.toml
:RustLsp openCargo
vim.cmd.RustLsp('openCargo')
Open docs.rs documentation
Open docs.rs documentation for the symbol under the cursor.
:RustLsp openDocs
vim.cmd.RustLsp('openDocs')
Parent Module
:RustLsp parentModule
vim.cmd.RustLsp('parentModule')
Filtered workspace symbol searches
rust-analyzer supports filtering workspace symbol searches.
:RustLsp[!] workspaceSymbol {onlyTypes?|allSymbols?} {query?}
vim.cmd.RustLsp('workspaceSymbol')
-- or
vim.cmd.RustLsp {
'workspaceSymbol',
'<onlyTypes|allSymbols>' --[[ optional ]],
'<query>' --[[ optional ]],
bang = true --[[ optional ]]
}
- Calling the command with a bang
!
will include dependencies in the search. - You can also influence the behaviour of
vim.lsp.buf.workspace_symbol()
by setting the rust-analyzerworkspace.symbol.search
server option.
Join lines
Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. Works with individual lines in normal mode and multiple lines in visual mode.
:RustLsp joinLines
vim.cmd.RustLsp('joinLines')
Structural search replace
- Searches the entire buffer in normal mode.
- Searches the selection in visual mode.
:RustLsp ssr {query}
vim.cmd.RustLsp { 'ssr', '<query>' --[[ optional ]] }
View crate graph
:RustLsp crateGraph {backend {output}}
vim.cmd.RustLsp { 'crateGraph', '[backend]', '[output]' }
Requires:
Fly check
Run cargo check
or another compatible command (f.x. clippy
)
in a background thread and provide LSP diagnostics based on
the output of the command.
Useful in large projects where running cargo check
on each save
can be costly.
:RustLsp flyCheck {run?|clear?|cancel?}
vim.cmd.RustLsp('flyCheck') -- defaults to 'run'
vim.cmd.RustLsp { 'flyCheck', 'run' }
vim.cmd.RustLsp { 'flyCheck', 'clear' }
vim.cmd.RustLsp { 'flyCheck', 'cancel' }
[!NOTE]
This is only useful if you set the option,
['rust-analzyer'].checkOnSave = false
.
View HIR / MIR
Opens a buffer with a textual representation of the HIR or MIR of the function containing the cursor. Useful for debugging or when working on rust-analyzer itself.
:RustLsp view {hir|mir}
vim.cmd.RustLsp { 'view', 'hir' }
vim.cmd.RustLsp { 'view', 'mir' }
Rustc unpretty
Opens a buffer with a textual representation of the MIR or others things, of the function closest to the cursor. Achieves an experience similar to Rust Playground.
NOTE: This currently requires a tree-sitter parser for Rust, and a nightly compiler toolchain.
:Rustc unpretty {hir|mir|...}
vim.cmd.Rustc { 'unpretty', 'hir' }
vim.cmd.Rustc { 'unpretty', 'mir' }
-- ...
Requires:
- A tree-sitter parser for Rust (required for the
:Rustc unpretty
command). Can be installed using nvim-treesitter.
To modify the default configuration, set vim.g.rustaceanvim
.
- See
:help rustaceanvim.config
for a detailed documentation of all available configuration options. You may need to run:helptags ALL
if the documentation has not been installed. - The default configuration can be found here (see
RustaceanDefaultConfig
). - For detailed descriptions of the language server configs,
see the
rust-analyzer
documentation.
You only need to specify the keys that you want to be changed, because defaults are applied for keys that are not provided.
Example config:
vim.g.rustaceanvim = {
-- Plugin configuration
tools = {
},
-- LSP configuration
server = {
on_attach = function(client, bufnr)
-- you can also put keymaps in here
end,
default_settings = {
-- rust-analyzer language server configuration
['rust-analyzer'] = {
},
},
},
-- DAP configuration
dap = {
},
}
Tip
vim.g.rustaceanvim
can also be a function that returns
a table.
For Rust, codelldb
from the CodeLLDB VSCode extension
provides a better experience than lldb
.
If you are using a distribution that lets you install the codelldb
executable, this plugin will automatically detect it and configure
itself to use it as a debug adapter.
Some examples:
- NixOS:
vscode-extensions.vadimcn.vscode-lldb.adapter
- This repository's Nix flake provides a
codelldb
package. - Arch Linux:
codelldb-bin
(AUR) - Using
mason.nvim
::MasonInstall codelldb
If your distribution does not have a codelldb
package,
you can configure it as follows:
- Install the CodeLLDB VSCode extension.
- Find out where it is installed.
On Linux, this is typically in
$HOME/.vscode/extensions/
- Update your configuration:
vim.g.rustaceanvim = function()
-- Update this path
local extension_path = vim.env.HOME .. '/.vscode/extensions/vadimcn.vscode-lldb-1.10.0/'
local codelldb_path = extension_path .. 'adapter/codelldb'
local liblldb_path = extension_path .. 'lldb/lib/liblldb'
local this_os = vim.uv.os_uname().sysname;
-- The path is different on Windows
if this_os:find "Windows" then
codelldb_path = extension_path .. "adapter\\codelldb.exe"
liblldb_path = extension_path .. "lldb\\bin\\liblldb.dll"
else
-- The liblldb extension is .so for Linux and .dylib for MacOS
liblldb_path = liblldb_path .. (this_os == "Linux" and ".so" or ".dylib")
end
local cfg = require('rustaceanvim.config')
return {
dap = {
adapter = cfg.get_codelldb_adapter(codelldb_path, liblldb_path),
},
}
end
By default, this plugin will look for a rust-analyzer.json
2
file in the project root directory, and attempt to load it.
If the file does not exist, or it can't be decoded,
the server.default_settings
will be used.
You can change this behaviour with the server.settings
config:
vim.g.rustaceanvim = {
-- ...
server = {
---@param project_root string Path to the project root
settings = function(project_root)
local ra = require('rustaceanvim.config.server')
return ra.load_rust_analyzer_settings(project_root, {
settings_file_pattern = 'rust-analyzer.json'
})
end,
},
}
For a health check, run :checkhealth rustaceanvim
To open the rust-analyzer
log file, run :RustLsp logFile
.
To troubleshoot this plugin with a minimal config in a temporary directory, you can try minimal.lua.
nvim -u minimal.lua
Note
If you use Nix, you can run
nix run "github:mrcjkb/rustaceanvim#nvim-minimal-stable"
.
or
nix run "github:mrcjkb/rustaceanvim#nvim-minimal-nightly"
.
If you cannot reproduce your issue with a minimal config,
it may be caused by another plugin,
or a setting of your plugin manager.
In this case, add additional plugins and configurations to minimal.lua
,
until you can reproduce it.
For issues related to rust-analyzer (e.g. LSP features not working), see also the rust-analyzer troubleshooting guide.
As Neovim >= 0.10 supports inlay hints natively (:h lsp-inlay_hint
),
I have removed the
code from this plugin.
To enable inlay hints in Neovim < 0.10, see this discussion.
As of #ff097f2091e7a970e5b12960683b4dade5563040,
Neovim has built-in completion based on the triggerCharacters
sent by
language servers.
Omni completion is also available for a more traditional vim
-like completion experience.
For more extensible and complex autocompletion setups you need a plugin such as nvim-cmp
and a LSP completion source like cmp-nvim-lsp
.
This plugin will automatically register the necessary client capabilities
if you have cmp-nvim-lsp
installed.
rustaceanvim doesn't implement (auto)completion. Issues with (auto)completion either come from another plugin or rust-analzyer.
See :h rustaceanvim.mason
for details about troubleshooting
mason.nvim and nvim-lspconfig issues, or configuring rustaceanvim to use
a rust-analyzer installation that is managed by mason.nvim.
rouge8/neotest-rust
Aneotest
adapter for Rust, usingcargo-nextest
.Saecki/crates.nvim
vxpm/ferris.nvim
Geared towards people who prefer manual LSP client configuration. Has some features that have not yet been implemented by this plugin.adaszko/tree_climber_rust.nvim
tree-sitter powered incremental selection tailored for Rust.
rust-tools.nvim
draws inspiration from akinsho/flutter-tools.nvim
Footnotes
-
See this example and the rust-analyzer configuration manual. ↩