Home

Awesome

<!-- markdownlint-disable --> <br /> <div align="center"> <a href="https://github.com/mrcjkb/rustaceanvim"> <img src="./rustaceanvim.svg" alt="rustaceanvim"> </a> <p align="center"> <br /> <a href="./doc/rustaceanvim.txt"><strong>Explore the docs »</strong></a> <br /> <br /> <a href="https://github.com/mrcjkb/rustaceanvim/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml">Report Bug</a> · <a href="https://github.com/mrcjkb/rustaceanvim/issues/new?assignees=&labels=enhancement&projects=&template=feature_request.yml">Request Feature</a> · <a href="https://github.com/mrcjkb/rustaceanvim/discussions/new?category=q-a">Ask Question</a> </p> <p> <strong> Supercharge your Rust experience in <a href="https://neovim.io/">Neovim</a>!<br /> A heavily modified fork of <a href="https://github.com/simrat39/rust-tools.nvim">rust-tools.nvim</a><br /> </strong> </p> <p>🦀</p>

Neovim Lua Rust Nix

GPL2 License Issues Build Status LuaRocks

</div> <!-- markdownlint-restore -->

[!NOTE]

:link: Quick Links

:grey_question: Do I need rustaceanvim

If you are starting out with Rust, Neovim's built-in LSP client API (see :h lsp) or 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.

:pencil: Prerequisites

Required

Optional

:inbox_tray: Installation

rocks.nvim

:Rocks install rustaceanvim

lazy.nvim

{
  'mrcjkb/rustaceanvim',
  version = '^5', -- 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.

Nix

For Nix users with flakes enabled, this project provides outputs in the form of a package and an overlay. It is also available in nixpkgs.

Look at the configuration information below to get started.

:zap: Quick Setup

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.lua1

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 }
)
vim.keymap.set(
  "n", 
  "K",  -- Override Neovim's built-in hover keymap with rustaceanvim's hover actions
  function()
    vim.cmd.RustLsp({'hover', 'actions'})
  end,
  { silent = true, buffer = bufnr }
)

[!TIP]

<!-- markdownlint-disable --> <!-- markdownlint-restore -->

[!IMPORTANT]

:books: Usage / Features

<!-- markdownlint-disable --> <details> <summary> <b>Debugging</b> </summary>
: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.

</details> <details> <summary> <b>Runnables</b> </summary>
: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.

</details> <details> <summary> <b>Testables and failed test diagnostics</b> </summary>

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.

</details> <details> <summary> <b>Neotest integration</b> </summary>

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:

rustaceanvimneotest-rust
Test discoveryrust-analyzer (LSP)tree-sitter
Command constructionrust-analyzer (LSP)tree-sitter
DAP strategyAutomatic DAP detection (reuses debuggables); overridable with vim.g.rustaceanvim.dapDefaults to codelldb; manual configuration
Test runnercargo or cargo-nextest, if detectedcargo-nextest

If you configure rustaceanvim to use neotest, the tools.test_executor will default to using neotest for testables and runnables that are tests.

</details> <details> <summary> <b>Expand macros recursively</b> </summary>
:RustLsp expandMacro
vim.cmd.RustLsp('expandMacro')

</details> <details> <summary> <b>Rebuild proc macros</b> </summary>
:RustLsp rebuildProcMacros
vim.cmd.RustLsp('rebuildProcMacros')
</details> <details> <summary> <b>Move item up/down</b> </summary>
:RustLsp moveItem {up|down}
vim.cmd.RustLsp { 'moveItem',  'up' }
vim.cmd.RustLsp { 'moveItem',  'down' }
</details> <details> <summary> <b>Grouped code actions</b> </summary>

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.

</details> <details> <summary> <b>Hover actions</b> </summary>

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().

You can invoke a hover action by switching to the hover window and entering <CR> on the respective line, or with a keymap for the <Plug>RustHoverAction mapping, which accepts a <count> prefix as the (1-based) index of the hover action to invoke.

For example, if you set the following keymap:

vim.keymap.set('n', '<space>a', '<Plug>RustHoverAction')

you can invoke the third hover action with 3<space>a.

