feat(diagnostic): custom status format function #36696

Problem:  Statusline component of diagnostics allows only the default
          format "sign:count".

Solution: Extend vim.diagnostic.Opts.Status to allow a custom signs
          or formatting function that provides the status presentation.
This commit is contained in:
Oleh Volynets
2026-03-13 12:21:45 +01:00
committed by GitHub
parent 7b8deacc3f
commit caf7808591
4 changed files with 133 additions and 28 deletions

View File

@@ -189,8 +189,37 @@ end
--- @class vim.diagnostic.Opts.Status
---
--- A table mapping |diagnostic-severity| to the text to use for each severity section.
--- @field text? table<vim.diagnostic.Severity,string>
--- Either:
--- - a table mapping |diagnostic-severity| to the text to use for each
--- existing severity section.
--- - a function that accepts a mapping of |diagnostic-severity| to the
--- number of diagnostics of the corresponding severity (only those
--- severity levels that have at least 1 diagnostic) and returns
--- a 'statusline' component. In this case highlights must be applied
--- by the user in the `format` function. Example:
--- ```lua
--- local signs = {
--- [vim.diagnostic.severity.ERROR] = "A",
--- -- ...
--- }
--- local hl_map = {
--- [vim.diagnostic.severity.ERROR] = 'DiagnosticSignError',
--- -- ...
--- }
--- vim.diagnostic.config({
--- status = {
--- format = function(counts)
--- local items = {}
--- for level, _ in ipairs(vim.diagnostic.severity) do
--- local count = counts[level] or 0
--- table.insert(items, ("%%#%s#%s %s"):format(hl_map[level], signs[level], count))
--- end
--- return table.concat(items, " ")
--- end
--- }
--- })
--- ```
--- @field format? table<vim.diagnostic.Severity,string>|(fun(counts:table<vim.diagnostic.Severity,integer>): string)
--- @class vim.diagnostic.Opts.Underline
---
@@ -2969,17 +2998,19 @@ local hl_map = {
[M.severity.INFO] = 'DiagnosticSignInfo',
[M.severity.HINT] = 'DiagnosticSignHint',
}
local default_status_signs = {
[M.severity.ERROR] = 'E',
[M.severity.WARN] = 'W',
[M.severity.INFO] = 'I',
[M.severity.HINT] = 'H',
}
--- Returns formatted string with diagnostics for the current buffer.
--- The severities with 0 diagnostics are left out.
--- Example `E:2 W:3 I:4 H:5`
---
--- To customise appearance, set diagnostic text for each severity with
--- ```lua
--- vim.diagnostic.config({
--- status = { text = { [vim.diagnostic.severity.ERROR] = 'e', ... } }
--- })
--- ```
--- To customise appearance, see |vim.diagnostic.Opts.Status|.
---
---@param bufnr? integer Buffer number to get diagnostics from.
--- Defaults to 0 for the current buffer
---
@@ -2987,20 +3018,26 @@ local hl_map = {
function M.status(bufnr)
vim.validate('bufnr', bufnr, 'number', true)
bufnr = bufnr or 0
local config = assert(M.config()).status or {}
vim.validate('config.format', config.format, { 'table', 'function' }, true)
local counts = M.count(bufnr)
local user_signs = vim.tbl_get(M.config() --[[@as vim.diagnostic.Opts]], 'status', 'text') or {}
local signs = vim.tbl_extend('keep', user_signs, { 'E', 'W', 'I', 'H' })
local result_str = vim
.iter(pairs(counts))
:map(function(severity, count)
return ('%%#%s#%s:%s'):format(hl_map[severity], signs[severity], count)
end)
:join(' ')
local format = config.format or default_status_signs
--- @type string
local result_str
if type(format) == 'table' then
local signs = vim.tbl_extend('keep', format, default_status_signs)
result_str = vim
.iter(pairs(counts))
:map(function(severity, count)
return ('%%#%s#%s:%s'):format(hl_map[severity], signs[severity], count)
end)
:join(' ')
elseif type(format) == 'function' then
result_str = format(counts)
end
if result_str:len() > 0 then
result_str = result_str .. '%##'
end
return result_str
end