2023-08-17 03:14:58 -07:00
|
|
|
-- Generator for various vimdoc and Lua type files
|
|
|
|
|
|
|
|
local DEP_API_METADATA = 'build/api_metadata.mpack'
|
2023-12-04 13:38:31 -07:00
|
|
|
local DEP_API_DOC = 'runtime/doc/api.mpack'
|
2023-07-28 06:48:41 -07:00
|
|
|
|
2023-08-01 06:20:44 -07:00
|
|
|
--- @class vim.api.metadata
|
|
|
|
--- @field name string
|
|
|
|
--- @field parameters {[1]:string,[2]:string}[]
|
|
|
|
--- @field return_type string
|
|
|
|
--- @field deprecated_since integer
|
|
|
|
--- @field eval boolean
|
|
|
|
--- @field fast boolean
|
|
|
|
--- @field handler_id integer
|
|
|
|
--- @field impl_name string
|
|
|
|
--- @field lua boolean
|
|
|
|
--- @field method boolean
|
|
|
|
--- @field remote boolean
|
|
|
|
--- @field since integer
|
|
|
|
|
|
|
|
local LUA_META_HEADER = {
|
2023-10-09 15:34:48 -07:00
|
|
|
'--- @meta _',
|
2023-08-01 06:20:44 -07:00
|
|
|
'-- THIS FILE IS GENERATED',
|
|
|
|
'-- DO NOT EDIT',
|
|
|
|
"error('Cannot require a meta file')",
|
|
|
|
}
|
|
|
|
|
|
|
|
local LUA_API_META_HEADER = {
|
2023-10-09 15:34:48 -07:00
|
|
|
'--- @meta _',
|
2023-08-01 06:20:44 -07:00
|
|
|
'-- THIS FILE IS GENERATED',
|
|
|
|
'-- DO NOT EDIT',
|
|
|
|
"error('Cannot require a meta file')",
|
|
|
|
'',
|
|
|
|
'vim.api = {}',
|
|
|
|
}
|
2023-07-28 06:48:41 -07:00
|
|
|
|
2023-08-04 13:26:53 -07:00
|
|
|
local LUA_OPTION_META_HEADER = {
|
2023-10-09 15:34:48 -07:00
|
|
|
'--- @meta _',
|
2023-08-04 13:26:53 -07:00
|
|
|
'-- THIS FILE IS GENERATED',
|
|
|
|
'-- DO NOT EDIT',
|
|
|
|
"error('Cannot require a meta file')",
|
|
|
|
'',
|
|
|
|
'---@class vim.bo',
|
|
|
|
'---@field [integer] vim.bo',
|
|
|
|
'vim.bo = vim.bo',
|
|
|
|
'',
|
|
|
|
'---@class vim.wo',
|
|
|
|
'---@field [integer] vim.wo',
|
|
|
|
'vim.wo = vim.wo',
|
|
|
|
}
|
|
|
|
|
2023-12-20 09:34:17 -07:00
|
|
|
local LUA_VVAR_META_HEADER = {
|
|
|
|
'--- @meta _',
|
|
|
|
'-- THIS FILE IS GENERATED',
|
|
|
|
'-- DO NOT EDIT',
|
|
|
|
"error('Cannot require a meta file')",
|
|
|
|
'',
|
|
|
|
'--- @class vim.v',
|
|
|
|
'vim.v = ...',
|
|
|
|
}
|
|
|
|
|
2023-07-28 06:48:41 -07:00
|
|
|
local LUA_KEYWORDS = {
|
|
|
|
['and'] = true,
|
|
|
|
['end'] = true,
|
|
|
|
['function'] = true,
|
|
|
|
['or'] = true,
|
|
|
|
['if'] = true,
|
|
|
|
['while'] = true,
|
|
|
|
['repeat'] = true,
|
2023-12-20 09:34:17 -07:00
|
|
|
['true'] = true,
|
|
|
|
['false'] = true,
|
2023-07-28 06:48:41 -07:00
|
|
|
}
|
|
|
|
|
2023-08-04 13:26:53 -07:00
|
|
|
local OPTION_TYPES = {
|
2023-12-06 12:34:29 -07:00
|
|
|
boolean = 'boolean',
|
2023-08-04 13:26:53 -07:00
|
|
|
number = 'integer',
|
|
|
|
string = 'string',
|
|
|
|
}
|
|
|
|
|
2023-08-01 06:20:44 -07:00
|
|
|
local API_TYPES = {
|
|
|
|
Window = 'integer',
|
|
|
|
Tabpage = 'integer',
|
|
|
|
Buffer = 'integer',
|
|
|
|
Boolean = 'boolean',
|
|
|
|
Object = 'any',
|
|
|
|
Integer = 'integer',
|
|
|
|
String = 'string',
|
|
|
|
Array = 'any[]',
|
|
|
|
LuaRef = 'function',
|
|
|
|
Dictionary = 'table<string,any>',
|
|
|
|
Float = 'number',
|
|
|
|
void = '',
|
|
|
|
}
|
|
|
|
|
2023-08-04 13:26:53 -07:00
|
|
|
--- @param x string
|
|
|
|
--- @param sep? string
|
|
|
|
--- @return string[]
|
|
|
|
local function split(x, sep)
|
|
|
|
return vim.split(x, sep or '\n', { plain = true })
|
|
|
|
end
|
|
|
|
|
2023-08-01 06:20:44 -07:00
|
|
|
--- Convert an API type to Lua
|
|
|
|
--- @param t string
|
|
|
|
--- @return string
|
|
|
|
local function api_type(t)
|
|
|
|
local as0 = t:match('^ArrayOf%((.*)%)')
|
|
|
|
if as0 then
|
2023-08-04 13:26:53 -07:00
|
|
|
local as = split(as0, ', ')
|
2023-08-01 06:20:44 -07:00
|
|
|
return api_type(as[1]) .. '[]'
|
|
|
|
end
|
|
|
|
|
|
|
|
local d = t:match('^Dict%((.*)%)')
|
|
|
|
if d then
|
|
|
|
return 'vim.api.keyset.' .. d
|
|
|
|
end
|
|
|
|
|
|
|
|
local d0 = t:match('^DictionaryOf%((.*)%)')
|
|
|
|
if d0 then
|
|
|
|
return 'table<string,' .. api_type(d0) .. '>'
|
|
|
|
end
|
|
|
|
|
|
|
|
return API_TYPES[t] or t
|
|
|
|
end
|
|
|
|
|
2023-07-28 06:48:41 -07:00
|
|
|
--- @param f string
|
|
|
|
--- @param params {[1]:string,[2]:string}[]|true
|
2023-08-04 13:26:53 -07:00
|
|
|
--- @return string
|
2023-07-28 06:48:41 -07:00
|
|
|
local function render_fun_sig(f, params)
|
|
|
|
local param_str --- @type string
|
|
|
|
if params == true then
|
|
|
|
param_str = '...'
|
|
|
|
else
|
|
|
|
param_str = table.concat(
|
|
|
|
vim.tbl_map(
|
|
|
|
--- @param v {[1]:string,[2]:string}
|
2023-08-04 13:26:53 -07:00
|
|
|
--- @return string
|
2023-07-28 06:48:41 -07:00
|
|
|
function(v)
|
|
|
|
return v[1]
|
|
|
|
end,
|
|
|
|
params
|
|
|
|
),
|
|
|
|
', '
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
if LUA_KEYWORDS[f] then
|
|
|
|
return string.format("vim.fn['%s'] = function(%s) end", f, param_str)
|
|
|
|
else
|
|
|
|
return string.format('function vim.fn.%s(%s) end', f, param_str)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Uniquify names
|
|
|
|
--- Fix any names that are lua keywords
|
2023-08-01 06:20:44 -07:00
|
|
|
--- @param params {[1]:string,[2]:string,[3]:string}[]
|
|
|
|
--- @return {[1]:string,[2]:string,[3]:string}[]
|
2023-07-28 06:48:41 -07:00
|
|
|
local function process_params(params)
|
|
|
|
local seen = {} --- @type table<string,true>
|
|
|
|
local sfx = 1
|
|
|
|
|
|
|
|
for _, p in ipairs(params) do
|
|
|
|
if LUA_KEYWORDS[p[1]] then
|
|
|
|
p[1] = p[1] .. '_'
|
|
|
|
end
|
|
|
|
if seen[p[1]] then
|
|
|
|
p[1] = p[1] .. sfx
|
|
|
|
sfx = sfx + 1
|
|
|
|
else
|
|
|
|
seen[p[1]] = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return params
|
|
|
|
end
|
|
|
|
|
2023-08-01 06:20:44 -07:00
|
|
|
--- @class vim.gen_vim_doc_fun
|
|
|
|
--- @field signature string
|
|
|
|
--- @field doc string[]
|
|
|
|
--- @field parameters_doc table<string,string>
|
|
|
|
--- @field return string[]
|
|
|
|
--- @field seealso string[]
|
|
|
|
--- @field annotations string[]
|
|
|
|
|
|
|
|
--- @return table<string, vim.EvalFn>
|
2023-08-04 13:26:53 -07:00
|
|
|
local function get_api_meta()
|
2023-08-17 03:14:58 -07:00
|
|
|
local mpack_f = assert(io.open(DEP_API_METADATA, 'rb'))
|
2023-08-01 06:20:44 -07:00
|
|
|
local metadata = vim.mpack.decode(mpack_f:read('*all')) --[[@as vim.api.metadata[] ]]
|
|
|
|
local ret = {} --- @type table<string, vim.EvalFn>
|
|
|
|
|
2023-08-17 03:14:58 -07:00
|
|
|
local doc_mpack_f = assert(io.open(DEP_API_DOC, 'rb'))
|
2023-08-01 06:20:44 -07:00
|
|
|
local doc_metadata = vim.mpack.decode(doc_mpack_f:read('*all')) --[[@as table<string,vim.gen_vim_doc_fun>]]
|
|
|
|
|
|
|
|
for _, fun in ipairs(metadata) do
|
2023-11-27 22:52:17 -07:00
|
|
|
if fun.lua then
|
|
|
|
local fdoc = doc_metadata[fun.name]
|
|
|
|
|
|
|
|
local params = {} --- @type {[1]:string,[2]:string}[]
|
|
|
|
for _, p in ipairs(fun.parameters) do
|
|
|
|
local ptype, pname = p[1], p[2]
|
|
|
|
params[#params + 1] = {
|
|
|
|
pname,
|
|
|
|
api_type(ptype),
|
|
|
|
fdoc and fdoc.parameters_doc[pname] or nil,
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
local r = {
|
|
|
|
signature = 'NA',
|
|
|
|
name = fun.name,
|
|
|
|
params = params,
|
|
|
|
returns = api_type(fun.return_type),
|
|
|
|
deprecated = fun.deprecated_since ~= nil,
|
2023-08-01 06:20:44 -07:00
|
|
|
}
|
|
|
|
|
2023-11-27 22:52:17 -07:00
|
|
|
if fdoc then
|
|
|
|
if #fdoc.doc > 0 then
|
|
|
|
r.desc = table.concat(fdoc.doc, '\n')
|
|
|
|
end
|
|
|
|
r.return_desc = (fdoc['return'] or {})[1]
|
2023-08-01 06:20:44 -07:00
|
|
|
end
|
|
|
|
|
2023-11-27 22:52:17 -07:00
|
|
|
ret[fun.name] = r
|
|
|
|
end
|
2023-08-01 06:20:44 -07:00
|
|
|
end
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Convert vimdoc references to markdown literals
|
|
|
|
--- Convert vimdoc codeblocks to markdown codeblocks
|
2023-09-14 06:23:01 -07:00
|
|
|
---
|
|
|
|
--- Ensure code blocks have one empty line before the start fence and after the closing fence.
|
|
|
|
---
|
2023-08-01 06:20:44 -07:00
|
|
|
--- @param x string
|
|
|
|
--- @return string
|
|
|
|
local function norm_text(x)
|
|
|
|
return (
|
|
|
|
x:gsub('|([^ ]+)|', '`%1`')
|
2023-09-14 06:23:01 -07:00
|
|
|
:gsub('\n*>lua', '\n\n```lua')
|
|
|
|
:gsub('\n*>vim', '\n\n```vim')
|
|
|
|
:gsub('\n+<$', '\n```')
|
|
|
|
:gsub('\n+<\n+', '\n```\n\n')
|
|
|
|
:gsub('%s+>\n+', '\n```\n')
|
|
|
|
:gsub('\n+<%s+\n?', '\n```\n')
|
2023-08-01 06:20:44 -07:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
--- @param _f string
|
|
|
|
--- @param fun vim.EvalFn
|
|
|
|
--- @param write fun(line: string)
|
2023-08-04 13:26:53 -07:00
|
|
|
local function render_api_meta(_f, fun, write)
|
2023-08-01 06:20:44 -07:00
|
|
|
if not vim.startswith(fun.name, 'nvim_') then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
write('')
|
|
|
|
|
|
|
|
if vim.startswith(fun.name, 'nvim__') then
|
|
|
|
write('--- @private')
|
|
|
|
end
|
|
|
|
|
|
|
|
if fun.deprecated then
|
|
|
|
write('--- @deprecated')
|
|
|
|
end
|
|
|
|
|
|
|
|
local desc = fun.desc
|
|
|
|
if desc then
|
2023-08-04 13:26:53 -07:00
|
|
|
for _, l in ipairs(split(norm_text(desc))) do
|
2023-08-01 06:20:44 -07:00
|
|
|
write('--- ' .. l)
|
|
|
|
end
|
|
|
|
write('---')
|
|
|
|
end
|
|
|
|
|
|
|
|
local param_names = {} --- @type string[]
|
|
|
|
local params = process_params(fun.params)
|
|
|
|
for _, p in ipairs(params) do
|
|
|
|
param_names[#param_names + 1] = p[1]
|
|
|
|
local pdesc = p[3]
|
|
|
|
if pdesc then
|
2023-08-04 13:26:53 -07:00
|
|
|
local pdesc_a = split(norm_text(pdesc))
|
2023-08-01 06:20:44 -07:00
|
|
|
write('--- @param ' .. p[1] .. ' ' .. p[2] .. ' ' .. pdesc_a[1])
|
|
|
|
for i = 2, #pdesc_a do
|
|
|
|
if not pdesc_a[i] then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
write('--- ' .. pdesc_a[i])
|
|
|
|
end
|
|
|
|
else
|
|
|
|
write('--- @param ' .. p[1] .. ' ' .. p[2])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if fun.returns ~= '' then
|
|
|
|
if fun.returns_desc then
|
|
|
|
write('--- @return ' .. fun.returns .. ' : ' .. fun.returns_desc)
|
|
|
|
else
|
|
|
|
write('--- @return ' .. fun.returns)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local param_str = table.concat(param_names, ', ')
|
|
|
|
|
|
|
|
write(string.format('function vim.api.%s(%s) end', fun.name, param_str))
|
|
|
|
end
|
|
|
|
|
|
|
|
--- @return table<string, vim.EvalFn>
|
2023-08-04 13:26:53 -07:00
|
|
|
local function get_api_keysets_meta()
|
2023-08-17 03:14:58 -07:00
|
|
|
local mpack_f = assert(io.open(DEP_API_METADATA, 'rb'))
|
2023-08-01 06:20:44 -07:00
|
|
|
|
|
|
|
--- @diagnostic disable-next-line:no-unknown
|
|
|
|
local metadata = assert(vim.mpack.decode(mpack_f:read('*all')))
|
|
|
|
|
|
|
|
local ret = {} --- @type table<string, vim.EvalFn>
|
|
|
|
|
2023-08-01 05:01:19 -07:00
|
|
|
--- @type {name: string, keys: string[], types: table<string,string>}[]
|
2023-08-01 06:20:44 -07:00
|
|
|
local keysets = metadata.keysets
|
|
|
|
|
2023-08-01 05:01:19 -07:00
|
|
|
for _, k in ipairs(keysets) do
|
|
|
|
local params = {}
|
|
|
|
for _, key in ipairs(k.keys) do
|
2023-12-04 13:38:31 -07:00
|
|
|
table.insert(params, { key .. '?', api_type(k.types[key] or 'any') })
|
2023-08-01 06:20:44 -07:00
|
|
|
end
|
2023-08-01 05:01:19 -07:00
|
|
|
ret[k.name] = {
|
2023-08-01 06:20:44 -07:00
|
|
|
signature = 'NA',
|
2023-08-01 05:01:19 -07:00
|
|
|
name = k.name,
|
|
|
|
params = params,
|
2023-08-01 06:20:44 -07:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
|
|
|
--- @param _f string
|
|
|
|
--- @param fun vim.EvalFn
|
|
|
|
--- @param write fun(line: string)
|
2023-08-04 13:26:53 -07:00
|
|
|
local function render_api_keyset_meta(_f, fun, write)
|
2023-08-01 06:20:44 -07:00
|
|
|
write('')
|
|
|
|
write('--- @class vim.api.keyset.' .. fun.name)
|
|
|
|
for _, p in ipairs(fun.params) do
|
|
|
|
write('--- @field ' .. p[1] .. ' ' .. p[2])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- @return table<string, vim.EvalFn>
|
2023-08-04 13:26:53 -07:00
|
|
|
local function get_eval_meta()
|
2023-08-01 06:20:44 -07:00
|
|
|
return require('src/nvim/eval').funcs
|
|
|
|
end
|
|
|
|
|
2023-07-28 06:48:41 -07:00
|
|
|
--- @param f string
|
|
|
|
--- @param fun vim.EvalFn
|
|
|
|
--- @param write fun(line: string)
|
2023-08-04 13:26:53 -07:00
|
|
|
local function render_eval_meta(f, fun, write)
|
2023-07-28 06:48:41 -07:00
|
|
|
if fun.lua == false then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local funname = fun.name or f
|
|
|
|
|
|
|
|
local params = process_params(fun.params)
|
|
|
|
|
2024-01-14 04:10:31 -07:00
|
|
|
write('')
|
|
|
|
if fun.deprecated then
|
|
|
|
write('--- @deprecated')
|
|
|
|
end
|
2023-07-28 06:48:41 -07:00
|
|
|
|
2024-01-14 04:10:31 -07:00
|
|
|
local desc = fun.desc
|
2023-07-28 06:48:41 -07:00
|
|
|
|
2024-01-14 04:10:31 -07:00
|
|
|
if desc then
|
|
|
|
--- @type string
|
|
|
|
desc = desc:gsub('\n%s*\n%s*$', '\n')
|
|
|
|
for _, l in ipairs(split(desc)) do
|
|
|
|
l = l:gsub('^ ', ''):gsub('\t', ' '):gsub('@', '\\@')
|
|
|
|
write('--- ' .. l)
|
2023-07-28 06:48:41 -07:00
|
|
|
end
|
2024-01-14 04:10:31 -07:00
|
|
|
end
|
2023-07-28 06:48:41 -07:00
|
|
|
|
2024-01-14 04:10:31 -07:00
|
|
|
local req_args = type(fun.args) == 'table' and fun.args[1] or fun.args or 0
|
2023-07-28 06:48:41 -07:00
|
|
|
|
2024-01-14 04:10:31 -07:00
|
|
|
for i, param in ipairs(params) do
|
|
|
|
local pname, ptype = param[1], param[2]
|
|
|
|
local optional = (pname ~= '...' and i > req_args) and '?' or ''
|
|
|
|
write(string.format('--- @param %s%s %s', pname, optional, ptype))
|
|
|
|
end
|
2023-07-28 06:48:41 -07:00
|
|
|
|
2024-01-14 04:10:31 -07:00
|
|
|
if fun.returns ~= false then
|
|
|
|
write('--- @return ' .. (fun.returns or 'any'))
|
2023-07-28 06:48:41 -07:00
|
|
|
end
|
|
|
|
|
2024-01-14 04:10:31 -07:00
|
|
|
write(render_fun_sig(funname, params))
|
2023-07-28 06:48:41 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
--- @type table<string,true>
|
|
|
|
local rendered_tags = {}
|
|
|
|
|
2023-08-01 03:12:00 -07:00
|
|
|
--- @param name string
|
|
|
|
--- @param fun vim.EvalFn
|
|
|
|
--- @param write fun(line: string)
|
|
|
|
local function render_sig_and_tag(name, fun, write)
|
2024-01-14 04:10:31 -07:00
|
|
|
if not fun.signature then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2023-08-01 03:12:00 -07:00
|
|
|
local tags = { '*' .. name .. '()*' }
|
|
|
|
|
|
|
|
if fun.tags then
|
|
|
|
for _, t in ipairs(fun.tags) do
|
|
|
|
tags[#tags + 1] = '*' .. t .. '*'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local tag = table.concat(tags, ' ')
|
|
|
|
local siglen = #fun.signature
|
2023-12-04 13:38:31 -07:00
|
|
|
local conceal_offset = 2 * (#tags - 1)
|
2023-08-01 03:12:00 -07:00
|
|
|
local tag_pad_len = math.max(1, 80 - #tag + conceal_offset)
|
|
|
|
|
|
|
|
if siglen + #tag > 80 then
|
|
|
|
write(string.rep(' ', tag_pad_len) .. tag)
|
|
|
|
write(fun.signature)
|
|
|
|
else
|
|
|
|
write(string.format('%s%s%s', fun.signature, string.rep(' ', tag_pad_len - siglen), tag))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-28 06:48:41 -07:00
|
|
|
--- @param f string
|
|
|
|
--- @param fun vim.EvalFn
|
|
|
|
--- @param write fun(line: string)
|
|
|
|
local function render_eval_doc(f, fun, write)
|
|
|
|
if fun.deprecated then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if not fun.signature then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local desc = fun.desc
|
|
|
|
|
|
|
|
if not desc then
|
|
|
|
write(fun.signature)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local name = fun.name or f
|
|
|
|
|
|
|
|
if rendered_tags[name] then
|
|
|
|
write(fun.signature)
|
|
|
|
else
|
2023-08-01 03:12:00 -07:00
|
|
|
render_sig_and_tag(name, fun, write)
|
|
|
|
rendered_tags[name] = true
|
2023-07-28 06:48:41 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
desc = vim.trim(desc)
|
2023-08-04 13:26:53 -07:00
|
|
|
local desc_l = split(desc)
|
2023-07-28 06:48:41 -07:00
|
|
|
for _, l in ipairs(desc_l) do
|
|
|
|
l = l:gsub('^ ', '')
|
2023-08-04 13:26:53 -07:00
|
|
|
if vim.startswith(l, '<') and not l:match('^<[^ \t]+>') then
|
2023-07-28 06:48:41 -07:00
|
|
|
write('<\t\t' .. l:sub(2))
|
2023-08-24 05:29:40 -07:00
|
|
|
elseif l:match('^>[a-z0-9]*$') then
|
|
|
|
write(l)
|
2023-07-28 06:48:41 -07:00
|
|
|
else
|
|
|
|
write('\t\t' .. l)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if #desc_l > 0 and not desc_l[#desc_l]:match('^<?$') then
|
|
|
|
write('')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-08-04 13:26:53 -07:00
|
|
|
--- @param d vim.option_defaults
|
|
|
|
--- @param vimdoc? boolean
|
|
|
|
--- @return string
|
|
|
|
local function render_option_default(d, vimdoc)
|
|
|
|
local dt --- @type integer|boolean|string|fun(): string
|
|
|
|
if d.if_false ~= nil then
|
|
|
|
dt = d.if_false
|
|
|
|
else
|
|
|
|
dt = d.if_true
|
|
|
|
end
|
|
|
|
|
|
|
|
if vimdoc then
|
|
|
|
if d.doc then
|
|
|
|
return d.doc
|
|
|
|
end
|
|
|
|
if type(dt) == 'boolean' then
|
|
|
|
return dt and 'on' or 'off'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-12-04 13:38:31 -07:00
|
|
|
if dt == '' or dt == nil or type(dt) == 'function' then
|
2023-08-04 13:26:53 -07:00
|
|
|
dt = d.meta
|
|
|
|
end
|
|
|
|
|
|
|
|
local v --- @type string
|
|
|
|
if not vimdoc then
|
|
|
|
v = vim.inspect(dt) --[[@as string]]
|
|
|
|
else
|
2023-12-04 13:38:31 -07:00
|
|
|
v = type(dt) == 'string' and '"' .. dt .. '"' or tostring(dt)
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
--- @type table<string, string|false>
|
|
|
|
local envvars = {
|
|
|
|
TMPDIR = false,
|
|
|
|
VIMRUNTIME = false,
|
2023-12-04 13:38:31 -07:00
|
|
|
XDG_CONFIG_HOME = vim.env.HOME .. '/.local/config',
|
|
|
|
XDG_DATA_HOME = vim.env.HOME .. '/.local/share',
|
|
|
|
XDG_STATE_HOME = vim.env.HOME .. '/.local/state',
|
2023-08-04 13:26:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for name, default in pairs(envvars) do
|
|
|
|
local value = vim.env[name] or default
|
|
|
|
if value then
|
2023-12-04 13:38:31 -07:00
|
|
|
v = v:gsub(vim.pesc(value), '$' .. name)
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return v
|
|
|
|
end
|
|
|
|
|
|
|
|
--- @param _f string
|
|
|
|
--- @param opt vim.option_meta
|
|
|
|
--- @param write fun(line: string)
|
|
|
|
local function render_option_meta(_f, opt, write)
|
|
|
|
write('')
|
|
|
|
for _, l in ipairs(split(norm_text(opt.desc))) do
|
2023-12-04 13:38:31 -07:00
|
|
|
write('--- ' .. l)
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
|
2023-12-04 13:38:31 -07:00
|
|
|
write('--- @type ' .. OPTION_TYPES[opt.type])
|
|
|
|
write('vim.o.' .. opt.full_name .. ' = ' .. render_option_default(opt.defaults))
|
2023-08-04 13:26:53 -07:00
|
|
|
if opt.abbreviation then
|
2023-12-04 13:38:31 -07:00
|
|
|
write('vim.o.' .. opt.abbreviation .. ' = vim.o.' .. opt.full_name)
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
for _, s in pairs {
|
2023-12-04 13:38:31 -07:00
|
|
|
{ 'wo', 'window' },
|
|
|
|
{ 'bo', 'buffer' },
|
|
|
|
{ 'go', 'global' },
|
2023-08-04 13:26:53 -07:00
|
|
|
} do
|
|
|
|
local id, scope = s[1], s[2]
|
|
|
|
if vim.list_contains(opt.scope, scope) or (id == 'go' and #opt.scope > 1) then
|
2023-12-04 13:38:31 -07:00
|
|
|
local pfx = 'vim.' .. id .. '.'
|
|
|
|
write(pfx .. opt.full_name .. ' = vim.o.' .. opt.full_name)
|
2023-08-04 13:26:53 -07:00
|
|
|
if opt.abbreviation then
|
2023-12-04 13:38:31 -07:00
|
|
|
write(pfx .. opt.abbreviation .. ' = ' .. pfx .. opt.full_name)
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-12-20 09:34:17 -07:00
|
|
|
--- @param _f string
|
|
|
|
--- @param opt vim.option_meta
|
|
|
|
--- @param write fun(line: string)
|
|
|
|
local function render_vvar_meta(_f, opt, write)
|
|
|
|
write('')
|
|
|
|
|
|
|
|
local desc = split(norm_text(opt.desc))
|
|
|
|
while desc[#desc]:match('^%s*$') do
|
|
|
|
desc[#desc] = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
for _, l in ipairs(desc) do
|
|
|
|
write('--- ' .. l)
|
|
|
|
end
|
|
|
|
|
|
|
|
write('--- @type ' .. (opt.type or 'any'))
|
|
|
|
|
|
|
|
if LUA_KEYWORDS[opt.full_name] then
|
|
|
|
write("vim.v['" .. opt.full_name .. "'] = ...")
|
|
|
|
else
|
|
|
|
write('vim.v.' .. opt.full_name .. ' = ...')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-08-04 13:26:53 -07:00
|
|
|
--- @param s string[]
|
|
|
|
--- @return string
|
|
|
|
local function scope_to_doc(s)
|
|
|
|
local m = {
|
|
|
|
global = 'global',
|
|
|
|
buffer = 'local to buffer',
|
|
|
|
window = 'local to window',
|
2023-12-04 13:38:31 -07:00
|
|
|
tab = 'local to tab page',
|
2023-08-04 13:26:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if #s == 1 then
|
|
|
|
return m[s[1]]
|
|
|
|
end
|
|
|
|
assert(s[1] == 'global')
|
2023-12-04 13:38:31 -07:00
|
|
|
return 'global or ' .. m[s[2]] .. ' |global-local|'
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
|
2023-08-23 01:32:15 -07:00
|
|
|
-- @param o vim.option_meta
|
|
|
|
-- @return string
|
|
|
|
local function scope_more_doc(o)
|
|
|
|
if
|
|
|
|
vim.list_contains({
|
|
|
|
'bufhidden',
|
|
|
|
'buftype',
|
|
|
|
'filetype',
|
|
|
|
'modified',
|
2023-09-11 16:37:05 -07:00
|
|
|
'previewwindow',
|
2023-08-23 01:32:15 -07:00
|
|
|
'readonly',
|
2023-09-11 16:37:05 -07:00
|
|
|
'scroll',
|
2023-08-23 01:32:15 -07:00
|
|
|
'syntax',
|
2023-09-11 16:37:05 -07:00
|
|
|
'winfixheight',
|
|
|
|
'winfixwidth',
|
2023-08-23 01:32:15 -07:00
|
|
|
}, o.full_name)
|
|
|
|
then
|
2023-09-11 16:37:05 -07:00
|
|
|
return ' |local-noglobal|'
|
2023-08-23 01:32:15 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
return ''
|
|
|
|
end
|
|
|
|
|
2023-12-20 09:34:17 -07:00
|
|
|
--- @param x string
|
|
|
|
--- @return string
|
|
|
|
local function dedent(x)
|
|
|
|
local xs = split(x)
|
|
|
|
local leading_ws = xs[1]:match('^%s*') --[[@as string]]
|
|
|
|
local leading_ws_pat = '^' .. leading_ws
|
|
|
|
|
|
|
|
for i in ipairs(xs) do
|
|
|
|
local strip_pat = xs[i]:match(leading_ws_pat) and leading_ws_pat or '^%s*'
|
|
|
|
xs[i] = xs[i]:gsub(strip_pat, '')
|
|
|
|
end
|
|
|
|
|
|
|
|
return table.concat(xs, '\n')
|
|
|
|
end
|
|
|
|
|
2023-08-04 13:26:53 -07:00
|
|
|
--- @return table<string,vim.option_meta>
|
|
|
|
local function get_option_meta()
|
|
|
|
local opts = require('src/nvim/options').options
|
|
|
|
local optinfo = vim.api.nvim_get_all_options_info()
|
|
|
|
local ret = {} --- @type table<string,vim.option_meta>
|
|
|
|
for _, o in ipairs(opts) do
|
|
|
|
if o.desc then
|
|
|
|
if o.full_name == 'cmdheight' then
|
|
|
|
table.insert(o.scope, 'tab')
|
|
|
|
end
|
|
|
|
local r = vim.deepcopy(o) --[[@as vim.option_meta]]
|
|
|
|
r.desc = o.desc:gsub('^ ', ''):gsub('\n ', '\n')
|
|
|
|
r.defaults = r.defaults or {}
|
|
|
|
if r.defaults.meta == nil then
|
|
|
|
r.defaults.meta = optinfo[o.full_name].default
|
|
|
|
end
|
|
|
|
ret[o.full_name] = r
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2023-12-20 09:34:17 -07:00
|
|
|
--- @return table<string,vim.option_meta>
|
|
|
|
local function get_vvar_meta()
|
|
|
|
local info = require('src/nvim/vvars').vars
|
|
|
|
local ret = {} --- @type table<string,vim.option_meta>
|
|
|
|
for name, o in pairs(info) do
|
|
|
|
o.desc = dedent(o.desc)
|
|
|
|
o.full_name = name
|
|
|
|
ret[name] = o
|
|
|
|
end
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2023-08-04 13:26:53 -07:00
|
|
|
--- @param opt vim.option_meta
|
|
|
|
--- @return string[]
|
|
|
|
local function build_option_tags(opt)
|
|
|
|
--- @type string[]
|
|
|
|
local tags = { opt.full_name }
|
|
|
|
|
2023-12-04 13:38:31 -07:00
|
|
|
tags[#tags + 1] = opt.abbreviation
|
2023-12-06 12:34:29 -07:00
|
|
|
if opt.type == 'boolean' then
|
2023-08-04 13:26:53 -07:00
|
|
|
for i = 1, #tags do
|
2023-12-04 13:38:31 -07:00
|
|
|
tags[#tags + 1] = 'no' .. tags[i]
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
for i, t in ipairs(tags) do
|
2023-12-04 13:38:31 -07:00
|
|
|
tags[i] = "'" .. t .. "'"
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
for _, t in ipairs(opt.tags or {}) do
|
2023-12-04 13:38:31 -07:00
|
|
|
tags[#tags + 1] = t
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
for i, t in ipairs(tags) do
|
2023-12-04 13:38:31 -07:00
|
|
|
tags[i] = '*' .. t .. '*'
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
return tags
|
|
|
|
end
|
|
|
|
|
|
|
|
--- @param _f string
|
|
|
|
--- @param opt vim.option_meta
|
|
|
|
--- @param write fun(line: string)
|
|
|
|
local function render_option_doc(_f, opt, write)
|
|
|
|
local tags = build_option_tags(opt)
|
|
|
|
local tag_str = table.concat(tags, ' ')
|
2023-12-04 13:38:31 -07:00
|
|
|
local conceal_offset = 2 * (#tags - 1)
|
2023-08-04 13:26:53 -07:00
|
|
|
local tag_pad = string.rep('\t', math.ceil((64 - #tag_str + conceal_offset) / 8))
|
|
|
|
-- local pad = string.rep(' ', 80 - #tag_str + conceal_offset)
|
2023-12-04 13:38:31 -07:00
|
|
|
write(tag_pad .. tag_str)
|
2023-08-04 13:26:53 -07:00
|
|
|
|
|
|
|
local name_str --- @type string
|
|
|
|
if opt.abbreviation then
|
|
|
|
name_str = string.format("'%s' '%s'", opt.full_name, opt.abbreviation)
|
|
|
|
else
|
|
|
|
name_str = string.format("'%s'", opt.full_name)
|
|
|
|
end
|
|
|
|
|
2023-12-06 12:34:29 -07:00
|
|
|
local otype = opt.type == 'boolean' and 'boolean' or opt.type
|
2023-08-04 13:26:53 -07:00
|
|
|
if opt.defaults.doc or opt.defaults.if_true ~= nil or opt.defaults.meta ~= nil then
|
|
|
|
local v = render_option_default(opt.defaults, true)
|
|
|
|
local pad = string.rep('\t', math.max(1, math.ceil((24 - #name_str) / 8)))
|
|
|
|
if opt.defaults.doc then
|
|
|
|
local deflen = #string.format('%s%s%s (', name_str, pad, otype)
|
|
|
|
--- @type string
|
2023-12-04 13:38:31 -07:00
|
|
|
v = v:gsub('\n', '\n' .. string.rep(' ', deflen - 2))
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
write(string.format('%s%s%s\t(default %s)', name_str, pad, otype, v))
|
|
|
|
else
|
|
|
|
write(string.format('%s\t%s', name_str, otype))
|
|
|
|
end
|
|
|
|
|
2023-12-04 13:38:31 -07:00
|
|
|
write('\t\t\t' .. scope_to_doc(opt.scope) .. scope_more_doc(opt))
|
2023-08-04 13:26:53 -07:00
|
|
|
for _, l in ipairs(split(opt.desc)) do
|
|
|
|
if l == '<' or l:match('^<%s') then
|
|
|
|
write(l)
|
|
|
|
else
|
2023-12-04 13:38:31 -07:00
|
|
|
write('\t' .. l:gsub('\\<', '<'))
|
2023-08-04 13:26:53 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-12-20 09:34:17 -07:00
|
|
|
--- @param _f string
|
|
|
|
--- @param vvar vim.option_meta
|
|
|
|
--- @param write fun(line: string)
|
|
|
|
local function render_vvar_doc(_f, vvar, write)
|
|
|
|
local name = vvar.full_name
|
|
|
|
|
|
|
|
local tags = { 'v:' .. name, name .. '-variable' }
|
|
|
|
if vvar.tags then
|
|
|
|
vim.list_extend(tags, vvar.tags)
|
|
|
|
end
|
|
|
|
|
|
|
|
for i, t in ipairs(tags) do
|
|
|
|
tags[i] = '*' .. t .. '*'
|
|
|
|
end
|
|
|
|
|
|
|
|
local tag_str = table.concat(tags, ' ')
|
|
|
|
local conceal_offset = 2 * (#tags - 1)
|
|
|
|
|
|
|
|
local tag_pad = string.rep('\t', math.ceil((64 - #tag_str + conceal_offset) / 8))
|
|
|
|
write(tag_pad .. tag_str)
|
|
|
|
|
|
|
|
local desc = split(vvar.desc)
|
|
|
|
|
|
|
|
if (#desc == 1 or #desc == 2 and desc[2]:match('^%s*$')) and #name < 10 then
|
|
|
|
-- single line
|
|
|
|
write('v:' .. name .. '\t' .. desc[1]:gsub('^%s*', ''))
|
|
|
|
write('')
|
|
|
|
else
|
|
|
|
write('v:' .. name)
|
|
|
|
for _, l in ipairs(desc) do
|
|
|
|
if l == '<' or l:match('^<%s') then
|
|
|
|
write(l)
|
|
|
|
else
|
|
|
|
write('\t\t' .. l:gsub('\\<', '<'))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-28 06:48:41 -07:00
|
|
|
--- @class nvim.gen_eval_files.elem
|
|
|
|
--- @field path string
|
2023-08-04 13:26:53 -07:00
|
|
|
--- @field from? string Skip lines in path until this pattern is reached.
|
|
|
|
--- @field funcs fun(): table<string, table>
|
|
|
|
--- @field render fun(f:string,obj:table,write:fun(line:string))
|
2023-07-28 06:48:41 -07:00
|
|
|
--- @field header? string[]
|
|
|
|
--- @field footer? string[]
|
|
|
|
|
|
|
|
--- @type nvim.gen_eval_files.elem[]
|
|
|
|
local CONFIG = {
|
|
|
|
{
|
|
|
|
path = 'runtime/lua/vim/_meta/vimfn.lua',
|
2023-08-01 06:20:44 -07:00
|
|
|
header = LUA_META_HEADER,
|
2023-08-04 13:26:53 -07:00
|
|
|
funcs = get_eval_meta,
|
|
|
|
render = render_eval_meta,
|
2023-08-01 06:20:44 -07:00
|
|
|
},
|
|
|
|
{
|
|
|
|
path = 'runtime/lua/vim/_meta/api.lua',
|
|
|
|
header = LUA_API_META_HEADER,
|
2023-08-04 13:26:53 -07:00
|
|
|
funcs = get_api_meta,
|
|
|
|
render = render_api_meta,
|
2023-08-01 06:20:44 -07:00
|
|
|
},
|
|
|
|
{
|
|
|
|
path = 'runtime/lua/vim/_meta/api_keysets.lua',
|
|
|
|
header = LUA_META_HEADER,
|
2023-08-04 13:26:53 -07:00
|
|
|
funcs = get_api_keysets_meta,
|
|
|
|
render = render_api_keyset_meta,
|
2023-07-28 06:48:41 -07:00
|
|
|
},
|
|
|
|
{
|
|
|
|
path = 'runtime/doc/builtin.txt',
|
2023-08-04 13:26:53 -07:00
|
|
|
funcs = get_eval_meta,
|
2023-07-28 06:48:41 -07:00
|
|
|
render = render_eval_doc,
|
|
|
|
header = {
|
|
|
|
'*builtin.txt* Nvim',
|
|
|
|
'',
|
|
|
|
'',
|
2023-08-01 16:17:26 -07:00
|
|
|
'\t\t NVIM REFERENCE MANUAL',
|
2023-07-28 06:48:41 -07:00
|
|
|
'',
|
|
|
|
'',
|
|
|
|
'Builtin functions\t\t*vimscript-functions* *builtin-functions*',
|
|
|
|
'',
|
|
|
|
'For functions grouped by what they are used for see |function-list|.',
|
|
|
|
'',
|
|
|
|
'\t\t\t\t Type |gO| to see the table of contents.',
|
|
|
|
'==============================================================================',
|
|
|
|
'1. Details *builtin-function-details*',
|
|
|
|
'',
|
|
|
|
},
|
|
|
|
footer = {
|
|
|
|
'==============================================================================',
|
|
|
|
'2. Matching a pattern in a String *string-match*',
|
|
|
|
'',
|
|
|
|
'This is common between several functions. A regexp pattern as explained at',
|
|
|
|
'|pattern| is normally used to find a match in the buffer lines. When a',
|
|
|
|
'pattern is used to find a match in a String, almost everything works in the',
|
|
|
|
'same way. The difference is that a String is handled like it is one line.',
|
|
|
|
'When it contains a "\\n" character, this is not seen as a line break for the',
|
|
|
|
'pattern. It can be matched with a "\\n" in the pattern, or with ".". Example:',
|
2023-08-01 01:57:52 -07:00
|
|
|
'>vim',
|
|
|
|
'\tlet a = "aaaa\\nxxxx"',
|
|
|
|
'\techo matchstr(a, "..\\n..")',
|
|
|
|
'\t" aa',
|
|
|
|
'\t" xx',
|
|
|
|
'\techo matchstr(a, "a.x")',
|
|
|
|
'\t" a',
|
|
|
|
'\t" x',
|
2023-07-28 06:48:41 -07:00
|
|
|
'',
|
|
|
|
'Don\'t forget that "^" will only match at the first character of the String and',
|
|
|
|
'"$" at the last character of the string. They don\'t match after or before a',
|
|
|
|
'"\\n".',
|
|
|
|
'',
|
|
|
|
' vim:tw=78:ts=8:noet:ft=help:norl:',
|
|
|
|
},
|
|
|
|
},
|
2023-08-04 13:26:53 -07:00
|
|
|
{
|
|
|
|
path = 'runtime/lua/vim/_meta/options.lua',
|
|
|
|
header = LUA_OPTION_META_HEADER,
|
|
|
|
funcs = get_option_meta,
|
|
|
|
render = render_option_meta,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path = 'runtime/doc/options.txt',
|
|
|
|
header = { '' },
|
|
|
|
from = 'A jump table for the options with a short description can be found at |Q_op|.',
|
|
|
|
footer = {
|
2023-12-04 13:38:31 -07:00
|
|
|
' vim:tw=78:ts=8:noet:ft=help:norl:',
|
2023-08-04 13:26:53 -07:00
|
|
|
},
|
|
|
|
funcs = get_option_meta,
|
|
|
|
render = render_option_doc,
|
2023-12-04 13:38:31 -07:00
|
|
|
},
|
2023-12-20 09:34:17 -07:00
|
|
|
{
|
|
|
|
path = 'runtime/lua/vim/_meta/vvars.lua',
|
|
|
|
header = LUA_VVAR_META_HEADER,
|
|
|
|
funcs = get_vvar_meta,
|
|
|
|
render = render_vvar_meta,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path = 'runtime/doc/vvars.txt',
|
|
|
|
header = { '' },
|
|
|
|
from = 'Type |gO| to see the table of contents.',
|
|
|
|
footer = {
|
|
|
|
' vim:tw=78:ts=8:noet:ft=help:norl:',
|
|
|
|
},
|
|
|
|
funcs = get_vvar_meta,
|
|
|
|
render = render_vvar_doc,
|
|
|
|
},
|
2023-07-28 06:48:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
--- @param elem nvim.gen_eval_files.elem
|
|
|
|
local function render(elem)
|
2023-12-04 13:38:31 -07:00
|
|
|
print('Rendering ' .. elem.path)
|
|
|
|
local from_lines = {} --- @type string[]
|
2023-08-04 13:26:53 -07:00
|
|
|
local from = elem.from
|
|
|
|
if from then
|
|
|
|
for line in io.lines(elem.path) do
|
2023-12-04 13:38:31 -07:00
|
|
|
from_lines[#from_lines + 1] = line
|
2023-08-04 13:26:53 -07:00
|
|
|
if line:match(from) then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-28 06:48:41 -07:00
|
|
|
local o = assert(io.open(elem.path, 'w'))
|
|
|
|
|
|
|
|
--- @param l string
|
|
|
|
local function write(l)
|
|
|
|
local l1 = l:gsub('%s+$', '')
|
|
|
|
o:write(l1)
|
|
|
|
o:write('\n')
|
|
|
|
end
|
|
|
|
|
2023-08-04 13:26:53 -07:00
|
|
|
for _, l in ipairs(from_lines) do
|
|
|
|
write(l)
|
|
|
|
end
|
|
|
|
|
2023-07-28 06:48:41 -07:00
|
|
|
for _, l in ipairs(elem.header or {}) do
|
|
|
|
write(l)
|
|
|
|
end
|
|
|
|
|
2023-08-01 06:20:44 -07:00
|
|
|
local funcs = elem.funcs()
|
|
|
|
|
2023-07-28 06:48:41 -07:00
|
|
|
--- @type string[]
|
|
|
|
local fnames = vim.tbl_keys(funcs)
|
|
|
|
table.sort(fnames)
|
|
|
|
|
|
|
|
for _, f in ipairs(fnames) do
|
|
|
|
elem.render(f, funcs[f], write)
|
|
|
|
end
|
|
|
|
|
|
|
|
for _, l in ipairs(elem.footer or {}) do
|
|
|
|
write(l)
|
|
|
|
end
|
|
|
|
|
|
|
|
o:close()
|
|
|
|
end
|
|
|
|
|
|
|
|
local function main()
|
|
|
|
for _, c in ipairs(CONFIG) do
|
|
|
|
render(c)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
main()
|