From 8cfb993fdfb4ec73c6c8a5ec8d9935f0f8439efd Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 13 Jun 2025 12:12:35 +0100 Subject: [PATCH] docs: support overloads and async --- runtime/doc/lua.txt | 9 ++++- runtime/doc/treesitter.txt | 3 ++ src/gen/gen_vimdoc.lua | 78 +++++++++++++++++++++++++++---------- src/gen/luacats_grammar.lua | 12 ++++-- src/gen/luacats_parser.lua | 14 +++++-- 5 files changed, 86 insertions(+), 30 deletions(-) diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index f7b4d80d17..cd13045105 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1543,6 +1543,9 @@ vim.deprecate({name}, {alternative}, {version}, {plugin}, {backtrace}) vim.inspect() *vim.inspect()* Gets a human-readable representation of the given object. + Overloads: ~ + • `fun(x: any, opts?: vim.inspect.Opts): string` + Return: ~ (`string`) @@ -2458,6 +2461,10 @@ vim.validate({name}, {value}, {validator}, {optional}, {message}) • {optional} (`boolean?`) Argument is optional (may be omitted) • {message} (`string?`) message when validation fails + Overloads: ~ + • `fun(name: string, val: any, validator: vim.validate.Validator, message: string)` + • `fun(spec: table)` + ============================================================================== Lua module: vim.loader *vim.loader* @@ -2709,8 +2716,6 @@ Rather than a |hit-enter-prompt|, messages shown in the cmdline area that do not fit are appended with a `[+x]` "spill" indicator, where `x` indicates the spilled lines. To see the full message, the |g<| command can be used. - - ============================================================================== Lua module: vim.filetype *vim.filetype* diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index ee71149ad6..3e92a42ad1 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -875,6 +875,9 @@ TSNode:range({include_bytes}) *TSNode:range()* Parameters: ~ • {include_bytes} (`false?`) + Overloads: ~ + • `fun(self: TSNode, include_bytes: true): integer, integer, integer, integer, integer, integer` + Return (multiple): ~ (`integer`) (`integer`) diff --git a/src/gen/gen_vimdoc.lua b/src/gen/gen_vimdoc.lua index 70365522a4..1abda64f65 100755 --- a/src/gen/gen_vimdoc.lua +++ b/src/gen/gen_vimdoc.lua @@ -796,6 +796,13 @@ local function render_fun(fun, classes, cfg) end end + if fun.overloads then + table.insert(ret, '\n Overloads: ~\n') + for _, p in ipairs(fun.overloads) do + table.insert(ret, (' • `%s`\n'):format(p)) + end + end + if fun.returns then local txt = render_returns(fun.returns, fun.generics, classes, cfg.exclude_types) if not txt:match('^%s*$') then @@ -887,14 +894,16 @@ end --- @field title string --- @field help_tag string --- @field funs_txt string ---- @field doc? string[] +--- @field classes_txt string +--- @field briefs string[] --- @param filename string --- @param cfg nvim.gen_vimdoc.Config ---- @param section_docs table +--- @param briefs string[] --- @param funs_txt string +--- @param classes_txt string --- @return nvim.gen_vimdoc.Section? -local function make_section(filename, cfg, section_docs, funs_txt) +local function make_section(filename, cfg, briefs, funs_txt, classes_txt) -- filename: e.g., 'autocmd.c' -- name: e.g. 'autocmd' local name = filename:match('(.*)%.[a-z]+') @@ -910,7 +919,7 @@ local function make_section(filename, cfg, section_docs, funs_txt) end local help_tags = '*' .. help_labels .. '*' - if funs_txt == '' and #section_docs == 0 then + if funs_txt == '' and classes_txt == '' and #briefs == 0 then return end @@ -919,7 +928,8 @@ local function make_section(filename, cfg, section_docs, funs_txt) title = cfg.section_fmt(sectname), help_tag = help_tags, funs_txt = funs_txt, - doc = section_docs, + classes_txt = classes_txt, + briefs = briefs, } end @@ -937,12 +947,24 @@ local function render_section(section, add_header) }) end - local sdoc = '\n\n' .. table.concat(section.doc or {}, '\n') - if sdoc:find('[^%s]') then - doc[#doc + 1] = sdoc + if next(section.briefs) then + local briefs_txt = {} --- @type string[] + for _, b in ipairs(section.briefs) do + briefs_txt[#briefs_txt + 1] = md_to_vimdoc(b, 0, 0, TEXT_WIDTH) + end + + local sdoc = '\n\n' .. table.concat(briefs_txt, '\n') + if sdoc:find('[^%s]') then + doc[#doc + 1] = sdoc + end end - if section.funs_txt then + if section.classes_txt ~= '' then + table.insert(doc, '\n\n') + table.insert(doc, (section.classes_txt:gsub('\n+$', '\n'))) + end + + if section.funs_txt ~= '' then table.insert(doc, '\n\n') table.insert(doc, section.funs_txt) end @@ -970,6 +992,17 @@ local function expand_files(files) end end +--- @param classes table +--- @return string? +local function find_module_class(classes, modvar) + for nm, cls in pairs(classes) do + local _, field = next(cls.fields or {}) + if cls.desc and field and field.classvar == modvar then + return nm + end + end +end + --- @param cfg nvim.gen_vimdoc.Config local function gen_target(cfg) cfg.fn_helptag_fmt = cfg.fn_helptag_fmt or fn_helptag_fmt_common @@ -998,21 +1031,26 @@ local function gen_target(cfg) for f, r in vim.spairs(file_results) do local classes, funs, briefs = r[1], r[2], r[3] - local briefs_txt = {} --- @type string[] - for _, b in ipairs(briefs) do - briefs_txt[#briefs_txt + 1] = md_to_vimdoc(b, 0, 0, TEXT_WIDTH) + local mod_cls_nm = find_module_class(classes, 'M') + if mod_cls_nm then + local mod_cls = classes[mod_cls_nm] + classes[mod_cls_nm] = nil + -- If the module documentation is present, add it to the briefs + -- so it appears at the top of the section. + briefs[#briefs + 1] = mod_cls.desc end + print(' Processing file:', f) - local funs_txt = render_funs(funs, all_classes, cfg) - if next(classes) then - local classes_txt = render_classes(classes, cfg) - if vim.trim(classes_txt) ~= '' then - funs_txt = classes_txt .. '\n' .. funs_txt - end - end + -- FIXME: Using f_base will confuse `_meta/protocol.lua` with `protocol.lua` local f_base = vim.fs.basename(f) - sections[f_base] = make_section(f_base, cfg, briefs_txt, funs_txt) + sections[f_base] = make_section( + f_base, + cfg, + briefs, + render_funs(funs, all_classes, cfg), + render_classes(classes, cfg) + ) end local first_section_tag = sections[cfg.section_order[1]].help_tag diff --git a/src/gen/luacats_grammar.lua b/src/gen/luacats_grammar.lua index 72d6d4fda4..f382ec89e1 100644 --- a/src/gen/luacats_grammar.lua +++ b/src/gen/luacats_grammar.lua @@ -145,7 +145,10 @@ local typedef = P({ type = v.ty * rep_array_opt_postfix * rep(Pf('|') * v.ty * rep_array_opt_postfix), ty = v.composite + paren(v.typedef), - composite = (v.types * array_postfix) + (v.types * opt_postfix) + v.types, + composite = (v.types * array_postfix) + + (v.types * opt_postfix) + + (P(ty_ident) * P('...')) -- Generic vararg + + v.types, types = v.generics + v.kv_table + v.tuple + v.dict + v.table_literal + v.fun + ty_prims, tuple = Pf('[') * comma1(v.type) * Plf(']'), @@ -154,11 +157,11 @@ local typedef = P({ table_literal = Pf('{') * comma1(opt_ident * Pf(':') * v.type) * Plf('}'), fun_param = (opt_ident + ellipsis) * opt(colon * v.type), fun_ret = v.type + (ellipsis * opt(colon * v.type)), - fun = Pf('fun') * paren(comma(v.fun_param)) * opt(Pf(':') * comma1(v.fun_ret)), + fun = opt(Pf('async')) * Pf('fun') * paren(comma(v.fun_param)) * opt(Pf(':') * comma1(v.fun_ret)), generics = P(ty_ident) * Pf('<') * comma1(v.type) * Plf('>'), }) / function(match) - return vim.trim(match):gsub('^%((.*)%)$', '%1'):gsub('%?+', '?') -end + return vim.trim(match):gsub('^%((.*)%)$', '%1'):gsub('%?+', '?') + end local access = P('private') + P('protected') + P('package') local caccess = Cg(access, 'access') @@ -184,6 +187,7 @@ local grammar = P { + annot('operator', ty_name * opt(paren(Cg(v.ctype, 'argtype'))) * colon * v.ctype) + annot(access) + annot('deprecated') + + annot('async') + annot('alias', ty_name * opt(ws * v.ctype)) + annot('enum', ty_name) + annot('overload', v.ctype) diff --git a/src/gen/luacats_parser.lua b/src/gen/luacats_parser.lua index cfc3c3b022..31c02efbda 100644 --- a/src/gen/luacats_parser.lua +++ b/src/gen/luacats_parser.lua @@ -22,6 +22,7 @@ local luacats_grammar = require('gen.luacats_grammar') --- @class nvim.luacats.parser.fun --- @field name string --- @field params nvim.luacats.parser.param[] +--- @field overloads string[] --- @field returns nvim.luacats.parser.return[] --- @field desc string --- @field access? 'private'|'package'|'protected' @@ -30,6 +31,7 @@ local luacats_grammar = require('gen.luacats_grammar') --- @field modvar? string --- @field classvar? string --- @field deprecated? true +--- @field async? true --- @field since? string --- @field attrs? string[] --- @field nodoc? true @@ -224,6 +226,11 @@ local function process_doc_line(line, state) elseif kind == 'enum' then -- TODO state.doc_lines = nil + elseif kind == 'async' then + cur_obj.async = true + elseif kind == 'overload' then + cur_obj.overloads = cur_obj.overloads or {} + table.insert(cur_obj.overloads, parsed.type) elseif vim.tbl_contains({ 'diagnostic', @@ -263,11 +270,13 @@ local function fun2field(fun) end return { + kind = 'field', name = fun.name, type = table.concat(parts, ''), access = fun.access, desc = fun.desc, nodoc = fun.nodoc, + classvar = fun.classvar, } end @@ -325,10 +334,7 @@ local function process_lua_line(line, state, classes, classvars, has_indent) end -- Add method as the field to the class - local cls = classes[class] - local field = fun2field(cur_obj) - field.classvar = cur_obj.classvar - table.insert(cls.fields, field) + table.insert(classes[class].fields, fun2field(cur_obj)) return end