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

Render internal xrefs #56

Merged
merged 5 commits into from
Sep 24, 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 requirements_dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pytest==7.2.0
recommonmark==0.7.1
nox
twine==4.0.2
beautifulsoup4
29 changes: 20 additions & 9 deletions sphinx_js/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
Type,
TypeParam,
TypeXRef,
TypeXRefInternal,
)
from .jsdoc import Analyzer as JsAnalyzer
from .parsers import PathVisitor
Expand Down Expand Up @@ -207,11 +208,15 @@ def _formal_params(self, obj: Function | Class) -> str:

return "({})".format(", ".join(formals))

def format_type(self, type: Type, escape: bool = False) -> str:
def format_type(self, type: Type, escape: bool = False, bold: bool = True) -> str:
if not type:
return ""
if isinstance(type, str):
return self.format_type([type])
if bold:
type = "**%s**" % type
if escape:
type = rst.escape(type)
return type
it = iter(type)

def strs() -> Iterator[str]:
Expand All @@ -236,17 +241,23 @@ def strs() -> Iterator[str]:
return "".join(res)

def render_xref(self, s: TypeXRef, escape: bool = False) -> str:
if isinstance(s, TypeXRefInternal):
name = rst.escape(s.name)
result = f":js:class:`{name}`"
else:
result = s.name
if escape:
return rst.escape(s.name)
return s.name
result = rst.escape(result)
return result

def _return_formatter(self, return_: Return) -> tuple[list[str], str]:
"""Derive heads and tail from ``@returns`` blocks."""
tail = ""
tail = []
if return_.type:
tail += "**%s** -- " % self.format_type(return_.type, escape=True)
tail += return_.description
return ["returns"], tail
tail.append(self.format_type(return_.type, escape=False))
if return_.description:
tail.append(return_.description)
return ["returns"], " -- ".join(tail)

def _type_param_formatter(self, tparam: TypeParam) -> tuple[list[str], str] | None:
v = tparam.name
Expand Down Expand Up @@ -280,7 +291,7 @@ def _exception_formatter(self, exception: Exc) -> tuple[list[str], str]:
"""Derive heads and tail from ``@throws`` blocks."""
heads = ["throws"]
if exception.type:
heads.append(self.format_type(exception.type))
heads.append(self.format_type(exception.type, bold=False))
tail = exception.description
return heads, tail

Expand Down
24 changes: 12 additions & 12 deletions tests/test_build_js/test_build_js.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ def test_autofunction_typedef(self):
"""Make sure @typedef uses can be documented with autofunction."""
self._file_contents_eq(
"autofunction_typedef",
"TypeDefinition()\n\n Arguments:\n * **width** (*Number*) -- width in pixels\n",
"TypeDefinition()\n\n Arguments:\n * **width** (**Number**) -- width in pixels\n",
)

def test_autofunction_callback(self):
"""Make sure @callback uses can be documented with autofunction."""
self._file_contents_eq(
"autofunction_callback",
"requestCallback(responseCode)\n\n Some global callback\n\n Arguments:\n * **responseCode** (*number*) --\n",
"requestCallback(responseCode)\n\n Some global callback\n\n Arguments:\n * **responseCode** (**number**) --\n",
)

def test_autofunction_example(self):
Expand All @@ -71,10 +71,10 @@ def test_autofunction_destructured_params(self):
"autofunction_destructured_params",
"destructuredParams(p1, p2)\n\n"
" Arguments:\n"
" * **p1** (*number*) --\n\n"
" * **p2** (*Object*) --\n\n"
" * **p2.foo** (*string*) --\n\n"
" * **p2.bar** (*string*) --\n",
" * **p1** (**number**) --\n\n"
" * **p2** (**Object**) --\n\n"
" * **p2.foo** (**string**) --\n\n"
" * **p2.bar** (**string**) --\n",
)

def test_autofunction_defaults_in_doclet(self):
Expand All @@ -84,9 +84,9 @@ def test_autofunction_defaults_in_doclet(self):
"autofunction_defaults_doclet",
'defaultsDocumentedInDoclet(func=() => 5, str="a string with \\" quote", strNum="42", strBool="true", num=5, nil=null)\n\n'
" Arguments:\n"
" * **func** (*function*) --\n\n"
" * **strNum** (*string*) --\n\n"
" * **strBool** (*string*) --\n",
" * **func** (**function**) --\n\n"
" * **strNum** (**string**) --\n\n"
" * **strBool** (**string**) --\n",
)

def test_autofunction_defaults_in_code(self):
Expand Down Expand Up @@ -361,7 +361,7 @@ def test_restructuredtext_injection(self):
"injection(a_, b)\n\n"
" Arguments:\n"
" * **a_** -- Snorf\n\n"
" * **b** (>>type_<<) -- >>Borf_<<\n\n"
" * **b** (**type_**) -- >>Borf_<<\n\n"
" Returns:\n"
" **rtype_** -- >>Dorf_<<\n",
)
Expand All @@ -377,7 +377,7 @@ def test_union_types(self):
switched from " | " as the union separator back to "|".

"""
assert "* **fnodeA** (*Node|Fnode*) --" in self._file_contents("union")
assert "* **fnodeA** (**Node|Fnode**) --" in self._file_contents("union")

def test_field_list_unwrapping(self):
"""Ensure the tails of field lists have line breaks and leading
Expand Down Expand Up @@ -414,7 +414,7 @@ def test_field_list_unwrapping(self):
FIELDS = """

Arguments:
* **node** (*Node*) -- Something of a single type
* **node** (**Node**) -- Something of a single type

Throws:
**PartyError|FartyError** -- Something with multiple types and a
Expand Down
13 changes: 13 additions & 0 deletions tests/test_build_ts/source/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,16 @@ export interface OptionalThings {
foop?(): void;
boop?: boolean;
}

/**
* Words words words
* @param a An optional thing
* @returns The result
*/
export function blah(a: OptionalThings) : ConstructorlessClass {
return 0 as ConstructorlessClass;
}

export function thunk(b : typeof blah) {

}
7 changes: 7 additions & 0 deletions tests/test_build_ts/source/docs/xrefs.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.. js:autofunction:: blah

blah

.. js:autofunction:: thunk

Xrefs in the function type of the argument
26 changes: 26 additions & 0 deletions tests/test_build_ts/test_build_ts.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from bs4 import BeautifulSoup
from conftest import TYPEDOC_VERSION

from tests.testing import SphinxBuildTestCase
Expand Down Expand Up @@ -121,3 +122,28 @@ def test_implements_links(self):
assert 'href="index.html#class.Interface"' in self._file_contents(
"autoclass_class_with_interface_and_supers"
)

def test_xrefs(self):
soup = BeautifulSoup(self._file_contents("xrefs"), "html.parser")

def get_links(id):
return soup.find(id=id).parent.find_all("a")

links = get_links("blah")
href = links[1]
assert href.attrs["class"] == ["reference", "internal"]
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()"

href = links[2]
assert href.attrs["class"] == ["reference", "internal"]
assert (
href.attrs["href"] == "autoclass_constructorless.html#ConstructorlessClass"
)
assert href.get_text() == "ConstructorlessClass()"

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