Skip to content

fix(diagnostic): allow virtual_{lines,text} cursor exclusivity #33517

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

mwcz
Copy link

@mwcz mwcz commented Apr 17, 2025

Problem:

virtual_text diagnostics are great when skimming a file, and virtual_lines are great when "zooming in" on a particular problem. Having both enabled results in duplicate diagnostics on-screen.

First reported in #33092, with this image as an example:
image

Solution:

This PR expands the behavior of current_line for virtual_text and virtual_lines by making virtual_text.current_line = false distinct from nil. If you set:

vim.diagnostic.config({
  virtual_text = { current_line = false },
  virtual_lines = { current_line = true },
})

With this configuration, virtual_text will be used to display diagnostics until the cursor reaches the same line, at which point they will be hidden and virtual_lines will take its place.

The situation in the image above now behaves like this:

image

image

Note to maintainers: this is my first PR to neovim and I haven't looked into writing tests for this yet but I will look into it if desired.

Problem:

virtual_text diagnostics are great when skimming a file, and
virtual_lines are great when "zooming in" on a particular problem.
Having both enabled results in duplicate diagnostics on-screen.

Solution:

This PR expands the behavior of `current_line` for virtual_text and
virtual_lines by making `virtual_text.current_line = false` distinct
from `nil`.  If you set:

    vim.diagnostic.config({
      virtual_text = { current_line = false },
      virtual_lines = { current_line = true },
    })

With this configuration, virtual_text will be used to display
diagnostics until the cursor reaches the same line, at which point they
will be hidden and virtual_lines will take its place.
@justinmk

This comment was marked as resolved.

@justinmk justinmk added the needs:response waiting for reply from the author label Apr 24, 2025
@gpanders
Copy link
Member

I think a simpler way to implement this would be to check for opts.current_line == false in the render_virtual_text function. That way we don't have to filter diagnostics yet again, creating another new table. Instead we just skip adding the extmark for the current line if current_line is false.

This will need a docs update too to indicate that

  • current_line=true => only show virtual text on the cursor line
  • current_line=false => show virtual text on all lines except the cursor line
  • current_line=nil => show virtual text on all lines

Or as @justinmk suggested, using a string enum instead of a nullable boolean (if we do that we'd also want to change the option for virtual_text as well, both of which would be a breaking change, so I'm personally in favor of sticking with the boolean).

@justinmk
Copy link
Member

current_line=false => show virtual text on all lines except the cursor line

This concise framing makes this PR much easier to understand. So I'm in favor of the false approach now :)

api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1)

for line, line_diagnostics in pairs(diagnostics) do
local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts)
local should_render_current_line = (opts.current_line == true and line == lnum)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If opts.current_line is nil this should also be true.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That case is handled by the last part of the condition: or (opts == true or type(opts) == 'table')

@mwcz
Copy link
Author

mwcz commented Apr 29, 2025

I think a simpler way to implement this would be to check for opts.current_line == false in the render_virtual_text function. That way we don't have to filter diagnostics yet again, creating another new table. Instead we just skip adding the extmark for the current line if current_line is false.

Thanks, that does sound a lot simpler. I reimplemented it there and it's a lot cleaner.

If you're happy with the code, I'll update the docs. Do you think this needs tests?

@github-actions github-actions bot removed the needs:response waiting for reply from the author label Apr 29, 2025
@justinmk
Copy link
Member

justinmk commented Apr 29, 2025

Do you think this needs tests?

could be a 2-line change in an existing test.

docs

--- Only show diagnostics for the current line.
--- (default `false`)
--- @field current_line? boolean
should be structured like:

  • true: only on the cursor line
  • false: on all lines except the cursor line
  • nil: on all lines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants