Skip to content

Commit 3f3b64c

Browse files
author
Jose Alvarez
committed
refactor(config): consolidate validation
1 parent d81fb2b commit 3f3b64c

File tree

2 files changed

+99
-51
lines changed

2 files changed

+99
-51
lines changed

lua/null-ls/config.lua

+53-40
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,45 @@ local defaults = {
66
debounce = 250,
77
keep_alive_interval = 60000, -- 60 seconds,
88
save_after_format = true,
9-
on_attach = nil,
10-
generators = {},
11-
filetypes = {},
12-
names = {}
9+
_generators = {},
10+
_filetypes = {},
11+
_names = {},
12+
_setup = false
1313
}
1414

15+
local type_overrides = {
16+
on_attach = {"function", "nil"},
17+
sources = {"table", "nil"}
18+
}
19+
20+
local wanted_type = function(k)
21+
if vim.startswith(k, "_") then return "nil", true end
22+
23+
local override = type_overrides[k]
24+
if type(override) == "string" then return override, true end
25+
if type(override) == "table" then
26+
return function(a) return vim.tbl_contains(override, type(a)) end,
27+
table.concat(override, ", ")
28+
end
29+
30+
return type(defaults[k]), true
31+
end
32+
1533
local config = vim.deepcopy(defaults)
1634

1735
-- allow plugins to call register multiple times without duplicating sources
1836
local is_registered = function(name, insert)
1937
if not name then return false end
38+
if vim.tbl_contains(config._names, name) then return true end
2039

21-
if vim.tbl_contains(config.names, name) then return true end
22-
23-
if insert then table.insert(config.names, name) end
24-
40+
if insert then table.insert(config._names, name) end
2541
return false
2642
end
2743

2844
local register_filetypes = function(filetypes)
2945
for _, filetype in pairs(filetypes) do
30-
if not vim.tbl_contains(config.filetypes, filetype) then
31-
table.insert(config.filetypes, filetype)
46+
if not vim.tbl_contains(config._filetypes, filetype) then
47+
table.insert(config._filetypes, filetype)
3248
end
3349
end
3450
end
@@ -49,11 +65,11 @@ local register_source = function(source, filetypes)
4965
local fn, async = generator.fn, generator.async
5066
validate({fn = {fn, "function"}, async = {async, "boolean", true}})
5167

52-
if not config.generators[method] then config.generators[method] = {} end
68+
if not config._generators[method] then config._generators[method] = {} end
5369
register_filetypes(filetypes)
5470

5571
generator.filetypes = filetypes
56-
table.insert(config.generators[method], generator)
72+
table.insert(config._generators[method], generator)
5773

5874
-- plugins that register sources after BufEnter may need to call try_attach() again,
5975
-- after filetypes have been registered
@@ -79,51 +95,48 @@ local register = function(to_register)
7995
if is_registered(name, true) then return end
8096

8197
validate({sources = {sources, "table"}, name = {name, "string", true}})
82-
8398
for _, source in pairs(sources) do register_source(source, filetypes) end
8499
end
85100

86101
local M = {}
87102

88103
M.get = function() return config end
89-
90104
M.reset = function() config = vim.deepcopy(defaults) end
91105

92106
M.is_registered = is_registered
93-
94107
M.register = register
95-
96-
M.reset_sources = function() config.generators = {} end
108+
M.reset_sources = function() config._generators = {} end
97109

98110
M.generators = function(method)
99-
if method then return config.generators[method] end
100-
return config.generators
111+
return method and config._generators[method] or config._generators
101112
end
102113

103-
M.setup = function(user_config)
104-
local on_attach, debounce, user_sources, save_after_format,
105-
keep_alive_interval = user_config.on_attach, user_config.debounce,
106-
user_config.sources,
107-
user_config.save_after_format,
108-
user_config.keep_alive_interval
114+
local validate_config = function(user_config)
115+
local to_validate, validated = {}, {}
109116

110-
validate({
111-
on_attach = {on_attach, "function", true},
112-
debounce = {debounce, "number", true},
113-
sources = {user_sources, "table", true},
114-
save_after_format = {save_after_format, "boolean", true},
115-
keep_alive_interval = {keep_alive_interval, "number", true}
116-
})
117+
local get_wanted = function(config_table)
118+
for k in pairs(config_table) do
119+
local wanted, optional = wanted_type(k)
120+
to_validate[k] = {user_config[k], wanted, optional}
117121

