feat: validate all option types (#2414)

* refactor: follow config structure for `ACCEPTED_TYPES`

* Bug fix

* Fix check for default values

* Reduce error notifications verbosity

* Address issues introduced previously

* stylua

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
This commit is contained in:
Azad
2023-09-23 06:56:17 +02:00
committed by GitHub
parent 914a6868cb
commit ea147418e0

View File

@@ -621,14 +621,31 @@ local FIELD_SKIP_VALIDATE = {
open_win_config = true, open_win_config = true,
} }
local FIELD_OVERRIDE_TYPECHECK = { local ACCEPTED_TYPES = {
width = { string = true, ["function"] = true, number = true, ["table"] = true }, on_attach = { "function", "string" },
max = { string = true, ["function"] = true, number = true }, sort = {
min = { string = true, ["function"] = true, number = true }, sorter = { "function", "string" },
on_attach = { ["function"] = true, string = true }, },
sorter = { ["function"] = true, string = true }, view = {
root_folder_label = { ["function"] = true, string = true, boolean = true }, width = {
picker = { ["function"] = true, string = true }, "string",
"function",
"number",
"table",
min = { "string", "function", "number" },
max = { "string", "function", "number" },
},
},
renderer = {
root_folder_label = { "function", "string", "boolean" },
},
actions = {
open_file = {
window_picker = {
picker = { "function", "string" },
},
},
},
} }
local ACCEPTED_STRINGS = { local ACCEPTED_STRINGS = {
@@ -653,43 +670,65 @@ local ACCEPTED_STRINGS = {
local function validate_options(conf) local function validate_options(conf)
local msg local msg
local function validate(user, def, strs, prefix) local function validate(user, def, strs, types, prefix)
-- only compare tables with contents that are not integer indexed -- if user's option is not a table there is nothing to do
if type(user) ~= "table" or type(def) ~= "table" or not next(def) or type(next(def)) == "number" then if type(user) ~= "table" then
return return
end end
-- only compare tables with contents that are not integer indexed
if type(def) ~= "table" or not next(def) or type(next(def)) == "number" then
-- unless the field can be a table (and is not a table in default config)
if vim.tbl_contains(types, "table") then
-- use a dummy default to allow all checks
def = {}
else
return
end
end
for k, v in pairs(user) do for k, v in pairs(user) do
if not FIELD_SKIP_VALIDATE[k] then if not FIELD_SKIP_VALIDATE[k] then
local invalid local invalid
local override_typecheck = FIELD_OVERRIDE_TYPECHECK[k] or {}
if def[k] == nil then if def[k] == nil and types[k] == nil then
-- option does not exist -- option does not exist
invalid = string.format("[NvimTree] unknown option: %s%s", prefix, k) invalid = string.format("Unknown option: %s%s", prefix, k)
elseif type(v) ~= type(def[k]) and not override_typecheck[type(v)] then elseif type(v) ~= type(def[k]) then
-- option is of the wrong type and is not a function local expected
invalid =
string.format("[NvimTree] invalid option: %s%s expected: %s actual: %s", prefix, k, type(def[k]), type(v)) if types[k] and #types[k] > 0 then
if not vim.tbl_contains(types[k], type(v)) then
expected = table.concat(types[k], "|")
end
else
expected = type(def[k])
end
if expected then
-- option is of the wrong type
invalid = string.format("Invalid option: %s%s. Expected %s, got %s", prefix, k, expected, type(v))
end
elseif type(v) == "string" and strs[k] and not vim.tbl_contains(strs[k], v) then elseif type(v) == "string" and strs[k] and not vim.tbl_contains(strs[k], v) then
-- option has type `string` but value is not accepted -- option has type `string` but value is not accepted
invalid = string.format("[NvimTree] invalid value for field %s%s: '%s'", prefix, k, v) invalid = string.format("Invalid value for field %s%s: '%s'", prefix, k, v)
end end
if invalid then if invalid then
if msg then if msg then
msg = string.format("%s | %s", msg, invalid) msg = string.format("%s | %s", msg, invalid)
else else
msg = string.format("%s", invalid) msg = string.format("[NvimTree] %s", invalid)
end end
user[k] = nil user[k] = nil
else else
validate(v, def[k], strs[k] or {}, prefix .. k .. ".") validate(v, def[k], strs[k] or {}, types[k] or {}, prefix .. k .. ".")
end end
end end
end end
end end
validate(conf, DEFAULT_OPTS, ACCEPTED_STRINGS, "") validate(conf, DEFAULT_OPTS, ACCEPTED_STRINGS, ACCEPTED_TYPES, "")
if msg then if msg then
vim.notify_once(msg .. " | see :help nvim-tree-opts for available configuration options", vim.log.levels.WARN) vim.notify_once(msg .. " | see :help nvim-tree-opts for available configuration options", vim.log.levels.WARN)