local t = require('test.functional.testutil')() local NIL = vim.NIL local command = t.command local clear = t.clear local exec_lua = t.exec_lua local eq = t.eq local matches = t.matches local api = t.api local pcall_err = t.pcall_err local fn = t.fn describe('vim.diagnostic', function() before_each(function() clear() exec_lua [[ require('vim.diagnostic') function make_diagnostic(msg, x1, y1, x2, y2, severity, source, code) return { lnum = x1, col = y1, end_lnum = x2, end_col = y2, message = msg, severity = severity, source = source, code = code, } end function make_error(msg, x1, y1, x2, y2, source, code) return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.ERROR, source, code) end function make_warning(msg, x1, y1, x2, y2, source, code) return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.WARN, source, code) end function make_info(msg, x1, y1, x2, y2, source, code) return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.INFO, source, code) end function make_hint(msg, x1, y1, x2, y2, source, code) return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.HINT, source, code) end function count_diagnostics(bufnr, severity, namespace) return #vim.diagnostic.get(bufnr, {severity = severity, namespace = namespace}) end function count_extmarks(bufnr, namespace) local ns = vim.diagnostic.get_namespace(namespace) local extmarks = 0 if ns.user_data.virt_text_ns then extmarks = extmarks + #vim.api.nvim_buf_get_extmarks(bufnr, ns.user_data.virt_text_ns, 0, -1, {}) end if ns.user_data.underline_ns then extmarks = extmarks + #vim.api.nvim_buf_get_extmarks(bufnr, ns.user_data.underline_ns, 0, -1, {}) end return extmarks end function get_virt_text_extmarks(ns) local ns = vim.diagnostic.get_namespace(ns) local virt_text_ns = ns.user_data.virt_text_ns return vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, virt_text_ns, 0, -1, {details = true}) end ]] exec_lua([[ diagnostic_ns = vim.api.nvim_create_namespace("diagnostic_spec") other_ns = vim.api.nvim_create_namespace("other_namespace") diagnostic_bufnr = vim.api.nvim_create_buf(true, false) local lines = {"1st line of text", "2nd line of text", "wow", "cool", "more", "lines"} vim.fn.bufload(diagnostic_bufnr) vim.api.nvim_buf_set_lines(diagnostic_bufnr, 0, 1, false, lines) return diagnostic_bufnr ]]) end) it('creates highlight groups', function() command('runtime plugin/diagnostic.vim') eq({ 'DiagnosticDeprecated', 'DiagnosticError', 'DiagnosticFloatingError', 'DiagnosticFloatingHint', 'DiagnosticFloatingInfo', 'DiagnosticFloatingOk', 'DiagnosticFloatingWarn', 'DiagnosticHint', 'DiagnosticInfo', 'DiagnosticOk', 'DiagnosticSignError', 'DiagnosticSignHint', 'DiagnosticSignInfo', 'DiagnosticSignOk', 'DiagnosticSignWarn', 'DiagnosticUnderlineError', 'DiagnosticUnderlineHint', 'DiagnosticUnderlineInfo', 'DiagnosticUnderlineOk', 'DiagnosticUnderlineWarn', 'DiagnosticUnnecessary', 'DiagnosticVirtualTextError', 'DiagnosticVirtualTextHint', 'DiagnosticVirtualTextInfo', 'DiagnosticVirtualTextOk', 'DiagnosticVirtualTextWarn', 'DiagnosticWarn', }, fn.getcompletion('Diagnostic', 'highlight')) end) it('retrieves diagnostics from all buffers and namespaces', function() local result = exec_lua [[ local other_bufnr = vim.api.nvim_create_buf(true, false) local lines = vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, -1, true) vim.api.nvim_buf_set_lines(other_bufnr, 0, 1, false, lines) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), make_error('Diagnostic #2', 2, 1, 2, 1), }) vim.diagnostic.set(other_ns, other_bufnr, { make_error('Diagnostic #3', 3, 1, 3, 1), }) return vim.diagnostic.get() ]] eq(3, #result) eq( 2, exec_lua( [[return #vim.tbl_filter(function(d) return d.bufnr == diagnostic_bufnr end, ...)]], result ) ) eq('Diagnostic #1', result[1].message) end) it('removes diagnostics from the cache when a buffer is removed', function() eq( 2, exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) local other_bufnr = vim.fn.bufadd('test | test') local lines = vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, -1, true) vim.api.nvim_buf_set_lines(other_bufnr, 0, 1, false, lines) vim.cmd('bunload! ' .. other_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), make_error('Diagnostic #2', 2, 1, 2, 1), }) vim.diagnostic.set(diagnostic_ns, other_bufnr, { make_error('Diagnostic #3', 3, 1, 3, 1), }) vim.api.nvim_set_current_buf(other_bufnr) vim.opt_local.buflisted = true vim.cmd('bwipeout!') return #vim.diagnostic.get() ]] ) eq( 2, exec_lua [[ vim.api.nvim_set_current_buf(diagnostic_bufnr) vim.opt_local.buflisted = false return #vim.diagnostic.get() ]] ) eq( 0, exec_lua [[ vim.cmd('bwipeout!') return #vim.diagnostic.get() ]] ) end) it('removes diagnostic from stale cache on reset', function() local diagnostics = exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), make_error('Diagnostic #2', 2, 1, 2, 1), }) local other_bufnr = vim.fn.bufadd('test | test') vim.cmd('noautocmd bwipeout! ' .. diagnostic_bufnr) return vim.diagnostic.get(diagnostic_bufnr) ]] eq(2, #diagnostics) diagnostics = exec_lua [[ vim.diagnostic.reset() return vim.diagnostic.get() ]] eq(0, #diagnostics) end) it('always returns a copy of diagnostic tables', function() local result = exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), }) local diag = vim.diagnostic.get() diag[1].col = 10000 return vim.diagnostic.get()[1].col == 10000 ]] eq(false, result) end) it('resolves buffer number 0 to the current buffer', function() eq( 2, exec_lua [[ vim.api.nvim_set_current_buf(diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), make_error('Diagnostic #2', 2, 1, 2, 1), }) return #vim.diagnostic.get(0) ]] ) end) it('saves and count a single error', function() eq( 1, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), }) return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns) ]] ) end) it('saves and count multiple errors', function() eq( 2, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), make_error('Diagnostic #2', 2, 1, 2, 1), }) return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns) ]] ) end) it('saves and count from multiple namespaces', function() eq( { 1, 1, 2 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic From Server 1', 1, 1, 1, 1), }) vim.diagnostic.set(other_ns, diagnostic_bufnr, { make_error('Diagnostic From Server 2', 1, 1, 1, 1), }) return { -- First namespace count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), -- Second namespace count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, other_ns), -- All namespaces count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR), } ]] ) end) it('saves and count from multiple namespaces with respect to severity', function() eq( { 3, 0, 3 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic From Server 1:1', 1, 1, 1, 1), make_error('Diagnostic From Server 1:2', 2, 2, 2, 2), make_error('Diagnostic From Server 1:3', 2, 3, 3, 2), }) vim.diagnostic.set(other_ns, diagnostic_bufnr, { make_warning('Warning From Server 2', 3, 3, 3, 3), }) return { -- Namespace 1 count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), -- Namespace 2 count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, other_ns), -- All namespaces count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR), } ]] ) end) it('handles one namespace clearing highlights while the other still has highlights', function() -- 1 Error (1) -- 1 Warning (2) -- 1 Warning (2) + 1 Warning (1) -- 2 highlights and 2 underlines (since error) -- 1 highlight + 1 underline local all_highlights = { 1, 1, 2, 4, 2 } eq( all_highlights, exec_lua [[ local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 2, 1, 2, 3), } local ns_2_diags = { make_warning("Warning 1", 2, 1, 2, 3), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) return { count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), count_extmarks(diagnostic_bufnr, diagnostic_ns), count_extmarks(diagnostic_bufnr, other_ns), } ]] ) -- Clear diagnostics from namespace 1, and make sure we have the right amount of stuff for namespace 2 eq( { 1, 1, 2, 0, 2 }, exec_lua [[ vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) return { count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), count_extmarks(diagnostic_bufnr, diagnostic_ns), count_extmarks(diagnostic_bufnr, other_ns), } ]] ) -- Show diagnostics from namespace 1 again eq( all_highlights, exec_lua([[ vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) return { count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), count_extmarks(diagnostic_bufnr, diagnostic_ns), count_extmarks(diagnostic_bufnr, other_ns), } ]]) ) end) it('does not display diagnostics when disabled', function() eq( { 0, 2 }, exec_lua [[ local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 2, 1, 2, 3), } local ns_2_diags = { make_warning("Warning 1", 2, 1, 2, 3), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) return { count_extmarks(diagnostic_bufnr, diagnostic_ns), count_extmarks(diagnostic_bufnr, other_ns), } ]] ) eq( { 4, 0 }, exec_lua [[ vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) vim.diagnostic.disable(diagnostic_bufnr, other_ns) return { count_extmarks(diagnostic_bufnr, diagnostic_ns), count_extmarks(diagnostic_bufnr, other_ns), } ]] ) end) describe('show() and hide()', function() it('works', function() local result = exec_lua [[ local other_bufnr = vim.api.nvim_create_buf(true, false) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) local result = {} vim.diagnostic.config({ underline = false, virtual_text = true }) local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 2, 1, 2, 5), } local ns_2_diags = { make_warning("Warning 1", 2, 1, 2, 5), } local other_buffer_diags = { make_info("This is interesting", 0, 0, 0, 0) } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) vim.diagnostic.set(diagnostic_ns, other_bufnr, other_buffer_diags) -- All buffers and namespaces table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) -- Hide one namespace vim.diagnostic.hide(diagnostic_ns) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) -- Show one namespace vim.diagnostic.show(diagnostic_ns) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) -- Hide one buffer vim.diagnostic.hide(nil, other_bufnr) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) -- Hide everything vim.diagnostic.hide() table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) -- Show one buffer vim.diagnostic.show(nil, diagnostic_bufnr) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) return result ]] eq(4, result[1]) eq(1, result[2]) eq(4, result[3]) eq(3, result[4]) eq(0, result[5]) eq(3, result[6]) end) it("doesn't error after bwipeout on buffer", function() exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {{ lnum = 0, end_lnum = 0, col = 0, end_col = 0 }}) vim.cmd("bwipeout! " .. diagnostic_bufnr) vim.diagnostic.show(diagnostic_ns) vim.diagnostic.hide(diagnostic_ns) ]] end) end) describe('enable() and disable()', function() it('works without arguments', function() local result = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) local result = {} vim.diagnostic.config({ underline = false, virtual_text = true }) local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 2, 1, 2, 5), } local ns_2_diags = { make_warning("Warning 1", 2, 1, 2, 5), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) vim.diagnostic.disable() table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) -- Create a new buffer local other_bufnr = vim.api.nvim_create_buf(true, false) local other_buffer_diags = { make_info("This is interesting", 0, 0, 0, 0) } vim.diagnostic.set(diagnostic_ns, other_bufnr, other_buffer_diags) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) vim.diagnostic.enable() table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) return result ]] eq(3, result[1]) eq(0, result[2]) eq(0, result[3]) eq(4, result[4]) end) it('works with only a buffer argument', function() local result = exec_lua [[ local other_bufnr = vim.api.nvim_create_buf(true, false) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) local result = {} vim.diagnostic.config({ underline = false, virtual_text = true }) local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 2, 1, 2, 5), } local ns_2_diags = { make_warning("Warning 1", 2, 1, 2, 5), } local other_buffer_diags = { make_info("This is interesting", 0, 0, 0, 0) } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) vim.diagnostic.set(diagnostic_ns, other_bufnr, other_buffer_diags) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) vim.diagnostic.disable(diagnostic_bufnr) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) vim.diagnostic.enable(diagnostic_bufnr) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) vim.diagnostic.disable(other_bufnr) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) return result ]] eq(4, result[1]) eq(1, result[2]) eq(4, result[3]) eq(3, result[4]) end) it('works with only a namespace argument', function() local result = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) local result = {} vim.diagnostic.config({ underline = false, virtual_text = true }) local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 2, 1, 2, 5), } local ns_2_diags = { make_warning("Warning 1", 2, 1, 2, 5), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) vim.diagnostic.disable(nil, diagnostic_ns) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) vim.diagnostic.enable(nil, diagnostic_ns) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) vim.diagnostic.disable(nil, other_ns) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) return result ]] eq(3, result[1]) eq(1, result[2]) eq(3, result[3]) eq(2, result[4]) end) it('works with both a buffer and a namespace argument', function() local result = exec_lua [[ local other_bufnr = vim.api.nvim_create_buf(true, false) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) local result = {} vim.diagnostic.config({ underline = false, virtual_text = true }) local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 2, 1, 2, 5), } local ns_2_diags = { make_warning("Warning 1", 2, 1, 2, 5), } local other_buffer_diags = { make_info("This is interesting", 0, 0, 0, 0) } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) vim.diagnostic.set(diagnostic_ns, other_bufnr, other_buffer_diags) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) vim.diagnostic.disable(diagnostic_bufnr, other_ns) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) -- Should have no effect vim.diagnostic.disable(other_bufnr, other_ns) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) return result ]] eq(4, result[1]) eq(2, result[2]) eq(1, result[3]) eq(3, result[4]) eq(3, result[5]) end) end) describe('reset()', function() it('diagnostic count is 0 and displayed diagnostics are 0 after call', function() -- 1 Error (1) -- 1 Warning (2) -- 1 Warning (2) + 1 Warning (1) -- 2 highlights and 2 underlines (since error) -- 1 highlight + 1 underline local all_highlights = { 1, 1, 2, 4, 2 } eq( all_highlights, exec_lua [[ local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 2, 1, 2, 3), } local ns_2_diags = { make_warning("Warning 1", 2, 1, 2, 3), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) return { count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), count_extmarks(diagnostic_bufnr, diagnostic_ns), count_extmarks(diagnostic_bufnr, other_ns), } ]] ) -- Reset diagnostics from namespace 1 exec_lua([[ vim.diagnostic.reset(diagnostic_ns) ]]) -- Make sure we have the right diagnostic count eq( { 0, 1, 1, 0, 2 }, exec_lua [[ local diagnostic_count = {} vim.wait(100, function () diagnostic_count = { count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), count_extmarks(diagnostic_bufnr, diagnostic_ns), count_extmarks(diagnostic_bufnr, other_ns), } end ) return diagnostic_count ]] ) -- Reset diagnostics from namespace 2 exec_lua([[ vim.diagnostic.reset(other_ns) ]]) -- Make sure we have the right diagnostic count eq( { 0, 0, 0, 0, 0 }, exec_lua [[ local diagnostic_count = {} vim.wait(100, function () diagnostic_count = { count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), count_extmarks(diagnostic_bufnr, diagnostic_ns), count_extmarks(diagnostic_bufnr, other_ns), } end ) return diagnostic_count ]] ) end) it("doesn't error after bwipeout called on buffer", function() exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {{ lnum = 0, end_lnum = 0, col = 0, end_col = 0 }}) vim.cmd("bwipeout! " .. diagnostic_bufnr) vim.diagnostic.reset(diagnostic_ns) ]] end) end) describe('get_next_pos()', function() it('can find the next pos with only one namespace', function() eq( { 1, 1 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) return vim.diagnostic.get_next_pos() ]] ) end) it('can find next pos with two errors', function() eq( { 4, 4 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), make_error('Diagnostic #2', 4, 4, 4, 4), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {3, 1}) return vim.diagnostic.get_next_pos { namespace = diagnostic_ns } ]] ) end) it('can cycle when position is past error', function() eq( { 1, 1 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {3, 1}) return vim.diagnostic.get_next_pos { namespace = diagnostic_ns } ]] ) end) it('will not cycle when wrap is off', function() eq( false, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {3, 1}) return vim.diagnostic.get_next_pos { namespace = diagnostic_ns, wrap = false } ]] ) end) it('can cycle even from the last line', function() eq( { 4, 4 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #2', 4, 4, 4, 4), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {vim.api.nvim_buf_line_count(0), 1}) return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns } ]] ) end) it('works with diagnostics past the end of the line #16349', function() eq( { 4, 0 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 3, 9001, 3, 9001), make_error('Diagnostic #2', 4, 0, 4, 0), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {1, 1}) vim.diagnostic.goto_next { float = false } return vim.diagnostic.get_next_pos { namespace = diagnostic_ns } ]] ) end) it('works with diagnostics before the start of the line', function() eq( { 4, 0 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 3, 9001, 3, 9001), make_error('Diagnostic #2', 4, -1, 4, -1), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {1, 1}) vim.diagnostic.goto_next { float = false } return vim.diagnostic.get_next_pos { namespace = diagnostic_ns } ]] ) end) end) describe('get_prev_pos()', function() it('can find the prev pos with only one namespace', function() eq( { 1, 1 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {3, 1}) return vim.diagnostic.get_prev_pos() ]] ) end) it('can find prev pos with two errors', function() eq( { 1, 1 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), make_error('Diagnostic #2', 4, 4, 4, 4), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {3, 1}) return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns } ]] ) end) it('can cycle when position is past error', function() eq( { 4, 4 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #2', 4, 4, 4, 4), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {3, 1}) return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns } ]] ) end) it('respects wrap parameter', function() eq( false, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #2', 4, 4, 4, 4), }) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.api.nvim_win_set_cursor(0, {3, 1}) return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns, wrap = false} ]] ) end) end) describe('get()', function() it('returns an empty table when no diagnostics are present', function() eq({}, exec_lua [[return vim.diagnostic.get(diagnostic_bufnr, {namespace=diagnostic_ns})]]) end) it('returns all diagnostics when no severity is supplied', function() eq( 2, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 3), }) return #vim.diagnostic.get(diagnostic_bufnr) ]] ) end) it('returns only requested diagnostics when severity range is supplied', function() eq( { 2, 3, 2 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 3), make_info("Ignored information", 1, 1, 2, 3), make_hint("Here's a hint", 1, 1, 2, 3), }) return { #vim.diagnostic.get(diagnostic_bufnr, { severity = {min=vim.diagnostic.severity.WARN} }), #vim.diagnostic.get(diagnostic_bufnr, { severity = {max=vim.diagnostic.severity.WARN} }), #vim.diagnostic.get(diagnostic_bufnr, { severity = { min=vim.diagnostic.severity.INFO, max=vim.diagnostic.severity.WARN, } }), } ]] ) end) it('returns only requested diagnostics when severities are supplied', function() eq( { 1, 1, 2 }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 3), make_info("Ignored information", 1, 1, 2, 3), make_hint("Here's a hint", 1, 1, 2, 3), }) return { #vim.diagnostic.get(diagnostic_bufnr, { severity = {vim.diagnostic.severity.WARN} }), #vim.diagnostic.get(diagnostic_bufnr, { severity = {vim.diagnostic.severity.ERROR} }), #vim.diagnostic.get(diagnostic_bufnr, { severity = { vim.diagnostic.severity.INFO, vim.diagnostic.severity.WARN, } }), } ]] ) end) it('allows filtering by line', function() eq( 1, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 3), make_info("Ignored information", 1, 1, 2, 3), make_error("Error On Other Line", 2, 1, 1, 5), }) return #vim.diagnostic.get(diagnostic_bufnr, {lnum = 2}) ]] ) end) end) describe('count', function() it('returns actually present severity counts', function() eq( exec_lua [[return { [vim.diagnostic.severity.ERROR] = 4, [vim.diagnostic.severity.WARN] = 3, [vim.diagnostic.severity.INFO] = 2, [vim.diagnostic.severity.HINT] = 1, }]], exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 2), make_error("Error 2", 1, 3, 1, 4), make_error("Error 3", 1, 5, 1, 6), make_error("Error 4", 1, 7, 1, 8), make_warning("Warning 1", 2, 1, 2, 2), make_warning("Warning 2", 2, 3, 2, 4), make_warning("Warning 3", 2, 5, 2, 6), make_info("Info 1", 3, 1, 3, 2), make_info("Info 2", 3, 3, 3, 4), make_hint("Hint 1", 4, 1, 4, 2), }) return vim.diagnostic.count(diagnostic_bufnr) ]] ) eq( exec_lua [[return { [vim.diagnostic.severity.ERROR] = 2, [vim.diagnostic.severity.INFO] = 1, }]], exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 2), make_error("Error 2", 1, 3, 1, 4), make_info("Info 1", 3, 1, 3, 2), }) return vim.diagnostic.count(diagnostic_bufnr) ]] ) end) it('returns only requested diagnostics count when severity range is supplied', function() eq( exec_lua [[return { { [vim.diagnostic.severity.ERROR] = 1, [vim.diagnostic.severity.WARN] = 1 }, { [vim.diagnostic.severity.WARN] = 1, [vim.diagnostic.severity.INFO] = 1, [vim.diagnostic.severity.HINT] = 1 }, { [vim.diagnostic.severity.WARN] = 1, [vim.diagnostic.severity.INFO] = 1 }, }]], exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 3), make_info("Ignored information", 1, 1, 2, 3), make_hint("Here's a hint", 1, 1, 2, 3), }) return { vim.diagnostic.count(diagnostic_bufnr, { severity = {min=vim.diagnostic.severity.WARN} }), vim.diagnostic.count(diagnostic_bufnr, { severity = {max=vim.diagnostic.severity.WARN} }), vim.diagnostic.count(diagnostic_bufnr, { severity = { min=vim.diagnostic.severity.INFO, max=vim.diagnostic.severity.WARN, } }), } ]] ) end) it('returns only requested diagnostics when severities are supplied', function() eq( exec_lua [[return { { [vim.diagnostic.severity.WARN] = 1 }, { [vim.diagnostic.severity.ERROR] = 1 }, { [vim.diagnostic.severity.WARN] = 1, [vim.diagnostic.severity.INFO] = 1 }, }]], exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 3), make_info("Ignored information", 1, 1, 2, 3), make_hint("Here's a hint", 1, 1, 2, 3), }) return { vim.diagnostic.count(diagnostic_bufnr, { severity = {vim.diagnostic.severity.WARN} }), vim.diagnostic.count(diagnostic_bufnr, { severity = {vim.diagnostic.severity.ERROR} }), vim.diagnostic.count(diagnostic_bufnr, { severity = { vim.diagnostic.severity.INFO, vim.diagnostic.severity.WARN, } }), } ]] ) end) it('allows filtering by line', function() eq( exec_lua [[return { [vim.diagnostic.severity.ERROR] = 1 }]], exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 3), make_info("Ignored information", 1, 1, 2, 3), make_error("Error On Other Line", 2, 1, 1, 5), }) return vim.diagnostic.count(diagnostic_bufnr, {lnum = 2}) ]] ) end) end) describe('config()', function() it('works with global, namespace, and ephemeral options', function() eq( 1, exec_lua [[ vim.diagnostic.config({ virtual_text = false, }) vim.diagnostic.config({ virtual_text = true, underline = false, }, diagnostic_ns) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Some Error', 4, 4, 4, 4), }) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]] ) eq( 1, exec_lua [[ vim.diagnostic.config({ virtual_text = false, }) vim.diagnostic.config({ virtual_text = false, underline = false, }, diagnostic_ns) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Some Error', 4, 4, 4, 4), }, {virtual_text = true}) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]] ) eq( 0, exec_lua [[ vim.diagnostic.config({ virtual_text = false, }) vim.diagnostic.config({ virtual_text = {severity=vim.diagnostic.severity.ERROR}, underline = false, }, diagnostic_ns) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_warning('Some Warning', 4, 4, 4, 4), }, {virtual_text = true}) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]] ) eq( 1, exec_lua [[ vim.diagnostic.config({ virtual_text = false, }) vim.diagnostic.config({ virtual_text = {severity=vim.diagnostic.severity.ERROR}, underline = false, }, diagnostic_ns) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_warning('Some Warning', 4, 4, 4, 4), }, { virtual_text = {} -- An empty table uses default values }) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]] ) end) it('can use functions for config values', function() exec_lua [[ vim.diagnostic.config({ virtual_text = function() return true end, }, diagnostic_ns) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Delayed Diagnostic', 4, 4, 4, 4), }) ]] eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) -- Now, don't enable virtual text. -- We should have one less extmark displayed. exec_lua [[ vim.diagnostic.config({ virtual_text = function() return false end, }, diagnostic_ns) ]] eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(1, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) end) it('allows filtering by severity', function() local get_extmark_count_with_severity = function(min_severity) return exec_lua( [[ vim.diagnostic.config({ underline = false, virtual_text = { severity = {min=...}, }, }) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_warning('Delayed Diagnostic', 4, 4, 4, 4), }) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]], min_severity ) end -- No messages with Error or higher eq(0, get_extmark_count_with_severity('ERROR')) -- But now we don't filter it eq(1, get_extmark_count_with_severity('WARN')) eq(1, get_extmark_count_with_severity('HINT')) end) it('allows sorting by severity', function() exec_lua [[ vim.diagnostic.config({ underline = false, signs = true, virtual_text = true, }) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_warning('Warning', 4, 4, 4, 4), make_error('Error', 4, 4, 4, 4), make_info('Info', 4, 4, 4, 4), }) function get_virt_text_and_signs(severity_sort) vim.diagnostic.config({ severity_sort = severity_sort, }) local virt_text = get_virt_text_extmarks(diagnostic_ns)[1][4].virt_text local virt_texts = {} for i = 2, #virt_text - 1 do table.insert(virt_texts, (string.gsub(virt_text[i][2], "DiagnosticVirtualText", ""))) end local ns = vim.diagnostic.get_namespace(diagnostic_ns) local sign_ns = ns.user_data.sign_ns local signs = {} local all_signs = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, sign_ns, 0, -1, {type = 'sign', details = true}) table.sort(all_signs, function(a, b) return a[1] > b[1] end) for _, v in ipairs(all_signs) do local s = v[4].sign_hl_group:gsub('DiagnosticSign', "") if not vim.tbl_contains(signs, s) then signs[#signs + 1] = s end end return {virt_texts, signs} end ]] local result = exec_lua [[return get_virt_text_and_signs(false)]] -- Virt texts are defined lowest priority to highest, signs from -- highest to lowest eq({ 'Warn', 'Error', 'Info' }, result[1]) eq({ 'Info', 'Error', 'Warn' }, result[2]) result = exec_lua [[return get_virt_text_and_signs(true)]] eq({ 'Info', 'Warn', 'Error' }, result[1]) eq({ 'Error', 'Warn', 'Info' }, result[2]) result = exec_lua [[return get_virt_text_and_signs({ reverse = true })]] eq({ 'Error', 'Warn', 'Info' }, result[1]) eq({ 'Info', 'Warn', 'Error' }, result[2]) end) it('can show diagnostic sources in virtual text', function() local result = exec_lua [[ local diagnostics = { make_error('Some error', 0, 0, 0, 0, 'source x'), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { underline = false, virtual_text = { prefix = '', source = 'always', } }) local extmarks = get_virt_text_extmarks(diagnostic_ns) local virt_text = extmarks[1][4].virt_text[3][1] return virt_text ]] eq(' source x: Some error', result) result = exec_lua [[ vim.diagnostic.config({ underline = false, virtual_text = { prefix = '', source = 'if_many', } }, diagnostic_ns) local extmarks = get_virt_text_extmarks(diagnostic_ns) local virt_text = extmarks[1][4].virt_text[3][1] return virt_text ]] eq(' Some error', result) result = exec_lua [[ local diagnostics = { make_error('Some error', 0, 0, 0, 0, 'source x'), make_error('Another error', 1, 1, 1, 1, 'source y'), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { underline = false, virtual_text = { prefix = '', source = 'if_many', } }) local extmarks = get_virt_text_extmarks(diagnostic_ns) local virt_text = {extmarks[1][4].virt_text[3][1], extmarks[2][4].virt_text[3][1]} return virt_text ]] eq(' source x: Some error', result[1]) eq(' source y: Another error', result[2]) end) it('supports a format function for diagnostic messages', function() local result = exec_lua [[ vim.diagnostic.config({ underline = false, virtual_text = { prefix = '', format = function(diagnostic) if diagnostic.severity == vim.diagnostic.severity.ERROR then return string.format("🔥 %s", diagnostic.message) end return string.format("👀 %s", diagnostic.message) end, } }) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_warning('Warning', 0, 0, 0, 0), make_error('Error', 1, 0, 1, 0), }) local extmarks = get_virt_text_extmarks(diagnostic_ns) return {extmarks[1][4].virt_text, extmarks[2][4].virt_text} ]] eq(' 👀 Warning', result[1][3][1]) eq(' 🔥 Error', result[2][3][1]) end) it('includes source for formatted diagnostics', function() local result = exec_lua [[ vim.diagnostic.config({ underline = false, virtual_text = { prefix = '', source = 'always', format = function(diagnostic) if diagnostic.severity == vim.diagnostic.severity.ERROR then return string.format("🔥 %s", diagnostic.message) end return string.format("👀 %s", diagnostic.message) end, } }) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_warning('Warning', 0, 0, 0, 0, 'some_linter'), make_error('Error', 1, 0, 1, 0, 'another_linter'), }) local extmarks = get_virt_text_extmarks(diagnostic_ns) return {extmarks[1][4].virt_text, extmarks[2][4].virt_text} ]] eq(' some_linter: 👀 Warning', result[1][3][1]) eq(' another_linter: 🔥 Error', result[2][3][1]) end) it('can add a prefix to virtual text', function() eq( 'E Some error', exec_lua [[ local diagnostics = { make_error('Some error', 0, 0, 0, 0), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { underline = false, virtual_text = { prefix = 'E', suffix = '', } }) local extmarks = get_virt_text_extmarks(diagnostic_ns) local prefix = extmarks[1][4].virt_text[2][1] local message = extmarks[1][4].virt_text[3][1] return prefix .. message ]] ) eq( '[(1/1) err-code] Some error', exec_lua [[ local diagnostics = { make_error('Some error', 0, 0, 0, 0, nil, 'err-code'), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { underline = false, virtual_text = { prefix = function(diag, i, total) return string.format('[(%d/%d) %s]', i, total, diag.code) end, suffix = '', } }) local extmarks = get_virt_text_extmarks(diagnostic_ns) local prefix = extmarks[1][4].virt_text[2][1] local message = extmarks[1][4].virt_text[3][1] return prefix .. message ]] ) end) it('can add a suffix to virtual text', function() eq( ' Some error ✘', exec_lua [[ local diagnostics = { make_error('Some error', 0, 0, 0, 0), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { underline = false, virtual_text = { prefix = '', suffix = ' ✘', } }) local extmarks = get_virt_text_extmarks(diagnostic_ns) local virt_text = extmarks[1][4].virt_text[3][1] return virt_text ]] ) eq( ' Some error [err-code]', exec_lua [[ local diagnostics = { make_error('Some error', 0, 0, 0, 0, nil, 'err-code'), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { underline = false, virtual_text = { prefix = '', suffix = function(diag) return string.format(' [%s]', diag.code) end, } }) local extmarks = get_virt_text_extmarks(diagnostic_ns) local virt_text = extmarks[1][4].virt_text[3][1] return virt_text ]] ) end) end) describe('set()', function() it('validates its arguments', function() matches( 'expected a list of diagnostics', pcall_err(exec_lua, [[vim.diagnostic.set(1, 0, {lnum = 1, col = 2})]]) ) end) it('can perform updates after insert_leave', function() exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]] api.nvim_input('o') eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) -- Save the diagnostics exec_lua [[ vim.diagnostic.config({ update_in_insert = false, }) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Delayed Diagnostic', 4, 4, 4, 4), }) ]] -- No diagnostics displayed yet. eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(0, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) api.nvim_input('') eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) end) it('does not perform updates when not needed', function() exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]] api.nvim_input('o') eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) -- Save the diagnostics exec_lua [[ vim.diagnostic.config({ update_in_insert = false, virtual_text = true, }) DisplayCount = 0 local set_virtual_text = vim.diagnostic.handlers.virtual_text.show vim.diagnostic.handlers.virtual_text.show = function(...) DisplayCount = DisplayCount + 1 return set_virtual_text(...) end vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Delayed Diagnostic', 4, 4, 4, 4), }) ]] -- No diagnostics displayed yet. eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(0, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) eq(0, exec_lua [[return DisplayCount]]) api.nvim_input('') eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) eq(1, exec_lua [[return DisplayCount]]) -- Go in and out of insert mode one more time. api.nvim_input('o') eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) api.nvim_input('') eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) -- Should not have set the virtual text again. eq(1, exec_lua [[return DisplayCount]]) end) it('never sets virtual text, in combination with insert leave', function() exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]] api.nvim_input('o') eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) -- Save the diagnostics exec_lua [[ vim.diagnostic.config({ update_in_insert = false, virtual_text = false, }) DisplayCount = 0 local set_virtual_text = vim.diagnostic.handlers.virtual_text.show vim.diagnostic.handlers.virtual_text.show = function(...) DisplayCount = DisplayCount + 1 return set_virtual_text(...) end vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Delayed Diagnostic', 4, 4, 4, 4), }) ]] -- No diagnostics displayed yet. eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(0, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) eq(0, exec_lua [[return DisplayCount]]) api.nvim_input('') eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(1, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) eq(0, exec_lua [[return DisplayCount]]) -- Go in and out of insert mode one more time. api.nvim_input('o') eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) api.nvim_input('') eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) -- Should not have set the virtual text still. eq(0, exec_lua [[return DisplayCount]]) end) it('can perform updates while in insert mode, if desired', function() exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]] api.nvim_input('o') eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) -- Save the diagnostics exec_lua [[ vim.diagnostic.config({ update_in_insert = true, }) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Delayed Diagnostic', 4, 4, 4, 4), }) ]] -- Diagnostics are displayed, because the user wanted them that way! eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) api.nvim_input('') eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) eq( 1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]] ) eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) end) it('can set diagnostics without displaying them', function() eq( 0, exec_lua [[ vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic From Server 1:1', 1, 1, 1, 1), }) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]] ) eq( 2, exec_lua [[ vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]] ) end) it('can set display options', function() eq( 0, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic From Server 1:1', 1, 1, 1, 1), }, { virtual_text = false, underline = false }) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]] ) eq( 1, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic From Server 1:1', 1, 1, 1, 1), }, { virtual_text = true, underline = false }) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]] ) end) it('sets and clears signs #26193 #26555', function() do local result = exec_lua [[ vim.diagnostic.config({ signs = true, }) local diagnostics = { make_error('Error', 1, 1, 1, 2), make_warning('Warning', 3, 3, 3, 3), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local ns = vim.diagnostic.get_namespace(diagnostic_ns) local sign_ns = ns.user_data.sign_ns local signs = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, sign_ns, 0, -1, {type ='sign', details = true}) local result = {} for _, s in ipairs(signs) do result[#result + 1] = { lnum = s[2] + 1, name = s[4].sign_hl_group } end return result ]] eq({ 2, 'DiagnosticSignError' }, { result[1].lnum, result[1].name }) eq({ 4, 'DiagnosticSignWarn' }, { result[2].lnum, result[2].name }) end do local result = exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {}) local ns = vim.diagnostic.get_namespace(diagnostic_ns) local sign_ns = ns.user_data.sign_ns return vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, sign_ns, 0, -1, {type ='sign', details = true}) ]] eq({}, result) end end) it('respects legacy signs placed with :sign define or sign_define #26618', function() -- Legacy signs for diagnostics were deprecated in 0.10 and will be removed in 0.12 eq(0, t.fn.has('nvim-0.12')) t.command('sign define DiagnosticSignError text= texthl= linehl=ErrorMsg numhl=ErrorMsg') t.command('sign define DiagnosticSignWarn text= texthl= linehl=WarningMsg numhl=WarningMsg') t.command('sign define DiagnosticSignInfo text= texthl= linehl=Underlined numhl=Underlined') t.command('sign define DiagnosticSignHint text= texthl= linehl=Underlined numhl=Underlined') local result = exec_lua [[ vim.diagnostic.config({ signs = true, }) local diagnostics = { make_error('Error', 1, 1, 1, 2), make_warning('Warning', 3, 3, 3, 3), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local ns = vim.diagnostic.get_namespace(diagnostic_ns) local sign_ns = ns.user_data.sign_ns local signs = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, sign_ns, 0, -1, {type ='sign', details = true}) local result = {} for _, s in ipairs(signs) do result[#result + 1] = { lnum = s[2] + 1, name = s[4].sign_hl_group, text = s[4].sign_text or '', numhl = s[4].number_hl_group, linehl = s[4].line_hl_group, } end return result ]] eq({ lnum = 2, name = 'DiagnosticSignError', text = '', numhl = 'ErrorMsg', linehl = 'ErrorMsg', }, result[1]) eq({ lnum = 4, name = 'DiagnosticSignWarn', text = '', numhl = 'WarningMsg', linehl = 'WarningMsg', }, result[2]) end) end) describe('open_float()', function() it('can display a header', function() eq( { 'Diagnostics:', '1. Syntax error' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float() local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { "We're no strangers to love...", '1. Syntax error' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({header = "We're no strangers to love..."}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { 'You know the rules', '1. Syntax error' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({header = {'You know the rules', 'Search'}}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) end) it('can show diagnostics from the whole buffer', function() eq( { '1. Syntax error', '2. Some warning' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), make_warning("Some warning", 1, 1, 1, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope="buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) end) it('can show diagnostics from a single line', function() -- Using cursor position eq( { '1. Some warning' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), make_warning("Some warning", 1, 1, 1, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {2, 1}) local float_bufnr, winnr = vim.diagnostic.open_float({header=false}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) -- With specified position eq( { '1. Some warning' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), make_warning("Some warning", 1, 1, 1, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) local float_bufnr, winnr = vim.diagnostic.open_float({header=false, pos=1}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) end) it('can show diagnostics from a specific position', function() -- Using cursor position eq( { 'Syntax error' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 1, 1, 1, 2), make_warning("Some warning", 1, 3, 1, 4), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {2, 2}) local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) -- With specified position eq( { 'Some warning' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 1, 1, 1, 2), make_warning("Some warning", 1, 3, 1, 4), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor", pos={1,3}}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) -- With column position past the end of the line. #16062 eq( { 'Syntax error' }, exec_lua [[ local first_line_len = #vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, 1, true)[1] local diagnostics = { make_error("Syntax error", 0, first_line_len + 1, 1, 0), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor", pos={0,first_line_len}}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) end) it( 'creates floating window and returns float bufnr and winnr if current line contains diagnostics', function() -- Two lines: -- Diagnostic: -- 1. eq( 2, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines ]] ) end ) it('only reports diagnostics from the current buffer when bufnr is omitted #15710', function() eq( 2, exec_lua [[ local other_bufnr = vim.api.nvim_create_buf(true, false) local buf_1_diagnostics = { make_error("Syntax error", 0, 1, 0, 3), } local buf_2_diagnostics = { make_warning("Some warning", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, buf_1_diagnostics) vim.diagnostic.set(other_ns, other_bufnr, buf_2_diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float() local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines ]] ) end) it('allows filtering by namespace', function() eq( 2, exec_lua [[ local ns_1_diagnostics = { make_error("Syntax error", 0, 1, 0, 3), } local ns_2_diagnostics = { make_warning("Some warning", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diagnostics) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {namespace = diagnostic_ns}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines ]] ) end) it( 'creates floating window and returns float bufnr and winnr without header, if requested', function() -- One line (since no header): -- 1. eq( 1, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {header = false}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines ]] ) end ) it('clamps diagnostic line numbers within the valid range', function() eq( 1, exec_lua [[ local diagnostics = { make_error("Syntax error", 6, 0, 6, 0), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {header = false, pos = 5}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines ]] ) end) it('can show diagnostic source', function() exec_lua [[vim.api.nvim_win_set_buf(0, diagnostic_bufnr)]] eq( { '1. Syntax error' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3, "source x"), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false, source = "if_many", }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { '1. source x: Syntax error' }, exec_lua [[ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false, source = "always", }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { '1. source x: Syntax error', '2. source y: Another error' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3, "source x"), make_error("Another error", 0, 1, 0, 3, "source y"), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false, source = "if_many", }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) end) it('respects severity_sort', function() exec_lua [[vim.api.nvim_win_set_buf(0, diagnostic_bufnr)]] eq( { '1. Syntax error', '2. Info', '3. Error', '4. Warning' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), make_info('Info', 0, 3, 0, 4), make_error('Error', 0, 2, 0, 2), make_warning('Warning', 0, 0, 0, 1), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.diagnostic.config({severity_sort = false}) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { '1. Syntax error', '2. Error', '3. Warning', '4. Info' }, exec_lua [[ vim.diagnostic.config({severity_sort = true}) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { '1. Info', '2. Warning', '3. Error', '4. Syntax error' }, exec_lua [[ vim.diagnostic.config({severity_sort = { reverse = true } }) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) end) it('can filter by severity', function() local count_diagnostics_with_severity = function(min_severity, max_severity) return exec_lua( [[ local min_severity, max_severity = ... vim.diagnostic.config({ float = { severity = {min=min_severity, max=max_severity}, }, }) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Syntax error", 0, 1, 0, 3), make_info('Info', 0, 3, 0, 4), make_error('Error', 0, 2, 0, 2), make_warning('Warning', 0, 0, 0, 1), }) local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { header = false }) if not float_bufnr then return 0 end local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines ]], min_severity, max_severity ) end eq(2, count_diagnostics_with_severity('ERROR')) eq(3, count_diagnostics_with_severity('WARN')) eq(1, count_diagnostics_with_severity('WARN', 'WARN')) eq(4, count_diagnostics_with_severity('HINT')) eq(0, count_diagnostics_with_severity('HINT', 'HINT')) end) it('can add a prefix to diagnostics', function() -- Default is to add a number eq( { '1. Syntax error', '2. Some warning' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), make_warning("Some warning", 1, 1, 1, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { 'Syntax error', 'Some warning' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), make_warning("Some warning", 1, 1, 1, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer", prefix = ""}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { '1. Syntax error', '2. Some warning' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), make_warning("Some warning", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, prefix = function(_, i, total) -- Only show a number if there is more than one diagnostic if total > 1 then return string.format("%d. ", i) end return "" end, }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { 'Syntax error' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, prefix = function(_, i, total) -- Only show a number if there is more than one diagnostic if total > 1 then return string.format("%d. ", i) end return "" end, }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( '.../diagnostic.lua:0: prefix: expected string|table|function, got number', pcall_err(exec_lua, [[ vim.diagnostic.open_float({ prefix = 42 }) ]]) ) end) it('can add a suffix to diagnostics', function() -- Default is to render the diagnostic error code eq( { '1. Syntax error [code-x]', '2. Some warning [code-y]' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3, nil, "code-x"), make_warning("Some warning", 1, 1, 1, 3, nil, "code-y"), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( { '1. Syntax error', '2. Some warning' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3, nil, "code-x"), make_warning("Some warning", 1, 1, 1, 3, nil, "code-y"), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer", suffix = ""}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) -- Suffix is rendered on the last line of a multiline diagnostic eq( { '1. Syntax error', ' More context [code-x]' }, exec_lua [[ local diagnostics = { make_error("Syntax error\nMore context", 0, 1, 0, 3, nil, "code-x"), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) eq( '.../diagnostic.lua:0: suffix: expected string|table|function, got number', pcall_err(exec_lua, [[ vim.diagnostic.open_float({ suffix = 42 }) ]]) ) end) it('works with the old signature', function() eq( { '1. Syntax error' }, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) local float_bufnr, winnr = vim.diagnostic.open_float(0, { header = false }) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines ]] ) end) end) describe('setloclist()', function() it('sets diagnostics in lnum order', function() local loc_list = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Farther Diagnostic', 4, 4, 4, 4), make_error('Lower Diagnostic', 1, 1, 1, 1), }) vim.diagnostic.setloclist() return vim.fn.getloclist(0) ]] assert(loc_list[1].lnum < loc_list[2].lnum) end) it('sets diagnostics in lnum order, regardless of namespace', function() local loc_list = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Lower Diagnostic', 1, 1, 1, 1), }) vim.diagnostic.set(other_ns, diagnostic_bufnr, { make_warning('Farther Diagnostic', 4, 4, 4, 4), }) vim.diagnostic.setloclist() return vim.fn.getloclist(0) ]] assert(loc_list[1].lnum < loc_list[2].lnum) end) end) describe('match()', function() it('matches a string', function() local msg = 'ERROR: george.txt:19:84:Two plus two equals five' local diagnostic = { severity = exec_lua [[return vim.diagnostic.severity.ERROR]], lnum = 18, col = 83, end_lnum = 18, end_col = 83, message = 'Two plus two equals five', } eq( diagnostic, exec_lua( [[ return vim.diagnostic.match(..., "^(%w+): [^:]+:(%d+):(%d+):(.+)$", {"severity", "lnum", "col", "message"}) ]], msg ) ) end) it('returns nil if the pattern fails to match', function() eq( NIL, exec_lua [[ local msg = "The answer to life, the universe, and everything is" return vim.diagnostic.match(msg, "This definitely will not match", {}) ]] ) end) it('respects default values', function() local msg = 'anna.txt:1:Happy families are all alike' local diagnostic = { severity = exec_lua [[return vim.diagnostic.severity.INFO]], lnum = 0, col = 0, end_lnum = 0, end_col = 0, message = 'Happy families are all alike', } eq( diagnostic, exec_lua( [[ return vim.diagnostic.match(..., "^[^:]+:(%d+):(.+)$", {"lnum", "message"}, nil, {severity = vim.diagnostic.severity.INFO}) ]], msg ) ) end) it('accepts a severity map', function() local msg = '46:FATAL:Et tu, Brute?' local diagnostic = { severity = exec_lua [[return vim.diagnostic.severity.ERROR]], lnum = 45, col = 0, end_lnum = 45, end_col = 0, message = 'Et tu, Brute?', } eq( diagnostic, exec_lua( [[ return vim.diagnostic.match(..., "^(%d+):(%w+):(.+)$", {"lnum", "severity", "message"}, {FATAL = vim.diagnostic.severity.ERROR}) ]], msg ) ) end) end) describe('toqflist() and fromqflist()', function() it('works', function() local result = exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Error 1', 0, 1, 0, 1), make_error('Error 2', 1, 1, 1, 1), make_warning('Warning', 2, 2, 2, 2), }) local diagnostics = vim.diagnostic.get(diagnostic_bufnr) vim.fn.setqflist(vim.diagnostic.toqflist(diagnostics)) local list = vim.fn.getqflist() local new_diagnostics = vim.diagnostic.fromqflist(list) -- Remove namespace since it isn't present in the return value of -- fromlist() for _, v in ipairs(diagnostics) do v.namespace = nil end return {diagnostics, new_diagnostics} ]] eq(result[1], result[2]) end) end) describe('handlers', function() it('checks that a new handler is a table', function() matches( [[.*handler: expected table, got string.*]], pcall_err(exec_lua, [[ vim.diagnostic.handlers.foo = "bar" ]]) ) matches( [[.*handler: expected table, got function.*]], pcall_err(exec_lua, [[ vim.diagnostic.handlers.foo = function() end ]]) ) end) it('can add new handlers', function() eq( true, exec_lua [[ local handler_called = false vim.diagnostic.handlers.test = { show = function(namespace, bufnr, diagnostics, opts) assert(namespace == diagnostic_ns) assert(bufnr == diagnostic_bufnr) assert(#diagnostics == 1) assert(opts.test.some_opt == 42) handler_called = true end, } vim.diagnostic.config({test = {some_opt = 42}}) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_warning("Warning", 0, 0, 0, 0), }) return handler_called ]] ) end) it('can disable handlers by setting the corresponding option to false', function() eq( false, exec_lua [[ local handler_called = false vim.diagnostic.handlers.test = { show = function(namespace, bufnr, diagnostics, opts) handler_called = true end, } vim.diagnostic.config({test = false}) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_warning("Warning", 0, 0, 0, 0), }) return handler_called ]] ) end) it("always calls a handler's hide function if defined", function() eq( { false, true }, exec_lua [[ local hide_called = false local show_called = false vim.diagnostic.handlers.test = { show = function(namespace, bufnr, diagnostics, opts) show_called = true end, hide = function(namespace, bufnr) assert(namespace == diagnostic_ns) assert(bufnr == diagnostic_bufnr) hide_called = true end, } vim.diagnostic.config({test = false}) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_warning("Warning", 0, 0, 0, 0), }) vim.diagnostic.hide(diagnostic_ns, diagnostic_bufnr) return {show_called, hide_called} ]] ) end) it('triggers the autocommand when diagnostics are set', function() eq( { true, true }, exec_lua [[ -- Set a different buffer as current to test that is being set properly in -- DiagnosticChanged callbacks local tmp = vim.api.nvim_create_buf(false, true) vim.api.nvim_set_current_buf(tmp) local triggered = {} vim.api.nvim_create_autocmd('DiagnosticChanged', { callback = function(args) triggered = {args.buf, #args.data.diagnostics} end, }) vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test") vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic', 0, 0, 0, 0) }) return { triggered[1] == diagnostic_bufnr, triggered[2] == 1, } ]] ) end) it('triggers the autocommand when diagnostics are cleared', function() eq( true, exec_lua [[ local tmp = vim.api.nvim_create_buf(false, true) vim.api.nvim_set_current_buf(tmp) vim.g.diagnostic_autocmd_triggered = 0 vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = +expand("")') vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test") vim.diagnostic.reset(diagnostic_ns, diagnostic_bufnr) return vim.g.diagnostic_autocmd_triggered == diagnostic_bufnr ]] ) end) it('checks if diagnostics are disabled in a buffer', function() eq( { true, true, true, true }, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic #1', 1, 1, 1, 1), }) vim.api.nvim_set_current_buf(diagnostic_bufnr) vim.diagnostic.disable() return { vim.diagnostic.is_disabled(), vim.diagnostic.is_disabled(diagnostic_bufnr), vim.diagnostic.is_disabled(diagnostic_bufnr, diagnostic_ns), vim.diagnostic.is_disabled(_, diagnostic_ns), } ]] ) eq( { false, false, false, false }, exec_lua [[ vim.diagnostic.enable() return { vim.diagnostic.is_disabled(), vim.diagnostic.is_disabled(diagnostic_bufnr), vim.diagnostic.is_disabled(diagnostic_bufnr, diagnostic_ns), vim.diagnostic.is_disabled(_, diagnostic_ns), } ]] ) end) end) end)