118-
if on_attach then config.on_attach = on_attach end
119-
if debounce then config.debounce = debounce end
120-
if save_after_format ~= nil then
121-
config.save_after_format = save_after_format
122-
end
123-
if keep_alive_interval then
124-
config.keep_alive_interval = keep_alive_interval
122+
validated[k] = user_config[k]
123+
end
125124
end
126-
if user_sources then register(user_sources) end
125+
get_wanted(config)
126+
get_wanted(type_overrides)
127+
128+
validate(to_validate)
129+
return validated
130+
end
131+
132+
M.setup = function(user_config)
133+
if config._setup then return end
134+
135+
local validated = validate_config(user_config)
136+
config = vim.tbl_extend("force", config, validated)
137+
138+
if config.sources then register(config.sources) end
139+
config._setup = true
127140
end
128141

129142
return M

test/spec/config_spec.lua

+46-11
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ describe("config", function()
4242

4343
assert.equals(vim.tbl_count(generators), 1)
4444
assert.equals(vim.tbl_count(generators[mock_source.method]), 1)
45-
assert.equals(vim.tbl_count(c.get().filetypes), 2)
45+
assert.equals(vim.tbl_count(c.get()._filetypes), 2)
4646
end)
4747

4848
it("should register additional generators for same method", function()
@@ -53,7 +53,7 @@ describe("config", function()
5353

5454
assert.equals(vim.tbl_count(generators), 1)
5555
assert.equals(vim.tbl_count(generators[mock_source.method]), 2)
56-
assert.equals(vim.tbl_count(c.get().filetypes), 2)
56+
assert.equals(vim.tbl_count(c.get()._filetypes), 2)
5757
end)
5858

5959
it("should call autocommands trigger method after registration",
@@ -71,7 +71,7 @@ describe("config", function()
7171

7272
assert.equals(vim.tbl_count(generators), 1)
7373
assert.equals(vim.tbl_count(generators[mock_source.method]), 2)
74-
assert.equals(vim.tbl_count(c.get().filetypes), 2)
74+
assert.equals(vim.tbl_count(c.get()._filetypes), 2)
7575
end)
7676

7777
it("should register multiple sources with shared configuration",
@@ -85,7 +85,7 @@ describe("config", function()
8585

8686
assert.equals(vim.tbl_count(generators), 1)
8787
assert.equals(vim.tbl_count(generators[mock_source.method]), 2)
88-
assert.equals(vim.tbl_count(c.get().filetypes), 1)
88+
assert.equals(vim.tbl_count(c.get()._filetypes), 1)
8989
end)
9090

9191
it("should only register sources once when name is specified",
@@ -107,7 +107,7 @@ describe("config", function()
107107
end)
108108

109109
describe("reset_sources", function()
110-
it("should reset sources", function()
110+
it("should reset sources only", function()
111111
c.setup({debounce = 500, sources = {mock_source}})
112112

113113
c.reset_sources()
@@ -135,7 +135,32 @@ describe("config", function()
135135
end)
136136

137137
describe("setup", function()
138-
it("should set on_attach", function()
138+
it("should set simple config value", function()
139+
local debounce = 999
140+
141+
c.setup({debounce = debounce})
142+
143+
assert.equals(c.get().debounce, debounce)
144+
end)
145+
146+
it("should only setup config once", function()
147+
c.setup({debounce = 999})
148+
149+
c.setup({debounce = 1})
150+
151+
assert.equals(c.get().debounce, 999)
152+
end)
153+
154+
it("should throw if simple config type does not match", function()
155+
local debounce = "999"
156+
157+
local ok, err = pcall(c.setup, {debounce = debounce})
158+
159+
assert.equals(ok, false)
160+
assert.matches("expected number", err)
161+
end)
162+
163+
it("should set override config value", function()
139164
local on_attach = stub.new()
140165
local _on_attach = function() on_attach() end
141166

@@ -145,12 +170,22 @@ describe("config", function()
145170
assert.stub(on_attach).was_called()
146171
end)
147172

148-
it("should set debounce", function()
149-
local debounce = 999
173+
it("should throw if override config type does not match", function()
174+
local on_attach = {"my function"}
150175

151-
c.setup({debounce = debounce})
176+
local ok, err = pcall(c.setup, {on_attach = on_attach})
152177

153-
assert.equals(c.get().debounce, debounce)
178+
assert.equals(ok, false)
179+
assert.matches("expected function, nil", err)
180+
end)
181+
182+
it("should throw if config value is private", function()
183+
local _names = {"my-integration"}
184+
185+
local ok, err = pcall(c.setup, {_names = _names})
186+
187+
assert.equals(ok, false)
188+
assert.matches("expected nil", err)
154189
end)
155190

156191
it("should register sources", function()
@@ -160,7 +195,7 @@ describe("config", function()
160195

161196
assert.equals(vim.tbl_count(generators), 1)
162197
assert.equals(vim.tbl_count(generators[mock_source.method]), 1)
163-
assert.equals(vim.tbl_count(c.get().filetypes), 2)
198+
assert.equals(vim.tbl_count(c.get()._filetypes), 2)
164199
end)
165200
end)
166201
end)

0 commit comments

Comments
 (0)