Skip to content

Add async prefix to async functions #65

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
36 changes: 5 additions & 31 deletions sphinx_js/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sphinx.errors import SphinxError

from .directives import (
JSStaticFunction,
JSFunction,
auto_attribute_directive_bound_to_app,
auto_class_directive_bound_to_app,
auto_function_directive_bound_to_app,
Expand Down Expand Up @@ -86,37 +86,12 @@ class JSGroupedField(JSXrefMixin, GroupedField):
# Cache this to guarantee it only runs once.
@cache
def fix_staticfunction_objtype() -> None:
"""Add support for staticfunction objtype

This adds a new staticfunction objtype to javascript domain class attribute.
Can't do this with ``app.add_object_type()`` because that adds it to the
std domain.

This also monkeypatches ``JSObject.get_index_text`` to have the right name
for static functions.

"""Override js:function directive with one that understands static and async
prefixes
"""
from sphinx.domains import ObjType
from sphinx.domains.javascript import JavaScriptDomain, JSObject
from sphinx.locale import _

if "staticfunction" in JavaScriptDomain.object_types:
return
JavaScriptDomain.object_types["staticfunction"] = ObjType(
_("static function"), "func"
)

orig_get_index_text = JSObject.get_index_text

def get_index_text(self: Any, objectname: str, name_obj: Any) -> Any:
name, obj = name_obj
if self.objtype == "staticfunction":
if not obj:
return _("%s() (built-in static function)") % name
return _("%s() (%s static method)") % (name, obj)
return orig_get_index_text(self, objectname, name_obj)
from sphinx.domains.javascript import JavaScriptDomain

JSObject.get_index_text = get_index_text # type:ignore[assignment]
JavaScriptDomain.directives["function"] = JSFunction


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

app.add_directive_to_domain("js", "staticfunction", JSStaticFunction)
app.add_directive_to_domain(
"js", "autofunction", auto_function_directive_bound_to_app(app)
)
Expand Down
28 changes: 20 additions & 8 deletions sphinx_js/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,23 @@ def _members_to_exclude(arg: str | None) -> set[str]:
return set(a.strip() for a in (arg or "").split(","))


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

def get_display_prefix(self) -> list[Any]:
return [
addnodes.desc_sig_keyword("static", "static"),
addnodes.desc_sig_space(),
]
class JSFunction(JSCallable):
option_spec = {
**JSCallable.option_spec,
"static": flag,
"async": flag,
}

def get_display_prefix(
self,
) -> list[Any]:
result = []
for name in ["static", "async"]:
if name in self.options:
result.extend(
[
addnodes.desc_sig_keyword(name, name),
addnodes.desc_sig_space(),
]
)
return result
1 change: 1 addition & 0 deletions sphinx_js/ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ class Attribute(TopLevel, _Member):
class Function(TopLevel, _Member):
"""A function or a method of a class"""

is_async: bool
params: list[Param]
exceptions: list[Exc]
returns: list[Return]
Expand Down
1 change: 1 addition & 0 deletions sphinx_js/jsdoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ def _doclet_as_function(self, doclet: Doclet, full_path: list[str]) -> Function:
is_abstract=False,
is_optional=False,
is_static=is_static(doclet),
is_async=False,
is_private=is_private(doclet),
exceptions=exceptions_to_ir(doclet.get("exceptions", [])),
returns=returns_to_ir(doclet.get("returns", [])),
Expand Down
2 changes: 2 additions & 0 deletions sphinx_js/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ def _template_vars(self, name: str, obj: Function) -> dict[str, Any]: # type: i
deprecated=deprecated,
is_optional=obj.is_optional,
is_static=obj.is_static,
is_async=obj.is_async,
see_also=obj.see_alsos,
content="\n".join(self._content),
)
Expand Down Expand Up @@ -425,6 +426,7 @@ def _template_vars(self, name: str, obj: Class | Interface) -> dict[str, Any]:
is_abstract=False,
is_optional=False,
is_static=False,
is_async=False,
is_private=False,
type_params=obj.type_params,
params=[],
Expand Down
10 changes: 6 additions & 4 deletions sphinx_js/templates/function.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{% import 'common.rst' as common %}

{% if is_static %}
.. js:staticfunction:: {{ name }}{{ '?' if is_optional else '' }}{{ params }}
{% else %}
.. js:function:: {{ name }}{{ '?' if is_optional else '' }}{{ params }}
{% endif %}
{% if is_static -%}
:static:
{% endif %}
{%- if is_async -%}
:async:
{% endif %}

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

Expand Down
5 changes: 4 additions & 1 deletion sphinx_js/typedoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,9 @@ def to_ir(

self._fix_type_suffix()
params = self._destructure_params()

# Would be nice if we could statically determine that the function was
# defined with `async` keyword but this is probably good enough
is_async = isinstance(self.type, ReferenceType) and self.type.name == "Promise"
# This is the real meat of a function, method, or constructor.
#
# Constructors' .name attrs end up being like 'new Foo'. They
Expand All @@ -894,6 +896,7 @@ def to_ir(
returns=self.return_type(converter)
if self.kindString != "Constructor signature"
else [],
is_async=is_async,
**self.parent_member_properties,
**self._top_level_properties(),
)
Expand Down
2 changes: 2 additions & 0 deletions tests/test_build_ts/source/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,5 @@ export function deprecatedFunction() {}
* ```
*/
export function exampleFunction() {}

export async function asyncFunction() {}
1 change: 1 addition & 0 deletions tests/test_build_ts/source/docs/async_function.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. js:autofunction:: asyncFunction
13 changes: 13 additions & 0 deletions tests/test_build_ts/test_build_ts.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,19 @@ def test_example(self):
),
)

def test_async(self):
self._file_contents_eq(
"async_function",
dedent(
"""\
async asyncFunction()

Returns:
Promise<void>
"""
),
)


class HtmlBuilderTests(SphinxBuildTestCase):
"""Tests which require an HTML build of our Sphinx tree, for checking
Expand Down
1 change: 1 addition & 0 deletions tests/test_jsdoc_analysis/test_jsdoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def test_top_level_and_function(self):
is_abstract=False,
is_optional=False,
is_static=False,
is_async=False,
params=[
Param(
name="bar",
Expand Down
8 changes: 7 additions & 1 deletion tests/test_renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def make_function(**args):
is_abstract=False,
is_optional=False,
is_static=False,
is_async=False,
is_private=False,
name="",
path=[],
Expand Down Expand Up @@ -117,7 +118,12 @@ def test_func_render_flags(function_render):
# TODO: look into this.
assert function_render(is_abstract=True) == DEFAULT_RESULT
assert function_render(is_optional=True) == ".. js:function:: blah?()\n"
assert function_render(is_static=True) == ".. js:staticfunction:: blah()\n"
assert function_render(is_static=True) == ".. js:function:: blah()\n :static:\n"
assert function_render(is_async=True) == ".. js:function:: blah()\n :async:\n"
assert (
function_render(is_async=True, is_static=True)
== ".. js:function:: blah()\n :static:\n :async:\n"
)
assert function_render(is_private=True) == DEFAULT_RESULT


Expand Down