Skip to content

Add external xref formatting #67

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

Merged
merged 1 commit into from
Sep 25, 2023
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
1 change: 1 addition & 0 deletions sphinx_js/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ def setup(app: Sphinx) -> None:
"js_source_path", default=["../"], rebuild="env", types=[str, list]
)
app.add_config_value("jsdoc_config_path", default=None, rebuild="env")
app.add_config_value("ts_xref_formatter", None, "env")

# We could use a callable as the "default" param here, but then we would
# have had to duplicate or build framework around the logic that promotes
Expand Down
1 change: 1 addition & 0 deletions sphinx_js/ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class TypeXRefInternal(TypeXRef):

@define
class TypeXRefExternal(TypeXRef):
package: str
sourcefilename: str
qualifiedName: str

Expand Down
29 changes: 24 additions & 5 deletions sphinx_js/renderers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import textwrap
from collections.abc import Callable, Iterator
from functools import partial
from re import sub
from typing import Any, Literal

Expand All @@ -10,6 +11,7 @@
from docutils.utils import new_document
from jinja2 import Environment, PackageLoader
from sphinx.application import Sphinx
from sphinx.config import Config
from sphinx.errors import SphinxError
from sphinx.util import logging, rst

Expand All @@ -29,6 +31,7 @@
Type,
TypeParam,
TypeXRef,
TypeXRefExternal,
TypeXRefInternal,
)
from .jsdoc import Analyzer as JsAnalyzer
Expand All @@ -54,10 +57,27 @@ class JsRenderer:

_renderer_type: Literal["function", "class", "attribute"]
_template: str
_xref_formatter: Callable[[TypeXRefExternal], str]
_partial_path: list[str]
_explicit_formal_params: str
_content: list[str]
_options: dict[str, Any]

def _template_vars(self, name: str, obj: TopLevel) -> dict[str, Any]:
raise NotImplementedError

def _set_xref_formatter(
self, formatter: Callable[[Config, TypeXRefExternal], str] | None
) -> None:
if formatter:
self._xref_formatter = partial(formatter, self._app.config)
return

def default_xref_formatter(xref: TypeXRefExternal) -> str:
return xref.name

self._xref_formatter = default_xref_formatter

def __init__(
self,
directive: Directive,
Expand All @@ -71,15 +91,13 @@ def __init__(
directive.state.document.settings.tab_width = 8

self._directive = directive
self._app = app
self._set_xref_formatter(app.config.ts_xref_formatter)

# content, arguments, options, app: all need to be accessible to
# template_vars, so we bring them in on construction and stow them away
# on the instance so calls to template_vars don't need to concern
# themselves with what it needs.
self._app = app
self._partial_path: list[str]
self._explicit_formal_params: str

(
self._partial_path,
self._explicit_formal_params,
Expand Down Expand Up @@ -295,7 +313,8 @@ def render_xref(self, s: TypeXRef, escape: bool = False) -> str:
name = rst.escape(s.name)
result = f":js:class:`{name}`"
else:
result = s.name
assert isinstance(s, TypeXRefExternal)
result = self._xref_formatter(s)
if escape:
result = rst.escape(result)
return result
Expand Down
7 changes: 6 additions & 1 deletion sphinx_js/typedoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,7 @@ class ReferenceType(TypeBase):
type: Literal["reference"]
name: str
target: int | Target | None
package: str | None = None
refersToTypeParameter: bool = False

def _render_name_root(self, converter: Converter) -> Iterator[str | ir.TypeXRef]:
Expand All @@ -996,8 +997,12 @@ def _render_name_root(self, converter: Converter) -> Iterator[str | ir.TypeXRef]
yield ir.TypeXRefInternal(self.name, node.path)
return
assert isinstance(self.target, Target)
assert self.package
yield ir.TypeXRefExternal(
self.name, self.target.sourceFileName, self.target.qualifiedName
self.name,
self.package,
self.target.sourceFileName,
self.target.qualifiedName,
)


Expand Down
38 changes: 36 additions & 2 deletions tests/test_renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
Param,
Return,
TypeParam,
TypeXRefExternal,
TypeXRefInternal,
)
from sphinx_js.renderers import AutoFunctionRenderer, JsRenderer
Expand Down Expand Up @@ -50,15 +51,29 @@ def test_render_description():


@pytest.fixture()
def function_render() -> AutoFunctionRenderer:
def function_renderer():
class _config:
pass

class _app:
config = _config

renderer = AutoFunctionRenderer.__new__(AutoFunctionRenderer)
renderer._app = _app
renderer._explicit_formal_params = None
renderer._content = []
renderer._set_xref_formatter(None)
return renderer


@pytest.fixture()
def function_render(function_renderer) -> AutoFunctionRenderer:
def function_render(partial_path=None, use_short_name=False, **args):
if not partial_path:
partial_path = ["blah"]
return renderer.rst(partial_path, make_function(**args), use_short_name)
return function_renderer.rst(
partial_path, make_function(**args), use_short_name
)

return function_render

Expand Down Expand Up @@ -202,6 +217,25 @@ def test_func_render_type_params(function_render):
)


def test_render_xref(function_renderer: AutoFunctionRenderer):
assert (
function_renderer.render_type([TypeXRefInternal(name="A", path=["a.", "A"])])
== ":js:class:`A`"
)
xref_external = TypeXRefExternal("A", "blah", "a.ts", "a.A")
assert function_renderer.render_type([xref_external]) == "A"
res = []

def xref_render(config, val):
res.append([config, val])
return val.package + "::" + val.name

function_renderer._set_xref_formatter(xref_render)
assert function_renderer.render_type([xref_external]) == "blah::A"
assert res[0][0] == function_renderer._app.config
assert res[0][1] == xref_external


def test_func_render_param_type(function_render):
assert function_render(
description="this is a description",
Expand Down
7 changes: 6 additions & 1 deletion tests/test_typedoc_analysis/test_typedoc_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,12 @@ def test_utility_types(self):
obj = self.analyzer.get_object(["partial"])
t = deepcopy(obj.type)
t[0].sourcefilename = "xxx"
assert t == [TypeXRefExternal("Partial", "xxx", "Partial"), "<", "string", ">"]
assert t == [
TypeXRefExternal("Partial", "typescript", "xxx", "Partial"),
"<",
"string",
">",
]

def test_constrained_by_property(self):

Expand Down