-
Notifications
You must be signed in to change notification settings - Fork 38.3k
feat(diagnostics): enhance diagnostics display, dynamic virtual text like VS Code Error Lens #1628
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would make a great addition :)
I left some comments for improvements
} or false, | ||
} | ||
end | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should call set_virtual_text
at least once outside of the autocmds.
Without doing that you will have no diagnostics at all, until you switch to insert mode for the first time.
set_virtual_text(false) |
init.lua
Outdated
-- Diagnostic configuration similar to VS Code's Error Lens. | ||
-- In insert mode, diagnostics are displayed as inline virtual text. | ||
-- In normal mode, diagnostics are shown as virtual lines below the affected lines. | ||
|
||
local function set_virtual_text(enable) | ||
local diagnostic_icons = { | ||
[vim.diagnostic.severity.ERROR] = ' ', | ||
[vim.diagnostic.severity.WARN] = ' ', | ||
[vim.diagnostic.severity.INFO] = ' ', | ||
[vim.diagnostic.severity.HINT] = ' ', | ||
} | ||
|
||
vim.diagnostic.config { | ||
update_in_insert = true, -- error messages in insert mode | ||
severity_sort = true, | ||
float = { border = 'rounded', source = 'if_many' }, | ||
underline = { severity = vim.diagnostic.severity.ERROR }, | ||
signs = vim.g.have_nerd_font and { | ||
text = diagnostic_icons, | ||
} or {}, | ||
|
||
virtual_lines = not enable and { | ||
format = function(diagnostic) | ||
return (diagnostic_icons[diagnostic.severity] or '') .. diagnostic.message | ||
end, | ||
} or false, | ||
|
||
virtual_text = enable and { | ||
source = 'if_many', | ||
spacing = 2, | ||
format = function(diagnostic) | ||
return (diagnostic_icons[diagnostic.severity] or '') .. diagnostic.message | ||
end, | ||
} or false, | ||
} | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the logic could be simplified by settings most of the config outside of the function.
i.e. something like this:
-- Diagnostic configuration similar to VS Code's Error Lens. | |
-- In insert mode, diagnostics are displayed as inline virtual text. | |
-- In normal mode, diagnostics are shown as virtual lines below the affected lines. | |
local function set_virtual_text(enable) | |
local diagnostic_icons = { | |
[vim.diagnostic.severity.ERROR] = ' ', | |
[vim.diagnostic.severity.WARN] = ' ', | |
[vim.diagnostic.severity.INFO] = ' ', | |
[vim.diagnostic.severity.HINT] = ' ', | |
} | |
vim.diagnostic.config { | |
update_in_insert = true, -- error messages in insert mode | |
severity_sort = true, | |
float = { border = 'rounded', source = 'if_many' }, | |
underline = { severity = vim.diagnostic.severity.ERROR }, | |
signs = vim.g.have_nerd_font and { | |
text = diagnostic_icons, | |
} or {}, | |
virtual_lines = not enable and { | |
format = function(diagnostic) | |
return (diagnostic_icons[diagnostic.severity] or '') .. diagnostic.message | |
end, | |
} or false, | |
virtual_text = enable and { | |
source = 'if_many', | |
spacing = 2, | |
format = function(diagnostic) | |
return (diagnostic_icons[diagnostic.severity] or '') .. diagnostic.message | |
end, | |
} or false, | |
} | |
end | |
vim.diagnostic.config { | |
update_in_insert = true, -- error messages in insert mode | |
severity_sort = true, | |
float = { border = 'rounded', source = 'if_many' }, | |
underline = { severity = vim.diagnostic.severity.ERROR }, | |
signs = vim.g.have_nerd_font and { | |
text = { | |
[vim.diagnostic.severity.ERROR] = ' ', | |
[vim.diagnostic.severity.WARN] = ' ', | |
[vim.diagnostic.severity.INFO] = ' ', | |
[vim.diagnostic.severity.HINT] = ' ', | |
}, | |
} or {}, | |
} | |
-- Diagnostic configuration similar to VS Code's Error Lens. | |
-- In insert mode, diagnostics are displayed as inline virtual text. | |
-- In normal mode, diagnostics are shown as virtual lines below the affected lines. | |
---@param enable boolean | |
local function set_virtual_text(enable) | |
vim.diagnostic.config { | |
virtual_lines = not enable, | |
virtual_text = enable and { | |
source = 'if_many', | |
spacing = 2, | |
}, | |
} | |
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also in my example, I removed format
as suggested by #1550
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the feedback and suggestions! 🙌
I've updated the code to reflect the recommendations:
- I've kept the severity icons for both
virtual_text
andvirtual_lines
, as I believe it improves the visual clarity and overall diagnostic experience — especially when switching between modes. - I've added a call to
set_virtual_text(false)
right after defining the function. While I didn’t observe any issues during testing without it, I agree it's a good addition for consistency and to prevent potential edge cases. - I also set
underline = true
instead of restricting it to only errors. I found that underlining all severities helps quickly locate the affected lines, improving usability during review and debugging.
If there's still interest in merging this feature, I’d be happy to re-submit the PR with these improvements applied.
Thanks again for the thoughtful review!
-- Diagnostic Config
-- See :help vim.diagnostic.Opts
local diagnostic_icons = {
[vim.diagnostic.severity.ERROR] = ' ',
[vim.diagnostic.severity.WARN] = ' ',
[vim.diagnostic.severity.INFO] = ' ',
[vim.diagnostic.severity.HINT] = ' ',
}
vim.diagnostic.config {
update_in_insert = true,
severity_sort = true,
float = { border = 'rounded', source = 'if_many' },
underline = true, -- { severity = vim.diagnostic.severity.ERROR }
signs = vim.g.have_nerd_font and { text = diagnostic_icons } or {},
}
-- Diagnostic configuration similar to VS Code's Error Lens.
-- In insert mode, diagnostics are displayed as inline virtual text.
-- In normal mode, diagnostics are shown as virtual lines below the affected lines.
---@param enable boolean
local function set_virtual_text(enable)
vim.diagnostic.config {
virtual_lines = not enable and {
format = function(diagnostic)
return (diagnostic_icons[diagnostic.severity] or '') .. diagnostic.message
end,
} or false,
virtual_text = enable and {
source = 'if_many',
spacing = 2,
format = function(diagnostic)
return (diagnostic_icons[diagnostic.severity] or '') .. diagnostic.message
end,
} or false,
}
end
set_virtual_text(false)
vim.api.nvim_create_autocmd('InsertEnter', {
callback = function()
set_virtual_text(true)
end,
})
vim.api.nvim_create_autocmd('InsertLeave', {
callback = function()
set_virtual_text(false)
end,
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there's still interest in merging this feature, I’d be happy to re-submit the PR with these improvements applied.
Go for it.
I think it would be a great addition for kickstart (or at least for my config, if the maintainers don't agree with me :)
Added functionality to mimic VSCode's Error Lens behavior: - Displays diagnostics as virtual text with severity icons during insert mode. - Switches to virtual lines with formatted messages in normal mode for better readability. Improves real-time feedback while coding and enhances the visual presentation of diagnostics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 😄
Just a heads-up.. exiting insert mode with Ctrl-C doesn't change back to virtual lines exiting with esc does however |
Added functionality to mimic VSCode's Error Lens behavior: - Displays diagnostics as virtual text with severity icons during insert mode. - Switches to virtual lines with formatted messages in normal mode for better readability. - Replaced `InsertLeave` with `ModeChanged` (`i:*`) to ensure diagnostics update correctly even when exiting insert> This refactor improves code readability, follows single-responsibility principles, and makes it easier to maintain > Improves real-time feedback while coding and enhances the visual presentation of diagnostics.
Thanks, done. |
After using it for a bit I find the transition between normal and insert mode a bit distracting, because all of the line positions change. Maybe this fits better as a toggle instead? |
Same here. At first it looked cool. Now it's just annoying. Especially working with new code about 3-5 lines. You are destined to have diagnostic warnings/errors when writing a new block of code. And with vim, since we tend to enter and exit insert mode often, this is annoying. I have turned it off in my config for now. Maybe a toggle would be more useful. |
yip.. also.. i tried removing the diagnostic messages and only keeping the icons too, but it still felt distracting imo it also disappears anyways as soon as you start typing in insert mode |
An alternative approach would be to automatically open a floating window when the cursor is over a diagnostic message, no need to press a key. You could still use a key binding to focus the window, and press q to close it when you're done. -- Diagnostic Config
-- See :help vim.diagnostic.Opts
local diagnostic_icons = {
[vim.diagnostic.severity.ERROR] = ' ',
[vim.diagnostic.severity.WARN] = ' ',
[vim.diagnostic.severity.INFO] = ' ',
[vim.diagnostic.severity.HINT] = ' ',
}
vim.diagnostic.config {
update_in_insert = true,
severity_sort = true,
underline = true, -- { severity = vim.diagnostic.severity.ERROR }
signs = vim.g.have_nerd_font and { text = diagnostic_icons } or {},
}
-- Diagnostic configuration similar to VS Code's Error Lens.
-- In insert mode, diagnostics are displayed as inline virtual text.
-- In normal mode, diagnostics are shown as virtual lines below the affected lines.
---@param enable boolean
local function set_virtual_text(enable)
vim.diagnostic.config {
virtual_text = enable and {
source = 'if_many',
spacing = 2,
format = function(diagnostic)
return (diagnostic_icons[diagnostic.severity] or '') .. diagnostic.message
end,
} or false,
}
end
set_virtual_text(false)
vim.api.nvim_create_autocmd('InsertEnter', {
callback = function()
set_virtual_text(true)
end,
})
vim.api.nvim_create_autocmd('ModeChanged', {
pattern = 'i:*',
callback = function()
set_virtual_text(false)
end,
})
vim.api.nvim_create_autocmd('CursorHold', {
callback = function()
vim.diagnostic.open_float(nil, { focusable = false, source = 'if_many', border = 'rounded' })
end,
})
vim.keymap.set('n', '<leader>df', function()
vim.diagnostic.open_float(nil, { border = 'rounded', source = 'if_many' })
end, { desc = 'Enter Diagnostics in Floating Window' })
|
Thank you for the all variations, the code is interesting and educational in itself, regardless of its functionality :) |
Hi there! 👋
This PR introduces a small enhancement to the diagnostic experience in Neovim.
Instead of keeping the diagnostics always in virtual text or virtual lines, this new setup dynamically switches between the two based on the current mode:
Insert mode: diagnostics are shown inline using virtual text — similar to the behavior of the Error Lens extension in VS Code.
Normal mode: diagnostics switch to virtual lines below the affected code, offering a cleaner and more elegant overview.
This approach provides real-time feedback while coding and a less intrusive display when navigating the code. It's a feature that requires a plugin in editors like VS Code, but here it’s done natively with just a bit of Lua! 😄
I believe this improves the overall developer experience by making diagnostics both more immediate and more readable.
Let me know what you think — open to suggestions!
Thanks 🙏