fix(module_doc): named parameter table detection
This commit is contained in:
parent
b58384e65b
commit
c2e0da3ba6
|
@ -447,131 +447,310 @@ describe("Scrap documentation", function()
|
|||
assert.same(expected, ast)
|
||||
end)
|
||||
|
||||
-- TODO : Fix the code then come back to this test, the current implementation is incomplete
|
||||
-- it("should produce a Record node when the function parameter is a table", function()
|
||||
-- local ast <const> = get_doc_from_page([[
|
||||
-- <h2 class="section-header">
|
||||
-- <a name="Static_module_functions"></a>Static module functions
|
||||
-- </h2>
|
||||
-- <dl class="function">
|
||||
-- <dt>
|
||||
-- <a
|
||||
-- class="copy-link js-copy-link"
|
||||
-- name="awful.screen.focused"
|
||||
-- href="#awful.screen.focused"
|
||||
-- >🔗</a
|
||||
-- >
|
||||
-- <strong
|
||||
-- >awful.screen.focused
|
||||
-- <span class="function_named_args"><b>{</b>[args]<b>}</b></span></strong
|
||||
-- >
|
||||
-- <span class="proptype"
|
||||
-- ><span class="summary_type"> -> nil <i>or</i> screen</span></span
|
||||
-- >
|
||||
-- <span class="baseclass"> </span>
|
||||
-- </dt>
|
||||
-- <dd>
|
||||
-- <h3>Parameters:</h3>
|
||||
-- <table class="see_also">
|
||||
-- <tbody>
|
||||
-- <tr class="param_header">
|
||||
-- <th>Name</th>
|
||||
-- <th></th>
|
||||
-- <th>Type(s)</th>
|
||||
-- <th>Description</th>
|
||||
-- <th>Default value</th>
|
||||
-- </tr>
|
||||
-- <tr>
|
||||
-- <td><span class="parameter">args</span></td>
|
||||
-- <td><span class="chips">Optional</span></td>
|
||||
-- <td>
|
||||
-- <span class="types"><span class="type">table</span></span>
|
||||
-- </td>
|
||||
-- <td class="see_also_description"></td>
|
||||
-- <td><span class="not_applicable">Undefined</span></td>
|
||||
-- </tr>
|
||||
-- <tr class="see_also_sublist">
|
||||
-- <td><span class="parameter">client</span></td>
|
||||
-- <td><span class="chips">Optional</span></td>
|
||||
-- <td>
|
||||
-- <span class="types"><span class="type">boolean</span></span>
|
||||
-- </td>
|
||||
-- <td class="see_also_description">
|
||||
-- Use the client screen instead of the mouse screen.
|
||||
-- </td>
|
||||
-- <td>
|
||||
-- <span class="default_value"><code>false</code></span>
|
||||
-- </td>
|
||||
-- </tr>
|
||||
-- <tr class="see_also_sublist">
|
||||
-- <td><span class="parameter">mouse</span></td>
|
||||
-- <td><span class="chips">Optional</span></td>
|
||||
-- <td>
|
||||
-- <span class="types"><span class="type">boolean</span></span>
|
||||
-- </td>
|
||||
-- <td class="see_also_description">Use the mouse screen</td>
|
||||
-- <td>
|
||||
-- <span class="default_value"><code>true</code></span>
|
||||
-- </td>
|
||||
-- </tr>
|
||||
-- </tbody>
|
||||
-- </table>
|
||||
-- <h3>Returns:</h3>
|
||||
-- <ol>
|
||||
-- <span class="types"
|
||||
-- >optional
|
||||
-- <a class="type" href="../core_components/screen.html#screen"
|
||||
-- >screen</a
|
||||
-- ></span
|
||||
-- >
|
||||
-- The focused screen object, or
|
||||
-- <code>nil</code>
|
||||
-- in case no screen is present currently.
|
||||
-- </ol>
|
||||
-- </dd>
|
||||
-- </dl>
|
||||
-- ]], "awful.screen")
|
||||
-- print(require("inspect")(ast))
|
||||
-- assert.same(ast, {
|
||||
-- children = {
|
||||
-- {
|
||||
-- children = {},
|
||||
-- name = "Signal",
|
||||
-- token = "enum",
|
||||
-- },
|
||||
-- {
|
||||
-- parameters = {
|
||||
-- {
|
||||
-- children = {
|
||||
-- {
|
||||
-- types = { "boolean" },
|
||||
-- name = "client",
|
||||
-- token = "variable",
|
||||
-- },
|
||||
-- {
|
||||
-- types = { "boolean" },
|
||||
-- name = "mouse",
|
||||
-- token = "variable",
|
||||
-- },
|
||||
-- },
|
||||
-- name = "Args",
|
||||
-- token = "record",
|
||||
-- },
|
||||
-- {
|
||||
-- types = { "Focused_Args" },
|
||||
-- name = "args",
|
||||
-- token = "variable",
|
||||
-- },
|
||||
-- },
|
||||
-- return_types = { "screen" },
|
||||
-- name = "focused",
|
||||
-- token = "function",
|
||||
-- },
|
||||
-- },
|
||||
-- name = "Screen",
|
||||
-- token = "module",
|
||||
-- })
|
||||
-- end)
|
||||
it("should produce a Record node when a function parameter is a named-parameter-table", function()
|
||||
local ast <const> = get_doc_from_page([[
|
||||
<h2 class="section-header">
|
||||
<a name="Static_module_functions"></a>Static module functions
|
||||
</h2>
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a
|
||||
class="copy-link js-copy-link"
|
||||
name="awful.screen.focused"
|
||||
href="#awful.screen.focused"
|
||||
>🔗</a
|
||||
>
|
||||
<strong
|
||||
>awful.screen.focused
|
||||
<span class="function_named_args"><b>{</b>[args]<b>}</b></span></strong
|
||||
>
|
||||
<span class="proptype"
|
||||
><span class="summary_type"> -> nil <i>or</i> screen</span></span
|
||||
>
|
||||
<span class="baseclass"> </span>
|
||||
</dt>
|
||||
<dd>
|
||||
<h3>Parameters:</h3>
|
||||
<table class="see_also">
|
||||
<tbody>
|
||||
<tr class="param_header">
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
<th>Type(s)</th>
|
||||
<th>Description</th>
|
||||
<th>Default value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="parameter">args</span></td>
|
||||
<td><span class="chips">Optional</span></td>
|
||||
<td>
|
||||
<span class="types"><span class="type">table</span></span>
|
||||
</td>
|
||||
<td class="see_also_description"></td>
|
||||
<td><span class="not_applicable">Undefined</span></td>
|
||||
</tr>
|
||||
<tr class="see_also_sublist">
|
||||
<td><span class="parameter">client</span></td>
|
||||
<td><span class="chips">Optional</span></td>
|
||||
<td>
|
||||
<span class="types"><span class="type">boolean</span></span>
|
||||
</td>
|
||||
<td class="see_also_description">
|
||||
Use the client screen instead of the mouse screen.
|
||||
</td>
|
||||
<td>
|
||||
<span class="default_value"><code>false</code></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="see_also_sublist">
|
||||
<td><span class="parameter">mouse</span></td>
|
||||
<td><span class="chips">Optional</span></td>
|
||||
<td>
|
||||
<span class="types"><span class="type">boolean</span></span>
|
||||
</td>
|
||||
<td class="see_also_description">Use the mouse screen</td>
|
||||
<td>
|
||||
<span class="default_value"><code>true</code></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
<span class="types"
|
||||
>optional
|
||||
<a class="type" href="../core_components/screen.html#screen"
|
||||
>screen</a
|
||||
></span
|
||||
>
|
||||
The focused screen object, or
|
||||
<code>nil</code>
|
||||
in case no screen is present currently.
|
||||
</ol>
|
||||
</dd>
|
||||
</dl>
|
||||
]], "awful.screen")
|
||||
assert.same(ast, {
|
||||
children = {
|
||||
{
|
||||
children = {},
|
||||
name = "Signal",
|
||||
token = "enum",
|
||||
},
|
||||
{
|
||||
children = {
|
||||
{
|
||||
types = { "boolean" },
|
||||
name = "client",
|
||||
token = "variable",
|
||||
},
|
||||
{
|
||||
types = { "boolean" },
|
||||
name = "mouse",
|
||||
token = "variable",
|
||||
}
|
||||
},
|
||||
name = "Focused_Args",
|
||||
token = "record",
|
||||
},
|
||||
{
|
||||
parameters = {
|
||||
{
|
||||
types = { "Focused_Args" },
|
||||
name = "args",
|
||||
token = "variable",
|
||||
},
|
||||
},
|
||||
return_types = { "screen" },
|
||||
name = "focused",
|
||||
token = "function",
|
||||
},
|
||||
},
|
||||
name = "Screen",
|
||||
token = "module",
|
||||
})
|
||||
end)
|
||||
|
||||
it("should go back to a table typed parameter when the record is empty", function()
|
||||
local ast <const> = get_doc_from_page([[
|
||||
<h2 class="section-header "><a name="Static_module_functions"></a>Static module functions</h2>
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a class="copy-link js-copy-link" name="crush" href="#crush">🔗</a>
|
||||
<strong><span class="function_modname">gears.table.</span>crush <span class="function_args"> <b>(</b>target, source, <span class="optional_param">raw</span><b>)</b></span></strong>
|
||||
<span class="proptype"><span class="summary_type"> -> table</span></span>
|
||||
<span class="baseclass">
|
||||
</span>
|
||||
</dt>
|
||||
<dd>
|
||||
</p><h3>Parameters:</h3>
|
||||
<table class="see_also">
|
||||
<tbody><tr class="param_header">
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
<th>Type(s)</th>
|
||||
<th>Description</th>
|
||||
<th>Default value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="parameter">target</span></td>
|
||||
<td></td>
|
||||
<td><span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.6" target="_blank">table</a></span></td>
|
||||
<td class="see_also_description"> The target table. Values from <code>source</code> will be copied
|
||||
into this table.</td>
|
||||
<td><span class="not_applicable" title="This parameter is mandatory">Not applicable</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="parameter">source</span></td>
|
||||
<td></td>
|
||||
<td><span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.6" target="_blank">table</a></span></td>
|
||||
<td class="see_also_description"> The source table. Its values will be copied into
|
||||
<code>target</code>.</td>
|
||||
<td><span class="not_applicable" title="This parameter is mandatory">Not applicable</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="parameter">raw</span></td>
|
||||
<td><span class="chips">Optional</span></td>
|
||||
<td><span class="types"><span class="type">bool</span></span></td>
|
||||
<td class="see_also_description"> If <code>true</code>, values will be assigned with <a href="https://www.lua.org/manual/5.3/manual.html#pdf-rawset" target="_blank">rawset</a>.
|
||||
This will bypass metamethods on <code>target</code>.</td>
|
||||
<td><span class="default_value"><code>false</code></span></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.6" target="_blank">table</a></span>
|
||||
The target table.
|
||||
</ol>
|
||||
</dd>
|
||||
</dl>
|
||||
]], "gears.table")
|
||||
local expected <const>: Node = {
|
||||
children = {
|
||||
{
|
||||
children = {},
|
||||
name = "Signal",
|
||||
token = "enum",
|
||||
},
|
||||
{
|
||||
parameters = {
|
||||
{
|
||||
types = { "table" },
|
||||
name = "target",
|
||||
token = "variable",
|
||||
},
|
||||
{
|
||||
types = { "table" },
|
||||
name = "source",
|
||||
token = "variable",
|
||||
},
|
||||
{
|
||||
types = { "bool" },
|
||||
name = "raw",
|
||||
token = "variable",
|
||||
}
|
||||
},
|
||||
return_types = { "table" },
|
||||
name = "crush",
|
||||
token = "function",
|
||||
}
|
||||
},
|
||||
name = "Table",
|
||||
token = "module",
|
||||
}
|
||||
assert.same(expected, ast)
|
||||
end)
|
||||
|
||||
it("should go back to a table typed parameter when the record is empty and it's the last parameter", function()
|
||||
local ast <const> = get_doc_from_page([[
|
||||
<h2 class="section-header"><a name="Object_methods"></a>Object methods</h2>
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a class="copy-link js-copy-link" name="tags" href="#tags">🔗</a>
|
||||
<strong
|
||||
>:tags
|
||||
<span class="function_args"> <b>(</b>tags_table<b>)</b></span></strong
|
||||
>
|
||||
<span class="proptype"
|
||||
><span class="summary_type"> -> table</span></span
|
||||
>
|
||||
<span class="baseclass"> · 1 signal </span>
|
||||
</dt>
|
||||
<dd>
|
||||
<h3>Parameters:</h3>
|
||||
<table class="see_also">
|
||||
<tbody>
|
||||
<tr class="param_header">
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
<th>Type(s)</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="parameter">tags_table</span></td>
|
||||
<td></td>
|
||||
<td>
|
||||
<span class="types"
|
||||
><a
|
||||
class="type"
|
||||
href="https://www.lua.org/manual/5.3/manual.html#6.6"
|
||||
target="_blank"
|
||||
>table</a
|
||||
></span
|
||||
>
|
||||
</td>
|
||||
<td class="see_also_description">
|
||||
A table with tags to set, or <code>nil</code> to get the current
|
||||
tags.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
<span class="types"
|
||||
><a
|
||||
class="type"
|
||||
href="https://www.lua.org/manual/5.3/manual.html#6.6"
|
||||
target="_blank"
|
||||
>table</a
|
||||
></span
|
||||
>
|
||||
A table with all tags.
|
||||
</ol>
|
||||
</dd>
|
||||
</dl>
|
||||
]], "awful.client")
|
||||
local expected <const>: Node = {
|
||||
children = {
|
||||
{
|
||||
children = {},
|
||||
name = "Signal",
|
||||
token = "enum",
|
||||
},
|
||||
{
|
||||
parameters = {
|
||||
{
|
||||
types = { "Client" },
|
||||
name = "self",
|
||||
token = "variable",
|
||||
},
|
||||
{
|
||||
types = { "table" },
|
||||
name = "tags_table",
|
||||
token = "variable",
|
||||
},
|
||||
},
|
||||
return_types = { "table" },
|
||||
name = "tags",
|
||||
token = "function",
|
||||
},
|
||||
},
|
||||
name = "Client",
|
||||
token = "module",
|
||||
}
|
||||
assert.same(expected, ast)
|
||||
end)
|
||||
|
||||
it("should return Function nodes with the `other_nodes` list when the function module name doesn't match the module name", function()
|
||||
local ast <const>, other_nodes <const> = get_doc_from_page([[
|
||||
|
|
|
@ -34,45 +34,63 @@ local function extract_item_name(item_name_node: scan.HTMLNode): string, string
|
|||
return utils.sanitize_string(name), module_name and utils.sanitize_string(module_name) or nil
|
||||
end
|
||||
|
||||
local function extract_function_parameters(table_html: string): { Node }
|
||||
local current_record_parameter: Node = nil
|
||||
local function extract_function_parameters(table_html: string, function_name: string): { Node }, { Node }
|
||||
local parameters_types <const>: { Node } = {}
|
||||
local is_populating_parameters_types: boolean = false
|
||||
local current_field: Node = nil
|
||||
|
||||
return scraper_utils.scrape(table_html, "tr", function(tr: scan.HTMLNode): Node
|
||||
local tr_html = tr:outer_html()
|
||||
local name_node <const> = scraper_utils.find(tr_html, "span.parameter")[1]
|
||||
local types_node <const> = scraper_utils.find(tr_html, "span.types")[1]
|
||||
if not name_node or not types_node then
|
||||
return nil
|
||||
end
|
||||
local parameters <const> = scraper_utils.scrape(table_html, "tr", function(tr: scan.HTMLNode): Node
|
||||
local tr_html = tr:outer_html()
|
||||
local name_node <const> = scraper_utils.find(tr_html, "span.parameter")[1]
|
||||
local types_node <const> = scraper_utils.find(tr_html, "span.types")[1]
|
||||
if not name_node or not types_node then
|
||||
return nil
|
||||
end
|
||||
|
||||
local name <const> = extract_node_text(name_node)
|
||||
local types <const> = parse_parameter_types(extract_node_text(types_node))
|
||||
|
||||
if tr.attr ~= nil and tr.attr.class == "see_also_sublist" and current_record_parameter then
|
||||
local field = ast.create_node("variable", name)
|
||||
field.types = types
|
||||
table.insert(current_record_parameter.children, field)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- We wrongly tried to convert a table to a record
|
||||
if current_record_parameter then
|
||||
current_record_parameter.token = "variable"
|
||||
current_record_parameter.name = utils.lowercase(current_record_parameter.name)
|
||||
current_record_parameter.types = { "table" }
|
||||
current_record_parameter.children = nil
|
||||
current_record_parameter = nil
|
||||
end
|
||||
|
||||
if #types == 1 and types[1] == "table" then
|
||||
current_record_parameter = ast.create_node("record", utils.capitalize(name))
|
||||
return current_record_parameter
|
||||
end
|
||||
local name <const> = extract_node_text(name_node)
|
||||
local types <const> = parse_parameter_types(extract_node_text(types_node))
|
||||
|
||||
-- Add a field to the current parameter type record
|
||||
if tr.attr ~= nil and tr.attr.class == "see_also_sublist" and is_populating_parameters_types then
|
||||
local field = ast.create_node("variable", name)
|
||||
field.types = types
|
||||
return field
|
||||
end)
|
||||
table.insert(parameters_types[#parameters_types].children, field)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Still here and we are populating the parameter type record ?
|
||||
-- Then oops, we wrongly tried to convert a table to a record
|
||||
if is_populating_parameters_types then
|
||||
table.remove(parameters_types, #parameters_types)
|
||||
is_populating_parameters_types = false
|
||||
current_field.types = { "table" }
|
||||
end
|
||||
|
||||
-- Otherwise, add a new parameter
|
||||
local field = ast.create_node("variable", name)
|
||||
field.types = types
|
||||
current_field = field
|
||||
|
||||
-- If the parameter is a table, then we try to convert it to a record
|
||||
if #types == 1 and types[1] == "table" then
|
||||
local record_name <const> = string.format(
|
||||
"%s_%s",
|
||||
utils.capitalize(function_name),
|
||||
utils.capitalize(name))
|
||||
table.insert(parameters_types, ast.create_node("record", record_name))
|
||||
is_populating_parameters_types = true
|
||||
field.types = { record_name }
|
||||
end
|
||||
|
||||
return field
|
||||
end)
|
||||
|
||||
if is_populating_parameters_types and #parameters_types[#parameters_types].children == 0 then
|
||||
table.remove(parameters_types, #parameters_types)
|
||||
current_field.types = { "table" }
|
||||
end
|
||||
|
||||
return parameters, parameters_types
|
||||
end
|
||||
|
||||
local function extract_function_return_types(ol_html: string): { string }
|
||||
|
@ -109,9 +127,11 @@ local function extract_section_functions(dl: string, module_name: string | nil):
|
|||
local body_html = nodes[list_query_selectors.body]:outer_html()
|
||||
|
||||
local parameter_node = scraper_utils.find(body_html, "table")[1]
|
||||
function_node.parameters = parameter_node and
|
||||
extract_function_parameters(parameter_node:outer_html()) or
|
||||
{}
|
||||
local parameters, parameters_types: { Node }, { Node } = {}, {}
|
||||
if parameter_node then
|
||||
parameters, parameters_types = extract_function_parameters(parameter_node:outer_html(), function_name)
|
||||
end
|
||||
function_node.parameters = parameters
|
||||
|
||||
local return_node = scraper_utils.find(body_html, "ol")[1]
|
||||
function_node.return_types = return_node and
|
||||
|
@ -120,8 +140,10 @@ local function extract_section_functions(dl: string, module_name: string | nil):
|
|||
|
||||
if module_name and function_module_name and module_name ~= function_module_name then
|
||||
function_node.name = function_module_name .. "." .. function_node.name
|
||||
utils.spread(other_functions, parameters_types)
|
||||
table.insert(other_functions, function_node)
|
||||
else
|
||||
utils.spread(functions, parameters_types)
|
||||
table.insert(functions, function_node)
|
||||
end
|
||||
end
|
||||
|
@ -203,9 +225,11 @@ local section_scrapers <total>: { Section : function(html: string, record_name:
|
|||
["Object methods"] = function(html: string, record_name: string): { Node }, { Node }, { string }
|
||||
local methods <const> = extract_section_functions(html)
|
||||
for _, method in ipairs(methods) do
|
||||
local self_parameter = ast.create_node("variable", "self")
|
||||
self_parameter.types = { record_name }
|
||||
table.insert(method.parameters, 1, self_parameter)
|
||||
if method.token == "function" then
|
||||
local self_parameter = ast.create_node("variable", "self")
|
||||
self_parameter.types = { record_name }
|
||||
table.insert(method.parameters, 1, self_parameter)
|
||||
end
|
||||
end
|
||||
return methods, {}, {}
|
||||
end,
|
||||
|
|
|
@ -15,15 +15,14 @@ function scraper_utils.scrape<T>(html: string, query_selector: string, extract_c
|
|||
scanner.scan_html(html, function(stack: scan.NodeStack)
|
||||
if stack:is(query_selector) then
|
||||
local node = stack:current()
|
||||
local success, info_or_error = pcall(extract_callback, node)
|
||||
local success, ret_or_error = pcall(extract_callback, node)
|
||||
|
||||
if not success then
|
||||
local error_message = info_or_error as string
|
||||
log:error(logger.message_with_metadata("Extraction error", { error = error_message }))
|
||||
else
|
||||
local info = info_or_error as T
|
||||
table.insert(ret, info)
|
||||
log:error(logger.message_with_metadata("Extraction error", { error = ret_or_error as string }))
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(ret, ret_or_error as T)
|
||||
end
|
||||
end)
|
||||
|
||||
|
|
|
@ -76,4 +76,10 @@ function utils.do_or_fail<T>(func: function<T>(...: any): (T | nil, string), ...
|
|||
return res as T -- promote to T since pcall succeeded at this point
|
||||
end
|
||||
|
||||
function utils.spread<T>(t: { T }, i: { T })
|
||||
for _, v in ipairs(i) do
|
||||
table.insert(t, v)
|
||||
end
|
||||
end
|
||||
|
||||
return utils
|
||||
|
|
Loading…
Reference in New Issue