Skip to content

Commit

Permalink
Fix interface xrefs (#153)
Browse files Browse the repository at this point in the history
This adds a role for interface cross referencing. It also updates
`ts_type_xref_formatter` to take a third argument "kind" which indicates which
type of object we're referring to so that we can generate proper xrefs. TODO: We
should probably move this field onto the XRefInternal object.
  • Loading branch information
hoodmane authored May 6, 2024
1 parent 90055b1 commit 0ac271e
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 58 deletions.
14 changes: 6 additions & 8 deletions sphinx_js/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
JSCallable,
JSConstructor,
JSObject,
JSXRefRole,
)
from sphinx.locale import _
from sphinx.util.docfields import GroupedField, TypedField
Expand Down Expand Up @@ -291,7 +292,7 @@ def get_display_prefix(self) -> list[Node]:


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

def patched_get_index_text(
Expand All @@ -305,12 +306,6 @@ def patched_get_index_text(
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 @@ -337,6 +332,7 @@ def add_directives(app: Sphinx) -> None:
fix_js_make_xref()
fix_staticfunction_objtype()
add_type_param_field_to_directives()
patch_JsObject_get_index_text()
app.add_role("sphinx_js_type", sphinx_js_type_role)
app.add_directive_to_domain(
"js", "autofunction", auto_function_directive_bound_to_app(app)
Expand All @@ -353,4 +349,6 @@ def add_directives(app: Sphinx) -> None:
app.add_directive_to_domain(
"js", "autosummary", auto_summary_directive_bound_to_app(app)
)
add_js_interface(app)
JavaScriptDomain.object_types["interface"] = ObjType(_("interface"), "interface")
app.add_directive_to_domain("js", "interface", JSInterface)
app.add_role_to_domain("js", "interface", JSXRefRole())
27 changes: 20 additions & 7 deletions sphinx_js/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
Type,
TypeParam,
TypeXRef,
TypeXRefInternal,
)
from .jsdoc import Analyzer as JsAnalyzer
from .parsers import PathVisitor
Expand Down Expand Up @@ -173,7 +174,7 @@ class HasDepPath(Protocol):


class Renderer:
_type_xref_formatter: Callable[[TypeXRef], str]
_type_xref_formatter: Callable[[TypeXRef, str | None], str]
# We turn the <span class="sphinx_js-type"> in the analyzer tests because it
# makes a big mess.
_add_span: bool
Expand Down Expand Up @@ -233,13 +234,13 @@ def from_directive(cls: type[R], directive: Directive, app: Sphinx) -> R:
)

def _set_type_xref_formatter(
self, formatter: Callable[[Config, TypeXRef], str] | None
self, formatter: Callable[[Config, TypeXRef, str | None], str] | None
) -> None:
if formatter:
self._type_xref_formatter = partial(formatter, self._app.config)
return

def default_type_xref_formatter(xref: TypeXRef) -> str:
def default_type_xref_formatter(xref: TypeXRef, _: str | None) -> str:
return xref.name

self._type_xref_formatter = default_type_xref_formatter
Expand Down Expand Up @@ -282,13 +283,16 @@ class JsRenderer(Renderer):
def _template_vars(self, name: str, obj: TopLevel) -> dict[str, Any]:
raise NotImplementedError

def get_object(self) -> TopLevel:
"""Return the IR object rendered by this renderer."""
def lookup_object(
self,
partial_path: list[str],
renderer_type: Literal["function", "class", "attribute"] = "attribute",
) -> TopLevel:
try:
analyzer: Analyzer = (
self._app._sphinxjs_analyzer # type:ignore[attr-defined]
)
obj = analyzer.get_object(self._partial_path, self._renderer_type)
obj = analyzer.get_object(partial_path, renderer_type)
return obj
except SuffixNotFound as exc:
raise SphinxError(
Expand All @@ -302,6 +306,10 @@ def get_object(self) -> TopLevel:
)
)

def get_object(self) -> TopLevel:
"""Return the IR object rendered by this renderer."""
return self.lookup_object(self._partial_path, self._renderer_type)

def rst_nodes(self) -> list[Node]:
"""Render into RST nodes a thing shaped like a function, having a name
and arguments.
Expand Down Expand Up @@ -438,7 +446,12 @@ def strs() -> Iterator[str]:
return joined

def render_xref(self, s: TypeXRef, escape: bool = False) -> str:
result = self._type_xref_formatter(s)
obj = None
kind = None
if isinstance(s, TypeXRefInternal | TypeXRefInternal):
obj = self.lookup_object(s.path)
kind = type(obj).__name__.lower()
result = self._type_xref_formatter(s, kind)
if escape:
result = rst.escape(result)
return result
Expand Down
4 changes: 2 additions & 2 deletions tests/test_build_ts/source/docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
from sphinx_js.ir import TypeXRefInternal


def ts_type_xref_formatter(config, xref):
def ts_type_xref_formatter(config, xref, kind):
if isinstance(xref, TypeXRefInternal):
name = rst.escape(xref.name)
return f":js:class:`{name}`"
return f":js:{kind}:`{name}`"
else:
return xref.name
6 changes: 3 additions & 3 deletions tests/test_build_ts/test_build_ts.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def test_abstract_extends_and_implements(self):
' * "ClassDefinition()"\n'
"\n"
" **Implements:**\n"
' * "Interface()"\n'
' * "Interface"\n'
"\n"
" I construct.\n",
)
Expand Down Expand Up @@ -354,7 +354,7 @@ def get_links(id):
assert href.attrs["href"] == "autoclass_interface_optionals.html#OptionalThings"
assert href.attrs["title"] == "OptionalThings"
assert next(href.children).name == "code"
assert href.get_text() == "OptionalThings()"
assert href.get_text() == "OptionalThings"

href = links[2]
assert href.attrs["class"] == ["reference", "internal"]
Expand All @@ -364,7 +364,7 @@ def get_links(id):
assert href.get_text() == "ConstructorlessClass()"

thunk_links = get_links("thunk")
assert thunk_links[1].get_text() == "OptionalThings()"
assert thunk_links[1].get_text() == "OptionalThings"
assert thunk_links[2].get_text() == "ConstructorlessClass()"

def test_sphinx_link_in_description(self):
Expand Down
Loading

0 comments on commit 0ac271e

Please sign in to comment.