diff --git a/CHANGELOG.md b/CHANGELOG.md
index b78648dc..b58c6b3a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,8 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [Unreleased]
+## [3.1.0] - 2023-10-28
### Added
+- `:RustLsp explainErrors` command, uses `rustc --explain` on error diagnostics with
+ an error code.
- `:RustLsp rebuildProcMacros` command.
## [3.0.4] - 2023-10-25
diff --git a/README.md b/README.md
index a3d21b5b..31d1d1fc 100644
--- a/README.md
+++ b/README.md
@@ -244,6 +244,25 @@ for more configuration options.
```
+
+
+ Explain errors
+
+
+ Display a hover window with explanations from the [rust error codes index](https://doc.rust-lang.org/error_codes/error-index.html)
+ over error diagnostics (if they have an error code).
+
+ ```vimscript
+ :RustLsp explainError
+ ```
+ ```lua
+ vim.cmd.RustLsp('explainError')
+ ```
+
+![](https://github.com/mrcjkb/rustaceanvim/assets/12857160/bac9b31c-22ca-40c4-bfd3-b8c5ba4cc49a)
+
+
+
Open Cargo.toml
diff --git a/lua/rustaceanvim/commands/explain_error.lua b/lua/rustaceanvim/commands/explain_error.lua
new file mode 100644
index 00000000..d1e9626f
--- /dev/null
+++ b/lua/rustaceanvim/commands/explain_error.lua
@@ -0,0 +1,70 @@
+local M = {}
+
+local rustc = 'rustc'
+
+function M.explain_error()
+ if vim.fn.executable(rustc) ~= 1 then
+ vim.notify('rustc is needed to explain errors.', vim.log.levels.ERROR)
+ return
+ end
+
+ local diagnostics = vim.tbl_filter(function(diagnostic)
+ return diagnostic.code ~= nil and diagnostic.source == 'rustc'
+ end, vim.diagnostic.get(0, {}))
+ if #diagnostics == 0 then
+ vim.notify('No explainnable errors found.', vim.log.levels.INFO)
+ return
+ end
+ local win_id = vim.api.nvim_get_current_win()
+ local opts = {
+ cursor_position = vim.api.nvim_win_get_cursor(win_id),
+ severity = vim.diagnostic.severity.ERROR,
+ wrap = true,
+ }
+ local found = false
+ local diagnostic
+ local pos_map = {}
+ local pos_id = 1
+ repeat
+ diagnostic = vim.diagnostic.get_next(opts)
+ pos_map[pos_id] = diagnostic
+ if diagnostic == nil then
+ break
+ end
+ found = diagnostic.code ~= nil and diagnostic.source == 'rustc'
+ local pos = { diagnostic.lnum, diagnostic.col }
+ pos_id = pos[1] + pos[2]
+ opts.cursor_position = pos
+ local searched_all = pos_map[pos_id] ~= nil
+ until diagnostic == nil or found or searched_all
+ if not found then
+ return
+ end
+
+ ---@param sc vim.SystemCompleted
+ local function handler(sc)
+ if sc.code ~= 0 or not sc.stdout then
+ vim.notify('Error calling rustc --explain' .. (sc.stderr and ': ' .. sc.stderr or ''), vim.log.levels.ERROR)
+ return
+ end
+ local output = sc.stdout:gsub('```', '```rust', 1)
+ local markdown_lines = vim.lsp.util.convert_input_to_markdown_lines(output, {})
+ vim.schedule(function()
+ vim.lsp.util.open_floating_preview(markdown_lines, 'markdown', {
+ focus = false,
+ focusable = true,
+ focus_id = 'rustc-explain-error',
+ close_events = { 'CursorMoved', 'BufHidden', 'InsertCharPre' },
+ })
+ end)
+ end
+
+ -- Save position in the window's jumplist
+ vim.cmd("normal! m'")
+ vim.api.nvim_win_set_cursor(win_id, { diagnostic.lnum + 1, diagnostic.col })
+ -- Open folds under the cursor
+ vim.cmd('normal! zv')
+ vim.system({ rustc, '--explain', diagnostic.code }, nil, vim.schedule_wrap(handler))
+end
+
+return M.explain_error
diff --git a/lua/rustaceanvim/commands/init.lua b/lua/rustaceanvim/commands/init.lua
index 6b370b15..fbfd3392 100644
--- a/lua/rustaceanvim/commands/init.lua
+++ b/lua/rustaceanvim/commands/init.lua
@@ -27,6 +27,9 @@ local command_tbl = {
expandMacro = function(_)
require('rustaceanvim.commands.expand_macro')()
end,
+ explainError = function(_)
+ require('rustaceanvim.commands.explain_error')()
+ end,
rebuildProcMacros = function()
require('rustaceanvim.commands.rebuild_proc_macros')()
end,
diff --git a/lua/rustaceanvim/health.lua b/lua/rustaceanvim/health.lua
index 9f5db1cf..de19e2cf 100644
--- a/lua/rustaceanvim/health.lua
+++ b/lua/rustaceanvim/health.lua
@@ -81,6 +81,20 @@ local external_dependencies = {
Not required in standalone files.
]],
},
+ {
+ name = 'rustc',
+ get_binaries = function()
+ return { 'rustc' }
+ end,
+ optional = function()
+ return true
+ end,
+ url = '[rustc](https://doc.rust-lang.org/rustc/what-is-rustc.html)',
+ info = [[
+ The Rust compiler.
+ Called by `:RustLsp explainError`.
+ ]],
+ },
{
name = config.dap.adapter.command,
get_binaries = function()
diff --git a/lua/rustaceanvim/hover_actions.lua b/lua/rustaceanvim/hover_actions.lua
index 9338a708..db7a5488 100644
--- a/lua/rustaceanvim/hover_actions.lua
+++ b/lua/rustaceanvim/hover_actions.lua
@@ -83,7 +83,7 @@ function M.handler(_, result, ctx)
'markdown',
vim.tbl_extend('keep', config.tools.hover_actions, {
focusable = true,
- focus_id = 'rust-tools-hover-actions',
+ focus_id = 'rust-analyzer-hover-actions',
close_events = { 'CursorMoved', 'BufHidden', 'InsertCharPre' },
})
)