Skip to content

Commit

Permalink
Add async prefix to async functions
Browse files Browse the repository at this point in the history
  • Loading branch information
hoodmane committed Sep 25, 2023
1 parent 132f08b commit f98307a
Show file tree
Hide file tree
Showing 11 changed files with 62 additions and 45 deletions.
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() {}
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

0 comments on commit f98307a

Please sign in to comment.