Conversation
The markdown generator ignored the parameter and return-value documentation that the HTML generator renders as a dedicated "Parameters" / "Returns" table: - for a function whose type is a `Signature`, `render_signature_type` appended raw `@param `name` - description` / `@return - description` lines to the signature; - for a function member declared with `---@field name fun(...)` (`DocFunction`) the `@param` descriptions were not attached to the member and were dropped completely. Add `MemberParam` rows to `MemberDoc` and fill them from the function type in `typ_gen` / `mod_gen`, mirroring the HTML generator's `function_details_html`, and render a `**Parameters**` / `**Returns**` block from the markdown templates. Drop the raw `@param` / `@return` lines from `render_signature_type`. Add an integration test covering a documented function member. Needed for luafun/luafun#100
The HTML generator renders `---@overload` declarations in a dedicated "Overloads" block (`signature_overloads_html`), but the markdown generator ignored them completely: `MemberDoc` had no `overloads` field and the templates had no block for it. Add `MemberDoc.overloads`, fill it from the function signature in `typ_gen` / `mod_gen` (mirroring the HTML generator) and render a `**Overloads**` block from the markdown templates. Add an integration test covering a function with an `---@overload`. Needed for luafun/luafun#100
There was a problem hiding this comment.
Code Review
Overall this is a clean, well-structured change that mirrors the HTML generator's behavior in the markdown generator. The Default derive + ..Default::default() pattern is idiomatic, and the new tests are a good addition. A few issues worth addressing:
1. function_details_md silently drops undocumented params/returns (behavioral concern)
In render.rs, the function returns None unless at least one param/return has a description:
let has_description = params.iter().any(|p| p.description.is_some())
|| returns.iter().any(|r| r.description.is_some());
has_description.then_some((params, returns))This means a function with documented types but no descriptions (e.g. ---@param a number with no @text) will render no Parameters/Returns block at all. That's a regression relative to the previous behavior, which at least rendered the signature. If the intent is only to suppress empty tables, consider returning the rows whenever there is at least one param or return, and letting the template decide. At minimum, document this trade-off explicitly (the doc comment mentions it, but the consequence — losing type-only params — isn't obvious).
2. render_param_type can return None for a valid empty render, but returns in the DocFunction branch is unconditional
In the DocFunction branch:
let returns = vec![MemberParam {
name: String::new(),
type_text: Some(render_typ(db, func.get_ret(), RenderLevel::Documentation)),
description: None,
}];Unlike render_param_type, this does not guard against an empty string. If render_typ returns "", you'll get type_text: Some(""), and the template will emit (** **) — an empty italic span. Use render_param_type(db, Some(func.get_ret())) here for consistency.
3. function_overloads_md ignores the DocFunction case
function_details_md handles both LuaType::Signature and LuaType::DocFunction, but function_overloads_md only handles Signature:
let LuaType::Signature(signature_id) = typ else {
return Vec::new();
};This is probably fine (overloads only exist on signatures), but the asymmetry is worth a comment, since a reader may expect parity with the sibling function.
4. Template duplication
The Parameters/Returns/Overloads block is copy-pasted verbatim across four locations (lua_module_template.tl ×2, lua_type_template.tl ×2). Consider extracting it into a Tera macro/include (e.g. {% include "member_details.tl" %}) to avoid drift when the markup changes.
5. Minor: MemberParam derives
MemberParam derives Clone but MemberDoc does not. If MemberDoc is ever cloned (e.g. for the index), the inconsistency will bite. Not blocking, but worth aligning.
6. Test robustness
Both new tests assert pages.len() == 1 and read pages[0]. This is fine for the fixture, but the filter_map(|entry| entry.ok()) silently swallows IO errors — a read_dir failure on one entry would just reduce the count and produce a confusing assertion failure. Prefer entry.unwrap() or propagate the error.
7. Test duplication
markdown_overloads.rs and markdown_params.rs share ~40 lines of identical fixture/runner code. Consider a shared tests/common/mod.rs helper.
Positive notes
- Removing the raw
@param/@returntext dump fromrender_signature_typeis the right call and is covered by the new assertion. skip_serializing_if = "Vec::is_empty"keeps the JSON index clean.- Doc comments on the new fields are helpful.
Needed for luafun/luafun#100