mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	fix(diagnostic): allow virtual_{lines,text} cursor exclusivity #33517
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.
			
			
This commit is contained in:
		| @@ -624,8 +624,12 @@ Lua module: vim.diagnostic                                    *diagnostic-api* | |||||||
|       • {severity}?           (`vim.diagnostic.SeverityFilter`) Only show |       • {severity}?           (`vim.diagnostic.SeverityFilter`) Only show | ||||||
|                               virtual text for diagnostics matching the given |                               virtual text for diagnostics matching the given | ||||||
|                               severity |diagnostic-severity| |                               severity |diagnostic-severity| | ||||||
|       • {current_line}?       (`boolean`) Only show diagnostics for the |       • {current_line}?       (`boolean`) Show or hide diagnostics based on | ||||||
|                               current line. (default `false`) |                               the current cursor line. If `true`, only | ||||||
|  |                               diagnostics on the current cursor line are | ||||||
|  |                               shown. If `false`, all diagnostics are shown | ||||||
|  |                               except on the current cursor line. If `nil`, all | ||||||
|  |                               diagnostics are shown. (default `nil`) | ||||||
|       • {source}?             (`boolean|"if_many"`) Include the diagnostic |       • {source}?             (`boolean|"if_many"`) Include the diagnostic | ||||||
|                               source in virtual text. Use `'if_many'` to only |                               source in virtual text. Use `'if_many'` to only | ||||||
|                               show sources if there is more than one |                               show sources if there is more than one | ||||||
|   | |||||||
| @@ -190,8 +190,10 @@ end | |||||||
| --- severity |diagnostic-severity| | --- severity |diagnostic-severity| | ||||||
| --- @field severity? vim.diagnostic.SeverityFilter | --- @field severity? vim.diagnostic.SeverityFilter | ||||||
| --- | --- | ||||||
| --- Only show diagnostics for the current line. | --- Show or hide diagnostics based on the current cursor line.  If `true`, only diagnostics on the | ||||||
| --- (default `false`) | --- current cursor line are shown.  If `false`, all diagnostics are shown except on the current | ||||||
|  | --- cursor line.  If `nil`, all diagnostics are shown. | ||||||
|  | --- (default `nil`) | ||||||
| --- @field current_line? boolean | --- @field current_line? boolean | ||||||
| --- | --- | ||||||
| --- Include the diagnostic source in virtual text. Use `'if_many'` to only | --- Include the diagnostic source in virtual text. Use `'if_many'` to only | ||||||
| @@ -1562,12 +1564,15 @@ M.handlers.underline = { | |||||||
| --- @param diagnostics table<integer, vim.Diagnostic[]> | --- @param diagnostics table<integer, vim.Diagnostic[]> | ||||||
| --- @param opts vim.diagnostic.Opts.VirtualText | --- @param opts vim.diagnostic.Opts.VirtualText | ||||||
| local function render_virtual_text(namespace, bufnr, diagnostics, opts) | local function render_virtual_text(namespace, bufnr, diagnostics, opts) | ||||||
|  |   local lnum = api.nvim_win_get_cursor(0)[1] - 1 | ||||||
|   api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) |   api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) | ||||||
|  |  | ||||||
|   for line, line_diagnostics in pairs(diagnostics) do |   for line, line_diagnostics in pairs(diagnostics) do | ||||||
|     local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts) |     local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts) | ||||||
|  |     local skip = (opts.current_line == true and line ~= lnum) | ||||||
|  |       or (opts.current_line == false and line == lnum) | ||||||
|  |  | ||||||
|     if virt_texts then |     if virt_texts and not skip then | ||||||
|       api.nvim_buf_set_extmark(bufnr, namespace, line, 0, { |       api.nvim_buf_set_extmark(bufnr, namespace, line, 0, { | ||||||
|         hl_mode = opts.hl_mode or 'combine', |         hl_mode = opts.hl_mode or 'combine', | ||||||
|         virt_text = virt_texts, |         virt_text = virt_texts, | ||||||
| @@ -1621,32 +1626,18 @@ M.handlers.virtual_text = { | |||||||
|  |  | ||||||
|     local line_diagnostics = diagnostic_lines(diagnostics) |     local line_diagnostics = diagnostic_lines(diagnostics) | ||||||
|  |  | ||||||
|     if opts.virtual_text.current_line == true then |     if opts.virtual_text.current_line ~= nil then | ||||||
|       api.nvim_create_autocmd('CursorMoved', { |       api.nvim_create_autocmd('CursorMoved', { | ||||||
|         buffer = bufnr, |         buffer = bufnr, | ||||||
|         group = ns.user_data.virt_text_augroup, |         group = ns.user_data.virt_text_augroup, | ||||||
|         callback = function() |         callback = function() | ||||||
|           local lnum = api.nvim_win_get_cursor(0)[1] - 1 |           render_virtual_text(ns.user_data.virt_text_ns, bufnr, line_diagnostics, opts.virtual_text) | ||||||
|           render_virtual_text( |  | ||||||
|             ns.user_data.virt_text_ns, |  | ||||||
|             bufnr, |  | ||||||
|             { [lnum] = diagnostics_at_cursor(line_diagnostics) }, |  | ||||||
|             opts.virtual_text |  | ||||||
|           ) |  | ||||||
|         end, |         end, | ||||||
|       }) |       }) | ||||||
|       -- Also show diagnostics for the current line before the first CursorMoved event. |  | ||||||
|       local lnum = api.nvim_win_get_cursor(0)[1] - 1 |  | ||||||
|       render_virtual_text( |  | ||||||
|         ns.user_data.virt_text_ns, |  | ||||||
|         bufnr, |  | ||||||
|         { [lnum] = diagnostics_at_cursor(line_diagnostics) }, |  | ||||||
|         opts.virtual_text |  | ||||||
|       ) |  | ||||||
|     else |  | ||||||
|       render_virtual_text(ns.user_data.virt_text_ns, bufnr, line_diagnostics, opts.virtual_text) |  | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  |     render_virtual_text(ns.user_data.virt_text_ns, bufnr, line_diagnostics, opts.virtual_text) | ||||||
|  |  | ||||||
|     save_extmarks(ns.user_data.virt_text_ns, bufnr) |     save_extmarks(ns.user_data.virt_text_ns, bufnr) | ||||||
|   end, |   end, | ||||||
|   hide = function(namespace, bufnr) |   hide = function(namespace, bufnr) | ||||||
|   | |||||||
| @@ -2144,6 +2144,25 @@ describe('vim.diagnostic', function() | |||||||
|       eq(1, #result) |       eq(1, #result) | ||||||
|       eq(' Error here!', result[1][4].virt_text[3][1]) |       eq(' Error here!', result[1][4].virt_text[3][1]) | ||||||
|     end) |     end) | ||||||
|  |  | ||||||
|  |     it('can hide virtual_text for the current line', function() | ||||||
|  |       local result = exec_lua(function() | ||||||
|  |         vim.api.nvim_win_set_cursor(0, { 1, 0 }) | ||||||
|  |  | ||||||
|  |         vim.diagnostic.config({ virtual_text = { current_line = false } }) | ||||||
|  |  | ||||||
|  |         vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, { | ||||||
|  |           _G.make_error('Error here!', 0, 0, 0, 0, 'foo_server'), | ||||||
|  |           _G.make_error('Another error there!', 1, 0, 1, 0, 'foo_server'), | ||||||
|  |         }) | ||||||
|  |  | ||||||
|  |         local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns) | ||||||
|  |         return extmarks | ||||||
|  |       end) | ||||||
|  |  | ||||||
|  |       eq(1, #result) | ||||||
|  |       eq(' Another error there!', result[1][4].virt_text[3][1]) | ||||||
|  |     end) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   describe('handlers.virtual_lines', function() |   describe('handlers.virtual_lines', function() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Michael Clayton
					Michael Clayton