diff options
-rw-r--r-- | runtime/doc/diagnostic.txt | 31 | ||||
-rw-r--r-- | runtime/lua/vim/diagnostic.lua | 51 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/diagnostic.lua | 20 | ||||
-rw-r--r-- | test/functional/lua/diagnostic_spec.lua | 47 |
4 files changed, 116 insertions, 33 deletions
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index f4975b187f..9ed75e1356 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -216,18 +216,31 @@ config({opts}, {namespace}) *vim.diagnostic.config()* {opts} table Configuration table with the following keys: • underline: (default true) Use underline for - diagnostics + diagnostics. Options: + • severity: Only underline diagnostics + matching the given severity + |diagnostic-severity| + • virtual_text: (default true) Use virtual - text for diagnostics + text for diagnostics. Options: + • severity: Only show virtual text for + diagnostics matching the given severity + |diagnostic-severity| + • signs: (default true) Use signs for - diagnostics + diagnostics. Options: + • severity: Only show signs for diagnostics + matching the given severity + |diagnostic-severity| + • 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 + displayed. Options: + • reverse: (boolean) Reverse sort order {namespace} number|nil Update the options for the given namespace. When omitted, update the global diagnostic options. @@ -254,7 +267,7 @@ get({bufnr}, {opts}) *vim.diagnostic.get()* Get current diagnostics. Parameters: ~ - {bufnr} number|nil Buffer number to get diagnistics from. + {bufnr} number|nil Buffer number to get diagnostics from. Use 0 for current buffer or nil for all buffers. {opts} table|nil A table with the following keys: • namespace: (number) Limit diagnostics to the @@ -320,7 +333,7 @@ get_virt_text_chunks({line_diags}, {opts}) insert before virtual text. Return: ~ - an array of [text, hl_group] arrays. This can be passed + array of ({text}, {hl_group}) tuples. This can be passed directly to the {virt_text} option of |nvim_buf_set_extmark()|. @@ -456,7 +469,7 @@ show_line_diagnostics({opts}, {bufnr}, {lnum}) of cursor. Return: ~ - A ({popup_bufnr}, {win_id}) tuple + tuple ({popup_bufnr}, {win_id}) *vim.diagnostic.show_position_diagnostics()* show_position_diagnostics({opts}, {bufnr}, {position}) @@ -465,7 +478,7 @@ show_position_diagnostics({opts}, {bufnr}, {position}) Parameters: ~ {opts} table|nil Configuration table with the same - keys as |vim.lsp.util.open_floatin_preview()| + keys as |vim.lsp.util.open_floating_preview()| in addition to the following: • namespace: (number) Limit diagnostics to the given namespace @@ -478,6 +491,6 @@ show_position_diagnostics({opts}, {bufnr}, {position}) to the current cursor position. Return: ~ - A ({popup_bufnr}, {win_id}) tuple + tuple ({popup_bufnr}, {win_id}) vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 688f9b5811..6547188594 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -136,13 +136,15 @@ local define_default_signs = (function() end for severity, sign_hl_name in pairs(sign_highlight_map) do - local severity_name = M.severity[severity] - vim.fn.sign_define(sign_hl_name, { - text = (severity_name or 'U'):sub(1, 1), - texthl = sign_hl_name, - linehl = '', - numhl = '', - }) + if vim.tbl_isempty(vim.fn.sign_getdefined(sign_hl_name)) then + local severity_name = M.severity[severity] + vim.fn.sign_define(sign_hl_name, { + text = (severity_name or 'U'):sub(1, 1), + texthl = sign_hl_name, + linehl = '', + numhl = '', + }) + end end signs_defined = true @@ -408,7 +410,11 @@ local function set_list(loclist, opts) local open = vim.F.if_nil(opts.open, true) local title = opts.title or "Diagnostics" local winnr = opts.winnr or 0 - local diagnostics = M.get(loclist and vim.api.nvim_win_get_buf(winnr), opts) + local bufnr + if loclist then + bufnr = vim.api.nvim_win_get_buf(winnr) + end + local diagnostics = M.get(bufnr, opts) local items = diagnostics_to_list_items(diagnostics) if loclist then vim.fn.setloclist(winnr, {}, ' ', { title = title, items = items }) @@ -434,13 +440,20 @@ end --- - `function`: Function with signature (namespace, bufnr) that returns any of the above. --- ---@param opts table Configuration table with the following keys: ---- - underline: (default true) Use underline for diagnostics ---- - virtual_text: (default true) Use virtual text for diagnostics ---- - signs: (default true) Use signs for diagnostics +--- - underline: (default true) Use underline for diagnostics. Options: +--- * severity: Only underline diagnostics matching the given severity +--- |diagnostic-severity| +--- - virtual_text: (default true) Use virtual text for diagnostics. Options: +--- * severity: Only show virtual text for diagnostics matching the given +--- severity |diagnostic-severity| +--- - signs: (default true) Use signs for diagnostics. Options: +--- * severity: Only show signs for diagnostics matching the given severity +--- |diagnostic-severity| --- - 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 +--- which signs and virtual text are displayed. Options: +--- * reverse: (boolean) Reverse sort order ---@param namespace number|nil Update the options for the given namespace. When omitted, update the --- global diagnostic options. function M.config(opts, namespace) @@ -856,7 +869,7 @@ end ---@param opts table|nil Configuration table with the following keys: --- - prefix: (string) Prefix to display before virtual text on line. --- - spacing: (number) Number of spaces to insert before virtual text. ----@return an array of [text, hl_group] arrays. This can be passed directly to +---@return array of ({text}, {hl_group}) tuples. This can be passed directly to --- the {virt_text} option of |nvim_buf_set_extmark()|. function M.get_virt_text_chunks(line_diags, opts) if #line_diags == 0 then @@ -983,6 +996,14 @@ function M.show(namespace, bufnr, diagnostics, opts) end end + 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) + end + end + if opts.underline then M._set_underline(namespace, bufnr, diagnostics, opts.underline) end @@ -1007,7 +1028,7 @@ end --- - show_header: (boolean, default true) Show "Diagnostics:" header ---@param bufnr number|nil Buffer number. Defaults to the current buffer. ---@param position table|nil The (0,0)-indexed position. Defaults to the current cursor position. ----@return A ({popup_bufnr}, {win_id}) tuple +---@return tuple ({popup_bufnr}, {win_id}) function M.show_position_diagnostics(opts, bufnr, position) vim.validate { opts = { opts, 't', true }, @@ -1039,7 +1060,7 @@ end ---@param opts table Configuration table. See |vim.diagnostic.show_position_diagnostics()|. ---@param bufnr number|nil Buffer number. Defaults to the current buffer. ---@param lnum number|nil Line number. Defaults to line number of cursor. ----@return A ({popup_bufnr}, {win_id}) tuple +---@return tuple ({popup_bufnr}, {win_id}) function M.show_line_diagnostics(opts, bufnr, lnum) vim.validate { opts = { opts, 't', true }, diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index eef840bee5..41c8bd36ec 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -52,7 +52,7 @@ end ---@private local function line_byte_from_position(lines, lnum, col, offset_encoding) - if offset_encoding == "utf-8" then + if not lines or offset_encoding == "utf-8" then return col end @@ -73,7 +73,19 @@ local function get_buf_lines(bufnr) local filename = vim.api.nvim_buf_get_name(bufnr) local f = io.open(filename) - local lines = vim.split(f:read("*a"), "\n") + if not f then + return + end + + local content = f:read("*a") + if not content then + -- Some LSP servers report diagnostics at a directory level, in which case + -- io.read() returns nil + f:close() + return + end + + local lines = vim.split(content, "\n") f:close() return lines end @@ -183,10 +195,6 @@ function M.on_publish_diagnostics(_, result, ctx, config) local diagnostics = result.diagnostics if config then - if vim.F.if_nil(config.severity_sort, false) then - table.sort(diagnostics, function(a, b) return a.severity > b.severity end) - end - for _, opt in pairs(config) do if type(opt) == 'table' then if not opt.severity and opt.severity_limit then diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index 8da33173a2..3c8d6e8f2c 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -35,7 +35,7 @@ describe('vim.diagnostic', function() } end - function make_information(msg, x1, y1, x2, y2) + function make_info(msg, x1, y1, x2, y2) return { lnum = x1, col = y1, @@ -456,7 +456,7 @@ describe('vim.diagnostic', function() vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 5), - make_information("Ignored information", 1, 1, 2, 5), + make_info("Ignored information", 1, 1, 2, 5), make_hint("Here's a hint", 1, 1, 2, 5), }) @@ -478,7 +478,7 @@ describe('vim.diagnostic', function() vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 5), - make_information("Ignored information", 1, 1, 2, 5), + make_info("Ignored information", 1, 1, 2, 5), make_error("Error On Other Line", 2, 1, 1, 5), }) @@ -538,6 +538,47 @@ describe('vim.diagnostic', function() eq(1, get_extmark_count_with_severity("WARN")) eq(1, get_extmark_count_with_severity("HINT")) end) + + it('allows sorting by severity', function() + local result = exec_lua([[ + vim.diagnostic.config({ + underline = true, + virtual_text = false, + severity_sort = false, + }) + + 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), + }) + + local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + + local warn_highlight = extmarks[1][4].hl_group + + vim.diagnostic.config({ + severity_sort = true, + }) + + extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + + local err_highlight = extmarks[1][4].hl_group + + vim.diagnostic.config({ + severity_sort = { reverse = true }, + }) + + extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + + local info_highlight = extmarks[1][4].hl_group + + return { warn_highlight, err_highlight, info_highlight } + ]]) + eq('DiagnosticUnderlineWarn', result[1]) + eq('DiagnosticUnderlineError', result[2]) + eq('DiagnosticUnderlineInfo', result[3]) + end) end) describe('set()', function() |