fix(diagnostic): do not override existing config settings #16043

When using `true` as the value of a configuration option, the option is
configured to use default values. For example, if a user configures
virtual text to include the source globally (using
vim.diagnostic.config) and a specific namespace or producer configures
virtual text with `virt_text = true`, the user's global configuration is
overriden.

Instead, interpret a value of `true` to mean "use existing settings if
defined, otherwise use defaults".
This commit is contained in:
Gregory Anders 2021-10-17 08:18:35 -06:00 committed by GitHub
parent 30af69509d
commit d2d30dfabd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 148 additions and 28 deletions

View File

@ -254,12 +254,31 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
Configure diagnostic options globally or for a specific
diagnostic namespace.
Configuration can be specified globally, per-namespace, or
ephemerally (i.e. only for a single call to
|vim.diagnostic.set()| or |vim.diagnostic.show()|). Ephemeral
configuration has highest priority, followed by namespace
configuration, and finally global configuration.
For example, if a user enables virtual text globally with >
vim.diagnostic.config({virt_text = true})
<
and a diagnostic producer sets diagnostics with >
vim.diagnostic.set(ns, 0, diagnostics, {virt_text = false})
<
then virtual text will not be enabled for those diagnostics.
Note:
Each of the configuration options below accepts one of the
following:
• `false` : Disable this feature
• `true` : Enable this feature, use default settings.
• `table` : Enable this feature with overrides.
• `table` : Enable this feature with overrides. Use an
empty table to use default values.
• `function` : Function with signature (namespace, bufnr)
that returns any of the above.
@ -460,8 +479,9 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults})
For example, consider a line of output from a linter: >
WARNING filename:27:3: Variable 'foo' does not exist
<
< This can be parsed into a diagnostic |diagnostic-structure|
This can be parsed into a diagnostic |diagnostic-structure|
with: >
local s = "WARNING filename:27:3: Variable 'foo' does not exist"

View File

@ -93,28 +93,6 @@ local function reformat_diagnostics(format, diagnostics)
return formatted
end
---@private
local function resolve_optional_value(option, namespace, bufnr)
local enabled_val = {}
if not option then
return false
elseif option == true then
return enabled_val
elseif type(option) == 'function' then
local val = option(namespace, bufnr)
if val == true then
return enabled_val
else
return val
end
elseif type(option) == 'table' then
return option
else
error("Unexpected option type: " .. vim.inspect(option))
end
end
local all_namespaces = {}
---@private
@ -139,13 +117,47 @@ local function get_namespace(ns)
return all_namespaces[ns]
end
---@private
local function enabled_value(option, namespace)
local ns = get_namespace(namespace)
if type(ns.opts[option]) == "table" then
return ns.opts[option]
end
if type(global_diagnostic_options[option]) == "table" then
return global_diagnostic_options[option]
end
return {}
end
---@private
local function resolve_optional_value(option, value, namespace, bufnr)
if not value then
return false
elseif value == true then
return enabled_value(option, namespace)
elseif type(value) == 'function' then
local val = value(namespace, bufnr)
if val == true then
return enabled_value(option, namespace)
else
return val
end
elseif type(value) == 'table' then
return value
else
error("Unexpected option type: " .. vim.inspect(value))
end
end
---@private
local function get_resolved_options(opts, namespace, bufnr)
local ns = get_namespace(namespace)
local resolved = vim.tbl_extend('keep', opts or {}, ns.opts, global_diagnostic_options)
for k in pairs(global_diagnostic_options) do
if resolved[k] ~= nil then
resolved[k] = resolve_optional_value(resolved[k], namespace, bufnr)
resolved[k] = resolve_optional_value(k, resolved[k], namespace, bufnr)
end
end
return resolved
@ -542,10 +554,27 @@ end
--- Configure diagnostic options globally or for a specific diagnostic
--- namespace.
---
--- Configuration can be specified globally, per-namespace, or ephemerally
--- (i.e. only for a single call to |vim.diagnostic.set()| or
--- |vim.diagnostic.show()|). Ephemeral configuration has highest priority,
--- followed by namespace configuration, and finally global configuration.
---
--- For example, if a user enables virtual text globally with
--- <pre>
--- vim.diagnostic.config({virt_text = true})
--- </pre>
---
--- and a diagnostic producer sets diagnostics with
--- <pre>
--- vim.diagnostic.set(ns, 0, diagnostics, {virt_text = false})
--- </pre>
---
--- then virtual text will not be enabled for those diagnostics.
---
---@note Each of the configuration options below accepts one of the following:
--- - `false`: Disable this feature
--- - `true`: Enable this feature, use default settings.
--- - `table`: Enable this feature with overrides.
--- - `table`: Enable this feature with overrides. Use an empty table to use default values.
--- - `function`: Function with signature (namespace, bufnr) that returns any of the above.
---
---@param opts table Configuration table with the following keys:
@ -653,8 +682,6 @@ function M.set(namespace, bufnr, diagnostics, opts)
if vim.api.nvim_buf_is_loaded(bufnr) then
M.show(namespace, bufnr, diagnostics, opts)
elseif opts then
M.config(opts, namespace)
end
vim.api.nvim_command("doautocmd <nomodeline> User DiagnosticsChanged")
@ -1277,6 +1304,7 @@ end
--- <pre>
--- WARNING filename:27:3: Variable 'foo' does not exist
--- </pre>
---
--- This can be parsed into a diagnostic |diagnostic-structure|
--- with:
--- <pre>

View File

@ -473,6 +473,78 @@ describe('vim.diagnostic', function()
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({