Skip to content

Commit

Permalink
Add @omitFromAutoModule and @summaryLink tags to control autodoc outp…
Browse files Browse the repository at this point in the history
…ut (#178)
  • Loading branch information
hoodmane authored Dec 31, 2024
1 parent 29a1701 commit 781dc61
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 49 deletions.
4 changes: 2 additions & 2 deletions sphinx_js/js/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ async function makeApp(args: string[]): Promise<Application> {
throw new ExitError(ExitCodes.Ok);
}
app.extraData = {};
app.options.getValue("modifierTags").push("@hidetype");
app.options.getValue("blockTags").push("@destructure");
app.options.getValue("modifierTags").push("@hidetype", "@omitFromAutoModule");
app.options.getValue("blockTags").push("@destructure", "@summaryLink");
return app;
}

Expand Down
42 changes: 21 additions & 21 deletions sphinx_js/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,11 @@ def get_object(self) -> Module: # type:ignore[override]
return analyzer._modules_by_path.get(self._partial_path)

def rst_for_group(self, objects: Iterable[TopLevel]) -> list[str]:
return [self.rst_for(obj) for obj in objects]
return [
self.rst_for(obj)
for obj in objects
if "@omitFromAutoModule" not in obj.modifier_tags
]

def rst( # type:ignore[override]
self,
Expand All @@ -722,7 +726,7 @@ def rst( # type:ignore[override]
rst.append([f".. js:module:: {''.join(partial_path)}"])
for group_name in _SECTION_ORDER:
rst.append(self.rst_for_group(getattr(obj, group_name)))
return "\n\n".join(["\n\n".join(r) for r in rst])
return "\n\n".join(["\n\n".join(r) for r in rst if r])


class AutoSummaryRenderer(Renderer):
Expand Down Expand Up @@ -787,26 +791,27 @@ def get_sig(self, obj: TopLevel) -> str:
else:
return ""

def get_summary_row(
self, pkgname: str, obj: TopLevel
) -> tuple[str, str, str, str, str, str]:
def get_summary_row(self, pkgname: str, obj: TopLevel) -> tuple[str, str, str]:
"""Get the summary table row for obj.
The output is designed to be input to format_table. The link name
needs to be set up so that :any:`link_name` makes a link to the
actual API docs for this object.
"""
sig = self.get_sig(obj)
display_name = obj.name
prefix = "**async** " if getattr(obj, "is_async", False) else ""
qualifier = "any"
summary = self.extract_summary(render_description(obj.description))
link_name = pkgname + "." + display_name
return (prefix, qualifier, display_name, sig, summary, link_name)
main = f"{prefix}:{qualifier}:`{display_name} <{link_name}>`"
if slink := obj.block_tags.get("summaryLink"):
main = render_description(slink[0])
sig = self.get_sig(obj)
summary = self.extract_summary(render_description(obj.description))
return (main, sig, summary)

def get_summary_table(
self, pkgname: str, group: Iterable[TopLevel]
) -> list[tuple[str, str, str, str, str, str]]:
) -> list[tuple[str, str, str]]:
"""Get the data for a summary tget_summary_tableable. Return value
is set up to be an argument of format_table.
"""
Expand All @@ -818,9 +823,7 @@ def get_summary_table(
# We have to change the value of one string: qualifier = 'obj ==>
# qualifier = 'any'
# https://github.com/sphinx-doc/sphinx/blob/6.0.x/sphinx/ext/autosummary/__init__.py#L375
def format_table(
self, items: list[tuple[str, str, str, str, str, str]]
) -> list[Node]:
def format_table(self, items: list[tuple[str, str, str]]) -> list[Node]:
"""Generate a proper list of table nodes for autosummary:: directive.
*items* is a list produced by :meth:`get_items`.
Expand Down Expand Up @@ -857,16 +860,13 @@ def append_row(column_texts: list[tuple[str, str]]) -> None:
row.append(entry)
body.append(row)

for prefix, qualifier, name, sig, summary, real_name in items:
for name, sig, summary in items:
# The body of this loop is changed from copied code.
sig = rst.escape(sig)
if sig:
sig = f"**{sig}**"
if "nosignatures" not in self._options:
col1 = rf"{prefix}:{qualifier}:`{name} <{real_name}>`\ {sig}"
else:
col1 = f"{prefix}:{qualifier}:`{name} <{real_name}>`"
col2 = summary
append_row([(col1, "name"), (col2, "summary")])
sig = rst.escape(sig)
if sig:
sig = f"**{sig}**"
name = rf"{name}\ {sig}"
append_row([(name, "name"), (summary, "summary")])

return [table_spec, table]
8 changes: 7 additions & 1 deletion tests/test_build_ts/source/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,20 @@ export let interfaceInstance: I = {};
*/
export type TestTypeAlias<T extends A> = { a: T };
export type TestTypeAlias2 = { a: number };
/**
* Omit from automodule and send summary link somewhere else
* @omitFromAutoModule
* @summaryLink :js:typealias:`TestTypeAlias3 <module.TestTypeAlias>`
*/
export type TestTypeAlias3 = { a: number };

export let t: TestTypeAlias<A>;
export let t2: TestTypeAlias2;

/**
* A function with a type parameter!
*
* We'll refer to ourselves: :js:func:`functionWithTypeParam`
* We'll refer to ourselves: :js:func:`~module.functionWithTypeParam`
*
* @typeParam T The type parameter
* @typeParam S Another type param
Expand Down
18 changes: 13 additions & 5 deletions tests/test_build_ts/test_build_ts.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,12 +490,20 @@ def test_autosummary(self):
classes = soup.find(class_="classes")
assert classes.find(class_="summary").get_text() == "This is a summary."

classes = soup.find(class_="interfaces")
interfaces = soup.find(class_="interfaces")
assert (
classes.find(class_="summary").get_text()
interfaces.find(class_="summary").get_text()
== "Documentation for the interface I"
)

classes = soup.find(class_="type_aliases")
assert classes
assert classes.find(class_="summary").get_text() == "A super special type alias"
type_aliases = soup.find(class_="type_aliases")
assert type_aliases
assert (
type_aliases.find(class_="summary").get_text()
== "A super special type alias"
)
rows = list(type_aliases.find_all("tr"))
assert len(rows) == 3
href = rows[2].find("a")
assert href.get_text() == "TestTypeAlias3"
assert href["href"] == "automodule.html#module.TestTypeAlias"
124 changes: 104 additions & 20 deletions tests/test_renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Exc,
Function,
Interface,
Module,
Param,
Return,
TypeAlias,
Expand All @@ -22,6 +23,7 @@
from sphinx_js.renderers import (
AutoAttributeRenderer,
AutoFunctionRenderer,
AutoModuleRenderer,
render_description,
)

Expand Down Expand Up @@ -68,42 +70,47 @@ def ts_xref_formatter(config, xref):
return xref.name


@pytest.fixture()
def function_renderer():
class _config:
pass

def make_renderer(cls):
class _app:
config = _config

def lookup_object(self, partial_path: list[str]):
return self.objects[partial_path[-1]]
class config:
ts_type_xref_formatter = ts_xref_formatter

renderer = AutoFunctionRenderer.__new__(AutoFunctionRenderer)
renderer = cls.__new__(cls)
renderer._app = _app
renderer._explicit_formal_params = None
renderer._content = []
renderer._set_type_xref_formatter(ts_xref_formatter)
renderer._add_span = False
return renderer


@pytest.fixture()
def function_renderer():
def lookup_object(self, partial_path: list[str]):
return self.objects[partial_path[-1]]

renderer = make_renderer(AutoFunctionRenderer)
renderer.lookup_object = lookup_object.__get__(renderer)
renderer.objects = {}
return renderer


@pytest.fixture()
def attribute_renderer():
class _config:
pass
return make_renderer(AutoAttributeRenderer)

class _app:
config = _config

renderer = AutoAttributeRenderer.__new__(AutoAttributeRenderer)
renderer._app = _app
renderer._explicit_formal_params = None
renderer._content = []
renderer._set_type_xref_formatter(ts_xref_formatter)
renderer._add_span = False
@pytest.fixture()
def auto_module_renderer():
renderer = make_renderer(AutoModuleRenderer)

class directive:
class state:
class document:
class settings:
pass

renderer._directive = directive
return renderer


Expand Down Expand Up @@ -146,6 +153,18 @@ def type_alias_render(partial_path=None, use_short_name=False, **args):
return type_alias_render


@pytest.fixture()
def auto_module_render(auto_module_renderer) -> Any:
def auto_module_render(partial_path=None, use_short_name=False, **args):
if not partial_path:
partial_path = ["blah"]
return auto_module_renderer.rst(
partial_path, make_module(**args), use_short_name
)

return auto_module_render


top_level_dict = dict(
name="",
path=[],
Expand Down Expand Up @@ -187,6 +206,17 @@ def type_alias_render(partial_path=None, use_short_name=False, **args):
)
attribute_dict = top_level_dict | member_dict | dict(type="")
type_alias_dict = top_level_dict | dict(type="", type_params=[])
module_dict = dict(
filename="",
deppath=None,
path=[],
line=0,
attributes=[],
functions=[],
classes=[],
interfaces=[],
type_aliases=[],
)


def make_class(**args):
Expand All @@ -209,6 +239,10 @@ def make_type_alias(**args):
return TypeAlias(**(type_alias_dict | args))


def make_module(**args):
return Module(**(module_dict | args))


DEFAULT_RESULT = ".. js:function:: blah()\n"


Expand Down Expand Up @@ -562,3 +596,53 @@ def test_type_alias(type_alias_render):
:typeparam T: ABC (extends **number**)
"""
)


def test_auto_module_render(auto_module_render):
assert auto_module_render() == ".. js:module:: blah"
assert auto_module_render(
functions=[
make_function(
name="f",
description="this is a description",
params=[Param("a", description="a description")],
),
make_function(name="g"),
],
attributes=[make_attribute(name="x", type="any"), make_attribute(name="y")],
type_aliases=[
make_type_alias(name="S"),
make_type_alias(name="T"),
# Check that we omit stuff marked with @omitFromAutoModule
make_type_alias(name="U", modifier_tags=["@omitFromAutoModule"]),
],
) == dedent(
"""\
.. js:module:: blah
.. js:typealias:: S
.. js:typealias:: T
.. js:attribute:: x
.. rst-class:: js attribute type
type: **any**
.. js:attribute:: y
.. js:function:: f(a)
this is a description
:param a: a description
.. js:function:: g()
"""
)
8 changes: 8 additions & 0 deletions tests/test_typedoc_analysis/source/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,11 @@ export type MappedType3 = { readonly [property in keys]-?: number };
export type TemplateLiteral = `${number}: ${string}`;

export type OptionalType = [number?];

/**
* @hidetype
* @omitFromAutoModule
* @destructure a.b
* @summaryLink :role:`target`
*/
export type CustomTags = {};
10 changes: 10 additions & 0 deletions tests/test_typedoc_analysis/test_typedoc_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,3 +742,13 @@ def test_template_literal(self):
obj = self.analyzer.get_object(["TemplateLiteral"])
assert isinstance(obj, TypeAlias)
assert join_type(obj.type) == "`${number}: ${string}`"

def test_custom_tags(self):
obj = self.analyzer.get_object(["CustomTags"])
assert isinstance(obj, TypeAlias)
assert "@hidetype" in obj.modifier_tags
assert "@omitFromAutoModule" in obj.modifier_tags
assert [join_description(d) for d in obj.block_tags["summaryLink"]] == [
":role:`target`"
]
assert [join_description(d) for d in obj.block_tags["destructure"]] == ["a.b"]

0 comments on commit 781dc61

Please sign in to comment.