Skip to content

Commit

Permalink
feat(lsp): arguments to explainError/renderDiagnostic from current li…
Browse files Browse the repository at this point in the history
…ne (#431)

Co-authored-by: Marc Jakobi <[email protected]>
Co-authored-by: Marc Jakobi <[email protected]>
  • Loading branch information
3 people authored Jun 21, 2024
1 parent d6d7620 commit 34fb2b4
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 23 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,25 @@ 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]

### Added

- LSP: Added optional `current`/`cycle` arguments to
the `explainError` and `renderDiagnostic` commands.
No argument defaults to `cycle`, which is current base behaviour. `current`
makes these functions only look for diagnostics in current cursor line

### Fixed

## [4.25.1] - 2024-06-21

### Changed

- Testables: Default to `termopen` test executor if not using `neotest`

### Added

### Fixed

- LSP: Support completions for `RustLsp` with selection ranges.
Expand Down
36 changes: 26 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,15 +427,23 @@ vim.keymap.set(
over error diagnostics (if they have an error code).

```vim
:RustLsp explainError
:RustLsp explainError {cycle?|current?}
```
```lua
vim.cmd.RustLsp('explainError')
vim.cmd.RustLsp('explainError') -- default to 'cycle'
vim.cmd.RustLsp({ 'explainError', 'cycle' })
vim.cmd.RustLsp({ 'explainError', 'current' })
```

Like `vim.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 `cycle` or no args:
Like `vim.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.

![](https://github.com/mrcjkb/rustaceanvim/assets/12857160/bac9b31c-22ca-40c4-bfd3-b8c5ba4cc49a)

Expand All @@ -453,15 +461,23 @@ vim.keymap.set(
together.

```vim
:RustLsp renderDiagnostic
:RustLsp renderDiagnostic {cycle?|current?}
```
```lua
vim.cmd.RustLsp('renderDiagnostic')
vim.cmd.RustLsp('renderDiagnostic') -- defaults to 'cycle'
vim.cmd.RustLsp({ 'renderDiagnostic', 'cycle' })
vim.cmd.RustLsp({ 'renderDiagnostic', 'current' })
```

Like `vim.diagnostic.goto_next`, `renderDiagnostic` will cycle diagnostics,
starting at the cursor position, until it can find a diagnostic with
rendered data.
- If called with `cycle` or no args:
Like `vim.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.

![](https://github.com/mrcjkb/rustaceanvim/assets/12857160/a972c6b6-c504-4c2a-8380-53451bb8c2de)

Expand Down
114 changes: 114 additions & 0 deletions lua/rustaceanvim/commands/diagnostic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,71 @@ function M.explain_error()
compat.system({ rustc, '--explain', tostring(diagnostic.code) }, nil, vim.schedule_wrap(handler))
end

function M.explain_error_current_line()
if vim.fn.executable(rustc) ~= 1 then
vim.notify('rustc is needed to explain errors.', vim.log.levels.ERROR)
return
end

local win_id = vim.api.nvim_get_current_win()
local cursor_position = vim.api.nvim_win_get_cursor(win_id)

-- get matching diagnostics from current line
local diagnostics = vim.tbl_filter(
function(diagnostic)
return diagnostic.code ~= nil
and diagnostic.source == 'rustc'
and diagnostic.severity == vim.diagnostic.severity.ERROR
end,
vim.diagnostic.get(0, {
lnum = cursor_position[1] - 1,
})
)

-- no matching diagnostics on current line
if #diagnostics == 0 then
vim.notify('No explainable errors found.', vim.log.levels.INFO)
return
end

local diagnostic = diagnostics[1]

---@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, {})
local float_preview_lines = vim.deepcopy(markdown_lines)
table.insert(float_preview_lines, 1, '---')
table.insert(float_preview_lines, 1, '1. Open in split')
vim.schedule(function()
close_hover()
local bufnr, winnr = vim.lsp.util.open_floating_preview(
float_preview_lines,
'markdown',
vim.tbl_extend('keep', config.tools.float_win_config, {
focus = false,
focusable = true,
focus_id = 'rustc-explain-error',
close_events = { 'CursorMoved', 'BufHidden', 'InsertCharPre' },
})
)
_window_state.float_winnr = winnr
set_close_keymaps(bufnr)
set_open_split_keymap(bufnr, winnr, markdown_lines)

if config.tools.float_win_config.auto_focus then
vim.api.nvim_set_current_win(winnr)
end
end)
end

compat.system({ rustc, '--explain', tostring(diagnostic.code) }, nil, vim.schedule_wrap(handler))
end

---@param diagnostic table
---@return string | nil
local function get_rendered_diagnostic(diagnostic)
Expand Down Expand Up @@ -249,4 +314,53 @@ function M.render_diagnostic()
end)
end

function M.render_diagnostic_current_line()
local win_id = vim.api.nvim_get_current_win()
local cursor_position = vim.api.nvim_win_get_cursor(win_id)

-- get rendered diagnostics from current line
local rendered_diagnostics = vim.tbl_map(
function(diagnostic)
return get_rendered_diagnostic(diagnostic)
end,
vim.diagnostic.get(0, {
lnum = cursor_position[1] - 1,
})
)
rendered_diagnostics = vim.tbl_filter(function(diagnostic)
return diagnostic ~= nil
end, rendered_diagnostics)

-- if no renderable diagnostics on current line
if #rendered_diagnostics == 0 then
vim.notify('No renderable diagnostics found.', vim.log.levels.INFO)
return
end

local rendered_diagnostic = rendered_diagnostics[1]
local lines = vim.split(rendered_diagnostic, '\n')
local float_preview_lines = vim.deepcopy(lines)
table.insert(float_preview_lines, 1, '---')
table.insert(float_preview_lines, 1, '1. Open in split')
vim.schedule(function()
close_hover()
local bufnr, winnr = vim.lsp.util.open_floating_preview(
float_preview_lines,
'',
vim.tbl_extend('keep', config.tools.float_win_config, {
focus = false,
focusable = true,
focus_id = 'ra-render-diagnostic',
close_events = { 'CursorMoved', 'BufHidden', 'InsertCharPre' },
})
)
_window_state.float_winnr = winnr
set_close_keymaps(bufnr)
set_open_split_keymap(bufnr, winnr, lines)
if config.tools.float_win_config.auto_focus then
vim.api.nvim_set_current_win(winnr)
end
end)
end

return M
34 changes: 30 additions & 4 deletions lua/rustaceanvim/commands/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,39 @@ local rustlsp_command_tbl = {
end,
},
explainError = {
impl = function(_)
require('rustaceanvim.commands.diagnostic').explain_error()
impl = function(args)
local subcmd = args[1] or 'cycle'
if subcmd == 'cycle' then
require('rustaceanvim.commands.diagnostic').explain_error()
elseif subcmd == 'current' then
require('rustaceanvim.commands.diagnostic').explain_error_current_line()
else
vim.notify(
'explainError: unknown subcommand: ' .. subcmd .. " expected 'cycle' or 'current'",
vim.log.levels.ERROR
)
end
end,
complete = function()
return { 'cycle', 'current' }
end,
},
renderDiagnostic = {
impl = function(_)
require('rustaceanvim.commands.diagnostic').render_diagnostic()
impl = function(args)
local subcmd = args[1] or 'cycle'
if subcmd == 'cycle' then
require('rustaceanvim.commands.diagnostic').render_diagnostic()
elseif subcmd == 'current' then
require('rustaceanvim.commands.diagnostic').render_diagnostic_current_line()
else
vim.notify(
'renderDiagnostic: unknown subcommand: ' .. subcmd .. " expected 'cycle' or 'current'",
vim.log.levels.ERROR
)
end
end,
complete = function()
return { 'cycle', 'current' }
end,
},
rebuildProcMacros = {
Expand Down
28 changes: 19 additions & 9 deletions lua/rustaceanvim/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,25 @@
--- 'expandMacro' - Expand macros recursively.
--- 'moveItem {up|down}' - Move items up or down.
--- 'hover {actions|range}' - Hover actions, or hover over visually selected range.
--- 'explainError' - Display a hover window with explanations form the Rust error index.
--- Like |vim.diagnostic.goto_next|, |explainError| will cycle diagnostics,
--- starting at the cursor position, until it can find a diagnostic with
--- an error code.
--- 'renderDiagnostic' - Display a hover window with the rendered diagnostic,
--- as displayed during `cargo build`.
--- Like |vim.diagnostic.goto_next|, |renderDiagnostic| will cycle diagnostics,
--- starting at the cursor position, until it can find a diagnostic with
--- rendered data.
--- 'explainError {cycle?|current?}' - Display a hover window with explanations form the Rust error index.
--- - If called with |cycle| or no args:
--- Like |vim.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.
--- 'renderDiagnostic {cycle?|current?}' - Display a hover window with the rendered diagnostic,
--- as displayed during |cargo build|.
--- - If called with |cycle| or no args:
--- Like |vim.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.
--- 'openCargo' - Open the Cargo.toml file for the current package.
--- 'openDocs' - Open docs.rs documentation for the symbol under the cursor.
--- 'parentModule' - Open the current module's parent module.
Expand Down

0 comments on commit 34fb2b4

Please sign in to comment.