Skip to content

Commit f626e90

Browse files
chore(refactor): improve configuration validation
## Details Takes a much more schema centric approach to validating that the current configuration is valid according to our type definitions. Easier to add more complex types compared to previous approach which required creating another custom validator implementation. Some error messages may be less specific than before, primarily around errors in union types, but should still generally point users in the right direction. There is a kind of special hack to make everything under `overrides` optional instead of mandatory, but I honestly prefer it to the previous version of passing around random bits of state to make everything work.
1 parent b7dad79 commit f626e90

34 files changed

+669
-663
lines changed

doc/render-markdown.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*render-markdown.txt* For NVIM v0.11.3 Last change: 2025 August 24
1+
*render-markdown.txt* For NVIM v0.11.3 Last change: 2025 August 25
22

33
==============================================================================
44
Table of Contents *render-markdown-table-of-contents*

lua/render-markdown/config/anti_conceal.lua

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,22 @@ M.default = {
6464
},
6565
}
6666

67-
---@param spec render.md.debug.ValidatorSpec
68-
function M.validate(spec)
69-
spec:type('enabled', 'boolean')
70-
spec:list('disabled_modes', 'string', 'boolean')
71-
spec:type('above', 'number')
72-
spec:type('below', 'number')
73-
spec:nested('ignore', function(ignore)
74-
for _, element in pairs(Element) do
75-
ignore:list(element, 'string', { 'boolean', 'nil' })
76-
end
77-
ignore:check()
78-
end)
79-
spec:check()
67+
---@return render.md.Schema
68+
function M.schema()
69+
---@type render.md.Schema
70+
local modes = {
71+
union = { { list = { type = 'string' } }, { type = 'boolean' } },
72+
}
73+
---@type render.md.Schema
74+
return {
75+
record = {
76+
enabled = { type = 'boolean' },
77+
disabled_modes = modes,
78+
above = { type = 'number' },
79+
below = { type = 'number' },
80+
ignore = { map = { key = { enum = Element }, value = modes } },
81+
},
82+
}
8083
end
8184

8285
return M

lua/render-markdown/config/base.lua

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,18 @@
77
---@class render.md.base.Cfg
88
local M = {}
99

10-
---@param spec render.md.debug.ValidatorSpec
11-
function M.validate(spec)
12-
spec:type('enabled', 'boolean')
13-
spec:list('render_modes', 'string', 'boolean')
10+
---@param additional_fields render.md.schema.Fields
11+
---@return render.md.Schema
12+
function M.schema(additional_fields)
13+
---@type render.md.schema.Fields
14+
local fields = {
15+
enabled = { type = 'boolean' },
16+
render_modes = {
17+
union = { { list = { type = 'string' } }, { type = 'boolean' } },
18+
},
19+
}
20+
---@type render.md.Schema
21+
return { record = vim.tbl_deep_extend('error', fields, additional_fields) }
1422
end
1523

1624
return M

lua/render-markdown/config/bullet.lua

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,29 @@ M.default = {
6666
scope_highlight = {},
6767
}
6868

69-
---@param spec render.md.debug.ValidatorSpec
70-
function M.validate(spec)
71-
require('render-markdown.config.base').validate(spec)
72-
spec:nested_list('icons', 'string', 'function')
73-
spec:nested_list('ordered_icons', 'string', 'function')
74-
spec:type('left_pad', { 'number', 'function' })
75-
spec:type('right_pad', { 'number', 'function' })
76-
spec:nested_list('highlight', 'string', 'function')
77-
spec:nested_list('scope_highlight', 'string', 'function')
78-
spec:check()
69+
---@return render.md.Schema
70+
function M.schema()
71+
---@type render.md.Schema
72+
local string_provider = {
73+
union = {
74+
{ type = 'string' },
75+
{ list = { type = 'string' } },
76+
{ list = { list = { type = 'string' } } },
77+
{ type = 'function' },
78+
},
79+
}
80+
---@type render.md.Schema
81+
local integer_provider = {
82+
union = { { type = 'number' }, { type = 'function' } },
83+
}
84+
return require('render-markdown.config.base').schema({
85+
icons = string_provider,
86+
ordered_icons = string_provider,
87+
left_pad = integer_provider,
88+
right_pad = integer_provider,
89+
highlight = string_provider,
90+
scope_highlight = string_provider,
91+
})
7992
end
8093

8194
return M

lua/render-markdown/config/callout.lua

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,20 @@ M.default = {
5151
cite = { raw = '[!CITE]', rendered = '󱆨 Cite', highlight = 'RenderMarkdownQuote', category = 'obsidian' },
5252
}
5353