</details> <details> <summary> <b>Hover range</b> </summary>
:RustLsp hover range
vim.cmd.RustLsp { 'hover', 'range' }
</details> <details> <summary> <b>Explain errors</b> </summary>

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' })

</details> <details> <summary> <b>Render diagnostics</b> </summary>

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' })

</details> <details> <summary> <b>Jump to related diagnostics</b> </summary>

Sometimes, rust-analyzer provides related diagnostics in multiple locations. Using the relatedDiagnostics subcommand, you can navigate between them. If a diagnostic has more than one related diagnostic, this will populate the quickfix list.

:RustLsp relatedDiagnostics
vim.cmd.RustLsp('relatedDiagnostics')

</details> <details> <summary> <b>Open Cargo.toml</b> </summary>
:RustLsp openCargo
vim.cmd.RustLsp('openCargo')
</details> <details> <summary> <b>Open docs.rs documentation</b> </summary>

Open docs.rs documentation for the symbol under the cursor.

:RustLsp openDocs
vim.cmd.RustLsp('openDocs')
</details> <details> <summary> <b>Parent Module</b> </summary>
:RustLsp parentModule
vim.cmd.RustLsp('parentModule')
</details> <details> <summary> <b>Filtered workspace symbol searches</b> </summary>

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 ]]
}
</details> <details> <summary> <b>Join lines</b> </summary>

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')

</details> <details> <summary> <b>Structural search replace</b> </summary>
:RustLsp ssr {query}
vim.cmd.RustLsp { 'ssr', '<query>' --[[ optional ]] }

tty

</details> <details> <summary> <b>View crate graph</b> </summary>
:RustLsp crateGraph {backend {output}}
vim.cmd.RustLsp { 'crateGraph', '[backend]', '[output]' }

Requires:

</details> <details> <summary> <b>View syntax tree</b> </summary>
:RustLsp syntaxTree
vim.cmd.RustLsp('syntaxTree')

</details> <details> <summary> <b>Fly check</b> </summary>

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.

</details> <details> <summary> <b>View HIR / MIR</b> </summary>

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' }
</details> <details> <summary> <b>Rustc unpretty</b> </summary>

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:

</details> <!-- markdownlint-restore -->

:gear: Advanced configuration

To modify the default configuration, set vim.g.rustaceanvim.

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.

Using codelldb for debugging

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:

If your distribution does not have a codelldb package, you can configure it as follows:

  1. Install the CodeLLDB VSCode extension.
  2. Find out where it is installed. On Linux, this is typically in $HOME/.vscode/extensions/
  3. 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

How to dynamically load different rust-analyzer settings per project

By default, this plugin will look for a .vscode/settings.json2 file and attempt to load it. If the file does not exist, or it can't be decoded, the server.default_settings will be used.

Another option is to use :h exrc.

:stethoscope: Troubleshooting

Health checks

For a health check, run :checkhealth rustaceanvim

rust-analyzer log file

To open the rust-analyzer log file, run :RustLsp logFile.

Minimal config

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.

rust-analyzer troubleshooting

For issues related to rust-analyzer (e.g. LSP features not working), see also the rust-analyzer troubleshooting guide.

:left_speech_bubble: FAQ

Where are inlay hints / type hints?

As Neovim >= 0.10 supports inlay hints natively, I have removed the code from this plugin. See :h lsp-inlay_hint).

How to enable auto completion?

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.

I'm having issues with (auto)completion

rustaceanvim doesn't implement (auto)completion. Issues with (auto)completion either come from another plugin or rust-analzyer.

mason.nvim and nvim-lspconfig

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.

I am not seeing diagnostics in a standalone file

rust-analyzer has limited support for standalone files. Many diagnostics come from Cargo. If you're not in a Cargo project, you won't see any Cargo diagnostics.

:link: Related Projects

Inspiration

rust-tools.nvim draws inspiration from akinsho/flutter-tools.nvim

<!-- markdownlint-disable --> <!-- prettier-ignore-end --> <!-- MARKDOWN LINKS & IMAGES -->

Footnotes

  1. See :help base-directories

  2. See this example and the rust-analyzer configuration manual. Note that JSON5 is currently not supported by Neovim.