Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a separate directive for interfaces than for classes #138

Merged
merged 1 commit into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 3 additions & 21 deletions sphinx_js/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@
from typing import Any

from sphinx.application import Sphinx
from sphinx.domains.javascript import JavaScriptDomain
from sphinx.errors import SphinxError

from .directives import (
JSFunction,
auto_attribute_directive_bound_to_app,
auto_class_directive_bound_to_app,
auto_function_directive_bound_to_app,
auto_module_directive_bound_to_app,
auto_summary_directive_bound_to_app,
add_directives,
sphinx_js_type_role,
)
from .jsdoc import Analyzer as JsAnalyzer
Expand Down Expand Up @@ -94,7 +91,6 @@ def fix_staticfunction_objtype() -> None:
"""Override js:function directive with one that understands static and async
prefixes
"""
from sphinx.domains.javascript import JavaScriptDomain

JavaScriptDomain.directives["function"] = JSFunction

Expand Down Expand Up @@ -153,21 +149,7 @@ def setup(app: Sphinx) -> None:
# is RSTs.
app.connect("builder-inited", analyze)

app.add_directive_to_domain(
"js", "autofunction", auto_function_directive_bound_to_app(app)
)
app.add_directive_to_domain(
"js", "autoclass", auto_class_directive_bound_to_app(app)
)
app.add_directive_to_domain(
"js", "autoattribute", auto_attribute_directive_bound_to_app(app)
)
app.add_directive_to_domain(
"js", "automodule", auto_module_directive_bound_to_app(app)
)
app.add_directive_to_domain(
"js", "autosummary", auto_summary_directive_bound_to_app(app)
)
add_directives(app)

# TODO: We could add a js:module with app.add_directive_to_domain().

Expand Down
57 changes: 56 additions & 1 deletion sphinx_js/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""
import re
from collections.abc import Iterable
from functools import cache
from os.path import join, relpath
from typing import Any

Expand All @@ -20,7 +21,9 @@
from docutils.utils import new_document
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.domains.javascript import JSCallable
from sphinx.domains import ObjType
from sphinx.domains.javascript import JavaScriptDomain, JSCallable, JSObject
from sphinx.locale import _

from .renderers import (
AutoAttributeRenderer,
Expand Down Expand Up @@ -178,6 +181,39 @@ def get_display_prefix(
return result


class JSInterface(JSCallable):
"""Like a callable but with a different prefix."""

allow_nesting = True

def get_display_prefix(self) -> list[Node]:
return [
addnodes.desc_sig_keyword("interface", "interface"),
addnodes.desc_sig_space(),
]


@cache
def patch_js_interface() -> None:
orig_get_index_text = JSObject.get_index_text

def patched_get_index_text(
self: JSObject, objectname: str, name_obj: tuple[str, str]
) -> str:
name, obj = name_obj
if self.objtype == "interface":
return _("%s() (interface)") % name
return orig_get_index_text(self, objectname, name_obj)

JSObject.get_index_text = patched_get_index_text # type:ignore[method-assign]


def add_js_interface(app: Sphinx) -> None:
patch_js_interface()
JavaScriptDomain.object_types["interface"] = ObjType(_("interface"), "interface")
app.add_directive_to_domain("js", "interface", JSInterface)


def auto_module_directive_bound_to_app(app: Sphinx) -> type[Directive]:
class AutoModuleDirective(JsDirectiveWithChildren):
"""TODO: words here"""
Expand All @@ -198,3 +234,22 @@ def run(self) -> list[Node]:
return self._run(AutoSummaryRenderer, app)

return JsDocSummary


def add_directives(app: Sphinx) -> None:
app.add_directive_to_domain(
"js", "autofunction", auto_function_directive_bound_to_app(app)
)
app.add_directive_to_domain(
"js", "autoclass", auto_class_directive_bound_to_app(app)
)
app.add_directive_to_domain(
"js", "autoattribute", auto_attribute_directive_bound_to_app(app)
)
app.add_directive_to_domain(
"js", "automodule", auto_module_directive_bound_to_app(app)
)
app.add_directive_to_domain(
"js", "autosummary", auto_summary_directive_bound_to_app(app)
)
add_js_interface(app)
4 changes: 1 addition & 3 deletions sphinx_js/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,9 +591,7 @@ def _template_vars(self, name: str, obj: Class | Interface) -> dict[str, Any]:
interfaces=[self.render_type(x) for x in obj.interfaces]
if isinstance(obj, Class)
else [],
is_interface=isinstance(
obj, Interface
), # TODO: Make interfaces not look so much like classes. This will require taking complete control of templating from Sphinx.
is_interface=isinstance(obj, Interface),
supers=[self.render_type(x) for x in obj.supers],
constructor_comment=render_description(constructor.description),
content="\n".join(self._content),
Expand Down
8 changes: 4 additions & 4 deletions sphinx_js/templates/class.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{% import 'common.rst' as common %}

{% if is_interface -%}
.. js:interface:: {{ name }}{{ params }}
{%- else -%}
.. js:class:: {{ name }}{{ params }}
{%- endif %}

{{ common.deprecated(deprecated)|indent(3) }}

Expand All @@ -12,10 +16,6 @@
*abstract*
{%- endif %}

{% if is_interface -%}
*interface*
{%- endif %}

{{ common.exported_from(exported_from)|indent(3) }}

{% if supers -%}
Expand Down
2 changes: 1 addition & 1 deletion tests/test_build_ts/source/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ export class Z {
export const q = { a: "z29", b: 76 };

/**
* Interface documentation
* Documentation for the interface I
*/
export interface I {}
15 changes: 7 additions & 8 deletions tests/test_build_ts/test_build_ts.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ def test_optional_members(self):
question marks sticking out of them."""
self._file_contents_eq(
"autoclass_interface_optionals",
"class OptionalThings()\n"
"\n"
" *interface*\n"
"interface OptionalThings()\n"
"\n"
' *exported from* "class"\n'
"\n"
Expand Down Expand Up @@ -316,11 +314,9 @@ class module.Z(a, b)

Z.z()

class module.I()

Interface documentation
interface module.I()

*interface*
Documentation for the interface I

*exported from* "module"
"""
Expand Down Expand Up @@ -422,4 +418,7 @@ def test_autosummary(self):
assert classes.find(class_="summary").get_text() == "This is a summary."

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