# :coding: utf-8
import collections
from sphinx import addnodes
import docutils.parsers.rst.directives
from .base import BaseDirective
from .rst_generator import (
get_rst_attribute_elements,
get_rst_method_elements
)
def _parse_members(argument):
"""Convert the :members: options to class directive."""
if argument is None:
return True
return [arg.strip() for arg in argument.split(",")]
[docs]class AutoClassDirective(BaseDirective):
"""Directive to render :term:`Javascript` class documentation.
The unique argument should be the identifier of the class element.
.. sourcecode:: rest
.. js:autoclass:: module.AwesomeClass
The available options are:
* members:
This option can be boolean if no arguments are given to indicate that
all members should be documented, or a white list of member names to
display.
* skip-constructor:
Indicate whether the constructor method should be displayed if
available.
* skip-attribute-value:
Indicate whether attribute values within the class should be skipped.
* undoc-members:
Indicate whether members with no docstrings should be displayed.
* private-members:
Indicate whether private members (with a name starting with an
underscore) should be displayed.
* alias:
String element to replace the class name.
* module-alias:
String element to replace the module name.
* module-path-alias:
String element to replace the module path.
* force-partial-import:
Indicate whether the class import statement display should be indicated
with partial import if the class element is exported.
.. seealso::
:ref:`directive/autoclass`
.. seealso::
:ref:`configuration/js_class_options`
"""
#: Javascript class is callable
has_arguments = True
#: Define the Object type
objtype = "class"
#: classes options
option_spec = {
"members": _parse_members,
"skip-constructor": lambda x: True,
"skip-attribute-value": lambda x: True,
"undoc-members": lambda x: True,
"private-members": lambda x: True,
"alias": docutils.parsers.rst.directives.unchanged_required,
"module-alias": docutils.parsers.rst.directives.unchanged_required,
"module-path-alias": docutils.parsers.rst.directives.unchanged_required,
"force-partial-import": lambda x: True,
}
[docs] def handle_signature(self, signature, node):
"""Update the signature *node*."""
env = self.state.document.settings.env.element_environment
module_env = self.state.document.settings.env.module_environment
name = self.options.get("alias", env["name"])
module_id = env["module_id"]
module_name = self.options.get(
"module-alias", module_env[module_id]["name"]
)
node["type"] = "class"
node["id"] = env["id"]
node["module"] = module_name
node["fullname"] = name
node += addnodes.desc_type("class ", "class ")
node += addnodes.desc_addname(module_name + ".", module_name + ".")
node += addnodes.desc_name(name, name)
param_list = addnodes.desc_parameterlist()
for method_environment in env["method"].values():
if method_environment["name"] == "constructor":
for argument in method_environment["arguments"]:
param_list += addnodes.desc_parameter(argument, argument)
node += param_list
return name, module_name
[docs] def before_content(self):
"""Update the content.
Compute the description and import statement if available, and generate
the class nested directive elements to integrate to the content.
"""
env = self.state.document.settings.env.element_environment
module_env = self.state.document.settings.env.module_environment
self.content = self.generate_import_statement(
env, module_env, self.options.get("force-partial-import")
)
self.content += self.generate_description(env)
# Automatic boolean options
options = self.env.config.js_class_options
# Options manually set
skip_constructor = self.options.get(
"skip-constructor", "skip-constructor" in options
)
undoc_members = self.options.get(
"undoc-members", "undoc-members" in options
)
private_members = self.options.get(
"private-members", "private-members" in options
)
members = self.options.get("members", "members" in options)
if members:
rst_elements = {}
whitelist = (
members if isinstance(members, collections.Iterable) else None
)
# Gather class attributes
rst_elements = get_rst_attribute_elements(
env,
whitelist_names=whitelist,
blacklist_ids=env["method"].keys(),
undocumented_members=undoc_members,
private_members=private_members,
skip_value=self.options.get(
"skip-attribute-value", "skip-attribute-value" in options
),
rst_elements=rst_elements
)
# Gather class methods
rst_elements = get_rst_method_elements(
env,
whitelist_names=whitelist,
skip_constructor=skip_constructor,
undocumented_members=undoc_members,
private_members=private_members,
rst_elements=rst_elements,
)
# Add content while respecting the line order
for line_number in sorted(rst_elements.keys()):
for rst_element in rst_elements[line_number]:
self.content += rst_element
[docs]class AutoMethodDirective(BaseDirective):
"""Directive to render :term:`Javascript` class method documentation.
The unique argument should be the identifier of the class method element.
.. sourcecode:: rest
.. js:automethod:: module.AwesomeClass.awesomeMethod
.. seealso::
:ref:`directive/automethod`
"""
#: Javascript method is callable
has_arguments = True
#: Define the Object type
objtype = "method"
[docs] def handle_signature(self, signature, node):
"""Update the signature *node*."""
env = self.state.document.settings.env.element_environment
name = env["name"]
prefix = env["prefix"]
node["type"] = "method"
node["id"] = env["id"]
node["fullname"] = name
if prefix is not None:
node += addnodes.desc_type(prefix + " ", prefix + " ")
node += addnodes.desc_name(name, name)
param_list = addnodes.desc_parameterlist()
for argument in env["arguments"]:
param_list += addnodes.desc_parameter(argument, argument)
node += param_list
class_name = env["class_id"].rsplit(".", 1)[-1]
return class_name + "." + name, None
[docs] def before_content(self):
"""Update the content.
Compute the description if available.
"""
env = self.state.document.settings.env.element_environment
self.content = self.generate_description(env)
[docs]class AutoAttributeDirective(BaseDirective):
"""Directive to render :term:`Javascript` class attribute documentation.
The unique argument should be the identifier of the class attribute element.
.. sourcecode:: rest
.. js:autoattribute:: module.AwesomeClass.DATA
The available options are:
* skip-value:
Indicate whether attribute value should be skipped.
.. seealso::
:ref:`directive/autoattribute`
"""
#: Javascript data are not callable
has_arguments = False
#: Define the Object type
objtype = "attribute"
#: data options
option_spec = {
"skip-value": lambda x: True,
}
[docs] def handle_signature(self, signature, node):
"""Update the signature *node*."""
env = self.state.document.settings.env.element_environment
name = env["name"]
value = env["value"]
prefix = env["prefix"]
node["type"] = "attribute"
node["id"] = env["id"]
node["fullname"] = name
skip_value = self.options.get("skip-value", False)
if prefix is not None:
node += addnodes.desc_type(prefix + " ", prefix + " ")
node += addnodes.desc_name(name, name)
if not skip_value:
node += addnodes.desc_annotation(" = " + value, " = " + value)
class_name = env["class_id"].rsplit(".", 1)[-1]
return class_name + "." + name, None
[docs] def before_content(self):
"""Update the content.
Compute the description if available.
"""
env = self.state.document.settings.env.element_environment
self.content = self.generate_description(env)