54-
---@param spec render.md.debug.ValidatorSpec
55-
function M.validate(spec)
56-
spec:each(function(callout)
57-
callout:type('raw', 'string')
58-
callout:type('rendered', 'string')
59-
callout:type('highlight', 'string')
60-
callout:type('quote_icon', { 'string', 'nil' })
61-
callout:type('category', { 'string', 'nil' })
62-
callout:check()
63-
end, false)
64-
spec:check()
54+
---@return render.md.Schema
55+
function M.schema()
56+
---@type render.md.Schema
57+
local callout = {
58+
record = {
59+
raw = { type = 'string' },
60+
rendered = { type = 'string' },
61+
highlight = { type = 'string' },
62+
quote_icon = { optional = true, type = 'string' },
63+
category = { optional = true, type = 'string' },
64+
},
65+
}
66+
---@type render.md.Schema
67+
return { map = { key = { type = 'string' }, value = callout } }
6568
end
6669

6770
return M

lua/render-markdown/config/checkbox.lua

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -61,28 +61,32 @@ M.default = {
6161
},
6262
}
6363

64-
---@param spec render.md.debug.ValidatorSpec
65-
function M.validate(spec)
66-
require('render-markdown.config.base').validate(spec)
67-
spec:type('bullet', 'boolean')
68-
spec:type('right_pad', 'number')
69-
spec:nested({ 'unchecked', 'checked' }, function(box)
70-
box:type('icon', 'string')
71-
box:type('highlight', 'string')
72-
box:type('scope_highlight', { 'string', 'nil' })
73-
box:check()
74-
end)
75-
spec:nested('custom', function(boxes)
76-
boxes:each(function(box)
77-
box:type('raw', 'string')
78-
box:type('rendered', 'string')
79-
box:type('highlight', 'string')
80-
box:type('scope_highlight', { 'string', 'nil' })
81-
box:check()
82-
end)
83-
boxes:check()
84-
end)
85-
spec:check()
64+
---@return render.md.Schema
65+
function M.schema()
66+
---@type render.md.Schema
67+
local component = {
68+
record = {
69+
icon = { type = 'string' },
70+
highlight = { type = 'string' },
71+
scope_highlight = { optional = true, type = 'string' },
72+
},
73+
}
74+
---@type render.md.Schema
75+
local custom = {
76+
record = {
77+
raw = { type = 'string' },
78+
rendered = { type = 'string' },
79+
highlight = { type = 'string' },
80+
scope_highlight = { optional = true, type = 'string' },
81+
},
82+
}
83+
return require('render-markdown.config.base').schema({
84+
bullet = { type = 'boolean' },
85+
right_pad = { type = 'number' },
86+
unchecked = component,
87+
checked = component,
88+
custom = { map = { key = { type = 'string' }, value = custom } },
89+
})
8690
end
8791

8892
return M

lua/render-markdown/config/code.lua

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -152,41 +152,45 @@ M.default = {
152152
style = 'full',
153153
}
154154

