diff options
-rw-r--r-- | runtime/doc/diagnostic.txt | 4 | ||||
-rw-r--r-- | runtime/lua/vim/diagnostic.lua | 136 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/diagnostic.lua | 11 | ||||
-rw-r--r-- | test/functional/lua/diagnostic_spec.lua | 52 |
4 files changed, 105 insertions, 98 deletions
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index 9ed75e1356..199c04be98 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -239,7 +239,9 @@ config({opts}, {namespace}) *vim.diagnostic.config()* • severity_sort: (default false) Sort diagnostics by severity. This affects the order in which signs and virtual text are - displayed. Options: + displayed. When true, higher severities are + displayed before lower severities (e.g. + ERROR is displayed before WARN). Options: • reverse: (boolean) Reverse sort order {namespace} number|nil Update the options for the given namespace. When omitted, update the global diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 6547188594..1e7f95a353 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -426,6 +426,70 @@ local function set_list(loclist, opts) end end +---@private +local function next_diagnostic(position, search_forward, bufnr, opts, namespace) + position[1] = position[1] - 1 + bufnr = bufnr or vim.api.nvim_get_current_buf() + local wrap = vim.F.if_nil(opts.wrap, true) + local line_count = vim.api.nvim_buf_line_count(bufnr) + opts.namespace = namespace + for i = 0, line_count do + local offset = i * (search_forward and 1 or -1) + local lnum = position[1] + offset + if lnum < 0 or lnum >= line_count then + if not wrap then + return + end + lnum = (lnum + line_count) % line_count + end + opts.lnum = lnum + local line_diagnostics = M.get(bufnr, opts) + if line_diagnostics and not vim.tbl_isempty(line_diagnostics) then + local sort_diagnostics, is_next + if search_forward then + sort_diagnostics = function(a, b) return a.col < b.col end + is_next = function(diagnostic) return diagnostic.col > position[2] end + else + sort_diagnostics = function(a, b) return a.col > b.col end + is_next = function(diagnostic) return diagnostic.col < position[2] end + end + table.sort(line_diagnostics, sort_diagnostics) + if i == 0 then + for _, v in pairs(line_diagnostics) do + if is_next(v) then + return v + end + end + else + return line_diagnostics[1] + end + end + end +end + +---@private +local function diagnostic_move_pos(opts, pos) + opts = opts or {} + + local enable_popup = vim.F.if_nil(opts.enable_popup, true) + local win_id = opts.win_id or vim.api.nvim_get_current_win() + + if not pos then + vim.api.nvim_echo({{"No more valid diagnostics to move to", "WarningMsg"}}, true, {}) + return + end + + vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]}) + + if enable_popup then + -- This is a bit weird... I'm surprised that we need to wait til the next tick to do this. + vim.schedule(function() + M.show_position_diagnostics(opts.popup_opts, vim.api.nvim_win_get_buf(win_id)) + end) + end +end + + -- }}} -- Public API {{{ @@ -452,7 +516,9 @@ end --- - update_in_insert: (default false) Update diagnostics in Insert mode (if false, --- diagnostics are updated on InsertLeave) --- - severity_sort: (default false) Sort diagnostics by severity. This affects the order in ---- which signs and virtual text are displayed. Options: +--- which signs and virtual text are displayed. When true, higher severities +--- are displayed before lower severities (e.g. ERROR is displayed before WARN). +--- Options: --- * reverse: (boolean) Reverse sort order ---@param namespace number|nil Update the options for the given namespace. When omitted, update the --- global diagnostic options. @@ -594,70 +660,6 @@ function M.get(bufnr, opts) return diagnostics end --- Diagnostic Movements {{{ - -local next_diagnostic = function(position, search_forward, bufnr, opts, namespace) - position[1] = position[1] - 1 - bufnr = bufnr or vim.api.nvim_get_current_buf() - local wrap = vim.F.if_nil(opts.wrap, true) - local line_count = vim.api.nvim_buf_line_count(bufnr) - opts.namespace = namespace - for i = 0, line_count do - local offset = i * (search_forward and 1 or -1) - local lnum = position[1] + offset - if lnum < 0 or lnum >= line_count then - if not wrap then - return - end - lnum = (lnum + line_count) % line_count - end - opts.lnum = lnum - local line_diagnostics = M.get(bufnr, opts) - if line_diagnostics and not vim.tbl_isempty(line_diagnostics) then - local sort_diagnostics, is_next - if search_forward then - sort_diagnostics = function(a, b) return a.col < b.col end - is_next = function(diagnostic) return diagnostic.col > position[2] end - else - sort_diagnostics = function(a, b) return a.col > b.col end - is_next = function(diagnostic) return diagnostic.col < position[2] end - end - table.sort(line_diagnostics, sort_diagnostics) - if i == 0 then - for _, v in pairs(line_diagnostics) do - if is_next(v) then - return v - end - end - else - return line_diagnostics[1] - end - end - end -end - ----@private -local function diagnostic_move_pos(opts, pos) - opts = opts or {} - - local enable_popup = vim.F.if_nil(opts.enable_popup, true) - local win_id = opts.win_id or vim.api.nvim_get_current_win() - - if not pos then - vim.api.nvim_echo({{"No more valid diagnostics to move to", "WarningMsg"}}, true, {}) - return - end - - vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]}) - - if enable_popup then - -- This is a bit weird... I'm surprised that we need to wait til the next tick to do this. - vim.schedule(function() - M.show_position_diagnostics(opts.popup_opts, vim.api.nvim_win_get_buf(win_id)) - end) - end -end - --- Get the previous diagnostic closest to the cursor position. --- ---@param opts table See |vim.diagnostic.goto_next()| @@ -998,9 +1000,9 @@ function M.show(namespace, bufnr, diagnostics, opts) if vim.F.if_nil(opts.severity_sort, false) then if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then - table.sort(diagnostics, function(a, b) return a.severity > b.severity end) - else table.sort(diagnostics, function(a, b) return a.severity < b.severity end) + else + table.sort(diagnostics, function(a, b) return a.severity > b.severity end) end end diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 41c8bd36ec..148836a93a 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -1,5 +1,3 @@ -local log = require('vim.lsp.log') - ---@brief lsp-diagnostic --- ---@class Diagnostic @@ -202,11 +200,9 @@ function M.on_publish_diagnostics(_, result, ctx, config) end end end - - vim.diagnostic.config(config, namespace) end - vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) + vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), config) -- Keep old autocmd for back compat. This should eventually be removed. vim.api.nvim_command("doautocmd <nomodeline> User LspDiagnosticsChanged") @@ -468,10 +464,7 @@ function M.set_signs(diagnostics, bufnr, client_id, _, opts) opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} end - local ok = vim.diagnostic._set_signs(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) - if not ok then - log.debug("Failed to place signs:", diagnostics) - end + vim.diagnostic._set_signs(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts) end --- Set underline for given diagnostics diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index 3c8d6e8f2c..2971d3bc51 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -540,11 +540,11 @@ describe('vim.diagnostic', function() end) it('allows sorting by severity', function() - local result = exec_lua([[ + exec_lua [[ vim.diagnostic.config({ - underline = true, - virtual_text = false, - severity_sort = false, + underline = false, + signs = true, + virtual_text = true, }) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { @@ -553,31 +553,41 @@ describe('vim.diagnostic', function() make_info('Info', 4, 4, 4, 4), }) - local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + function get_virt_text_and_signs(severity_sort) + vim.diagnostic.config({ + severity_sort = severity_sort, + }) - local warn_highlight = extmarks[1][4].hl_group + local virt_text = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true})[1][4].virt_text - vim.diagnostic.config({ - severity_sort = true, - }) + local virt_texts = {} + for i = 2, #virt_text do + table.insert(virt_texts, (string.gsub(virt_text[i][2], "DiagnosticVirtualText", ""))) + end - extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + local signs = {} + for _, v in ipairs(vim.fn.sign_getplaced(diagnostic_bufnr, {group = "*"})[1].signs) do + table.insert(signs, (string.gsub(v.name, "DiagnosticSign", ""))) + end - local err_highlight = extmarks[1][4].hl_group + return {virt_texts, signs} + end + ]] - vim.diagnostic.config({ - severity_sort = { reverse = true }, - }) + local result = exec_lua [[return get_virt_text_and_signs(false)]] - extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + -- 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]) - local info_highlight = extmarks[1][4].hl_group + result = exec_lua [[return get_virt_text_and_signs(true)]] + eq({'Info', 'Warn', 'Error'}, result[1]) + eq({'Error', 'Warn', 'Info'}, result[2]) - return { warn_highlight, err_highlight, info_highlight } - ]]) - eq('DiagnosticUnderlineWarn', result[1]) - eq('DiagnosticUnderlineError', result[2]) - eq('DiagnosticUnderlineInfo', result[3]) + result = exec_lua [[return get_virt_text_and_signs({ reverse = true })]] + eq({'Error', 'Warn', 'Info'}, result[1]) + eq({'Info', 'Warn', 'Error'}, result[2]) end) end) |