From 42333ea98dfcd2994ee128a3467dfe68205154cd Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 28 Jul 2023 14:48:41 +0100 Subject: feat(docs): generate builtin.txt (#24493) - eval.lua is now the source of truth. - Formatting is much more consistent. - Fixed Lua type generation for polymorphic functions (get(), etc). - Removed "Overview" section from builtin.txt - Can generate this if we really want it. - Moved functions from sign.txt and testing.txt into builtin.txt. - Removed the *timer* *timers* tags since libuv timers via vim.uv should be preferred. - Removed the temp-file-name tag from tempname() - Moved lueval() from lua.txt to builtin.txt. * Fix indent * fixup! * fixup! fixup! * fixup! better tag formatting * fixup: revert changes no longer needed * fixup! CI --------- Co-authored-by: zeertzjq --- scripts/gen_eval_files.lua | 274 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100755 scripts/gen_eval_files.lua (limited to 'scripts/gen_eval_files.lua') diff --git a/scripts/gen_eval_files.lua b/scripts/gen_eval_files.lua new file mode 100755 index 0000000000..7193346758 --- /dev/null +++ b/scripts/gen_eval_files.lua @@ -0,0 +1,274 @@ +#!/usr/bin/env -S nvim -l +-- Generator for src/nvim/eval.lua + +local funcs = require('src/nvim/eval').funcs + +local LUA_KEYWORDS = { + ['and'] = true, + ['end'] = true, + ['function'] = true, + ['or'] = true, + ['if'] = true, + ['while'] = true, + ['repeat'] = true, +} + +--- @param f string +--- @param params {[1]:string,[2]:string}[]|true +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} + 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 +--- @param params {[1]:string,[2]:string}[] +--- @return {[1]:string,[2]:string}[] +local function process_params(params) + local seen = {} --- @type table + 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 + +--- @param f string +--- @param fun vim.EvalFn +--- @param write fun(line: string) +local function render_vimfn(f, fun, write) + if fun.lua == false then + return + end + + local funname = fun.name or f + + local params = process_params(fun.params) + + if fun.signature then + write('') + if fun.deprecated then + write('--- @deprecated') + end + + local desc = fun.desc + + if desc then + desc = desc:gsub('\n%s*\n%s*$', '\n') + for _, l in ipairs(vim.split(desc, '\n', { plain = true })) do + l = l:gsub('^ ', ''):gsub('\t', ' '):gsub('@', '\\@') + write('--- ' .. l) + end + end + + local req_args = type(fun.args) == 'table' and fun.args[1] or fun.args or 0 + + 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 + + if fun.returns ~= false then + write('--- @return ' .. (fun.returns or 'any')) + end + + write(render_fun_sig(funname, params)) + + return + end + + print('no doc for', funname) +end + +--- @type table +local rendered_tags = {} + +--- @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 + 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 + if rendered_tags[name] then + write(fun.signature) + else + if siglen + #tag > 80 then + write(string.rep('\t', 6) .. tag) + write(fun.signature) + else + local tt = math.max(1, (76 - siglen - #tag) / 8) + write(string.format('%s%s%s', fun.signature, string.rep('\t', tt), tag)) + end + end + rendered_tags[name] = true + + desc = vim.trim(desc) + local desc_l = vim.split(desc, '\n', { plain = true }) + for _, l in ipairs(desc_l) do + l = l:gsub('^ ', '') + if vim.startswith(l, '<') and not l:match('^<[A-Z][A-Z]') then + write('<\t\t' .. l:sub(2)) + else + write('\t\t' .. l) + end + end + + if #desc_l > 0 and not desc_l[#desc_l]:match('^', + '\t:let a = "aaaa\\nxxxx"', + '\t:echo matchstr(a, "..\\n..")', + '\taa', + '\txx', + '\t:echo matchstr(a, "a.x")', + '\ta', + '\tx', + '', + '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:', + }, + }, +} + +--- @param elem nvim.gen_eval_files.elem +local function render(elem) + 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 + + for _, l in ipairs(elem.header or {}) do + write(l) + end + + --- @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() -- cgit