155-
---@param spec render.md.debug.ValidatorSpec
156-
function M.validate(spec)
157-
require('render-markdown.config.base').validate(spec)
158-
spec:type('sign', 'boolean')
159-
spec:type('conceal_delimiters', 'boolean')
160-
spec:type('language', 'boolean')
161-
spec:one_of('position', vim.tbl_values(Position))
162-
spec:type('language_icon', 'boolean')
163-
spec:type('language_name', 'boolean')
164-
spec:type('language_info', 'boolean')
165-
spec:type('language_pad', 'number')
166-
spec:list('disable_background', 'string', 'boolean')
167-
spec:one_of('width', vim.tbl_values(Width))
168-
spec:type('left_margin', 'number')
169-
spec:type('left_pad', 'number')
170-
spec:type('right_pad', 'number')
171-
spec:type('min_width', 'number')
172-
spec:one_of('border', vim.tbl_values(Border))
173-
spec:type('language_border', 'string')
174-
spec:type('language_left', 'string')
175-
spec:type('language_right', 'string')
176-
spec:type('above', 'string')
177-
spec:type('below', 'string')
178-
spec:type('inline', 'boolean')
179-
spec:type('inline_left', 'string')
180-
spec:type('inline_right', 'string')
181-
spec:type('inline_pad', 'number')
182-
spec:type('highlight', 'string')
183-
spec:type('highlight_info', 'string')
184-
spec:type('highlight_language', { 'string', 'nil' })
185-
spec:one_of('highlight_border', { false }, 'string')
186-
spec:type('highlight_fallback', 'string')
187-
spec:type('highlight_inline', 'string')
188-
spec:one_of('style', vim.tbl_values(Style))
189-
spec:check()
155+
---@return render.md.Schema
156+
function M.schema()
157+
return require('render-markdown.config.base').schema({
158+
sign = { type = 'boolean' },
159+
conceal_delimiters = { type = 'boolean' },
160+
language = { type = 'boolean' },
161+
position = { enum = Position },
162+
language_icon = { type = 'boolean' },
163+
language_name = { type = 'boolean' },
164+
language_info = { type = 'boolean' },
165+
language_pad = { type = 'number' },
166+
disable_background = {
167+
union = { { list = { type = 'string' } }, { type = 'boolean' } },
168+
},
169+
width = { enum = Width },
170+
left_margin = { type = 'number' },
171+
left_pad = { type = 'number' },
172+
right_pad = { type = 'number' },
173+
min_width = { type = 'number' },
174+
border = { enum = Border },
175+
language_border = { type = 'string' },
176+
language_left = { type = 'string' },
177+
language_right = { type = 'string' },
178+
above = { type = 'string' },
179+
below = { type = 'string' },
180+
inline = { type = 'boolean' },
181+
inline_left = { type = 'string' },
182+
inline_right = { type = 'string' },
183+
inline_pad = { type = 'number' },
184+
highlight = { type = 'string' },
185+
highlight_info = { type = 'string' },
186+
highlight_language = { optional = true, type = 'string' },
187+
highlight_border = {
188+
union = { { enum = { false } }, { type = 'string' } },
189+
},
190+
highlight_fallback = { type = 'string' },
191+
highlight_inline = { type = 'string' },
192+
style = { enum = Style },
193+
})
190194
end
191195

192196
return M

lua/render-markdown/config/completions.lua

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,24 @@ M.default = {
3434
},
3535
}
3636

37-
---@param spec render.md.debug.ValidatorSpec
38-
function M.validate(spec)
39-
spec:nested({ 'blink', 'coq', 'lsp' }, function(completion)
40-
completion:type('enabled', 'boolean')
41-
completion:check()
42-
end)
43-
spec:nested('filter', function(filter)
44-
filter:type('callout', 'function')
45-
filter:type('checkbox', 'function')
46-
filter:check()
47-
end)
48-
spec:check()
37+
---@return render.md.Schema
38+
function M.schema()
39+
---@type render.md.Schema
40+
local completion = { record = { enabled = { type = 'boolean' } } }
41+
---@type render.md.Schema
42+
return {
43+
record = {
44+
blink = completion,
45+
coq = completion,
46+
lsp = completion,
47+
filter = {
48+
record = {
49+
callout = { type = 'function' },
50+
checkbox = { type = 'function' },
51+
},
52+
},
53+
},
54+
}
4955
end
5056

5157
return M

lua/render-markdown/config/dash.lua

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ M.default = {
3030
highlight = 'RenderMarkdownDash',
3131
}
3232

33-
---@param spec render.md.debug.ValidatorSpec
34-
function M.validate(spec)
35-
require('render-markdown.config.base').validate(spec)
36-
spec:type('icon', 'string')
37-
spec:one_of('width', { 'full' }, 'number')
38-
spec:type('left_margin', 'number')
39-
spec:type('highlight', 'string')
40-
spec:check()
33+
---@return render.md.Schema
34+
function M.schema()
35+
return require('render-markdown.config.base').schema({
36+
icon = { type = 'string' },
37+
width = {
38+
union = { { enum = { 'full' } }, { type = 'number' } },
39+
},
40+
left_margin = { type = 'number' },
41+
highlight = { type = 'string' },
42+
})
4143
end
4244

4345
return M

lua/render-markdown/config/document.lua

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@ M.default = {
2424
},
2525
}
2626

27-
---@param spec render.md.debug.ValidatorSpec
28-
function M.validate(spec)
29-
require('render-markdown.config.base').validate(spec)
30-
spec:nested('conceal', function(conceal)
31-
conceal:list('char_patterns', 'string')
32-
conceal:list('line_patterns', 'string')
33-
conceal:check()
34-
end)
35-
spec:check()
27+
---@return render.md.Schema
28+
function M.schema()
29+
return require('render-markdown.config.base').schema({
30+
conceal = {
31+
record = {
32+
char_patterns = { list = { type = 'string' } },
33+
line_patterns = { list = { type = 'string' } },
34+
},
35+
},
36+
})
3637
end
3738

3839
return M

0 commit comments

Comments
 (0)