Source code for champollion.parser.js_class

# :coding: utf-8

import re
import functools

from .helper import filter_comments
from .helper import collapse_all
from .helper import get_docstring


#: Regular Expression pattern for classes
_CLASS_PATTERN = re.compile(
    r"(?P<start_regex>(\n|^)) *(?P<export>export +)?(?P<default>default +)?"
    r"(class +(?P<class_name>\w+)|(const|let|var) +(?P<data_name>\w+) "
    r"*= *class +\w+)"
    r"( +extends +(?P<mother_class>[\w._-]+))? *{"
)

#: Regular Expression pattern for class methods
_CLASS_METHOD_PATTERN = re.compile(
    r"(?P<start_regex>(\n|^)) *(?P<prefix>(static|get|set) +)?"
    r"(?P<method_name>[\w._-]+) *\([\n ]*(?P<arguments>.*?)[\n ]*\) *{",
    re.DOTALL
)

#: Regular Expression pattern for class arrow methods
_CLASS_METHOD_ARROW_PATTERN = re.compile(
    r"(?P<start_regex>(\n|^)) *(?P<prefix>static +)?(?P<method_name>\w+) *= *"
    r"(\([\n ]*(?P<arguments>.*?)[\n ]*\)|(?P<single_argument>[\w._-]+)) *"
    r"=> *",
    re.DOTALL
)

#: Regular Expression pattern for class attribute
_CLASS_ATTRIBUTE_PATTERN = re.compile(
    r"(?P<start_regex>(\n|^)) *(?P<prefix>static +)?"
    r"(?P<name>[\w._-]+) *= *(?P<value>.+?;)",
    re.DOTALL
)


[docs]def fetch_environment(content, module_id): """Return class environment dictionary from *content*. *module_id* represent the identifier of the module. The environment is in the form of:: { "moduleName.AwesomeClass": { "id": "module.AwesomeClass", "name": "AwesomeClass", "parent": "MotherClass", "line": 42, "description": "Class doc.\\n\\nDetailed description." "id": "moduleName.AwesomeClass", "module_id": "moduleName", "exported": False, "default": False, "name": "AwesomeClass", "parent": None, "line_number": 2, "description": "Class doc.\\n\\nDetailed description." "method": { "moduleName.AwesomeClass.awesomeMethod": { .... } }, "attribute": { "moduleName.AwesomeClass.DATA": { .... } } }, ... } """ environment = {} lines = content.split("\n") # The comment filter is made during the collapse content process to # preserve the class content with all comments (and docstrings!) content, collapsed_content = collapse_all(content, filter_comment=True) for match in _CLASS_PATTERN.finditer(content): class_name = match.group("class_name") if class_name is None: class_name = match.group("data_name") class_id = ".".join([module_id, class_name]) line_number = ( content[:match.start()].count("\n") + match.group("start_regex").count("\n") + 1 ) method_environment = {} attribute_environment = {} if line_number in collapsed_content.keys(): class_content = collapsed_content[line_number][1:-1] method_environment = fetch_methods_environment( class_content, class_id, line_number=line_number-1 ) attribute_environment = fetch_attribute_environment( class_content, class_id, line_number=line_number-1 ) class_environment = { "id": class_id, "module_id": module_id, "exported": match.group("export") is not None, "default": match.group("default") is not None, "name": class_name, "parent": match.group("mother_class"), "line_number": line_number, "description": get_docstring(line_number, lines), "method": method_environment, "attribute": attribute_environment } environment[class_id] = class_environment return environment
[docs]def fetch_methods_environment(content, class_id, line_number=0): """Return function environment dictionary from *content*. *class_id* represent the identifier of the method class. *line_number* is the first line number of content. The environment is in the form of:: { "moduleName.AwesomeClass.awesomeMethod": { "id": "moduleName.AwesomeClass.awesomeMethod", "class_id": "moduleName.AwesomeClass", "module_id": "moduleName", "name": "awesomeMethod", "prefix": "get", "arguments": ["argument1", "argument2"], "line_number": 5, "description": "Method doc.\\n\\nDetailed description." } } """ environment = {} lines = content.split("\n") content = filter_comments(content) content = collapse_all(content)[0] for match_iter in ( _CLASS_METHOD_ARROW_PATTERN.finditer(content), _CLASS_METHOD_PATTERN.finditer(content) ): for match in match_iter: method_id = ".".join([class_id, match.group("method_name")]) prefix = match.group("prefix") if prefix is not None: prefix = prefix.strip() # Add the prefix to the method ID if the method if a getter or # a setter as several method could have the same name. if prefix in ["get", "set"]: method_id += "." + prefix _line_number = ( content[:match.start()].count("\n") + match.group("start_regex").count("\n") + 1 ) arguments_matched = match.group("arguments") if arguments_matched is None: arguments_matched = match.group("single_argument") arguments = list(filter(lambda x: len(x), [ arg.strip() for arg in arguments_matched.split(",") ])) method_environment = { "id": method_id, "class_id": class_id, "module_id": class_id.rsplit(".", 1)[0], "name": match.group("method_name"), "prefix": prefix, "arguments": arguments, "line_number": _line_number + line_number, "description": get_docstring(_line_number, lines) } environment[method_id] = method_environment return environment
[docs]def fetch_attribute_environment(content, class_id, line_number=0): """Return attribute environment dictionary from *content*. *class_id* represent the identifier of the attribute class. *line_number* is the first line number of content. The environment is in the form of:: { "moduleName.AwesomeClass.DATA": { "id": "moduleName.AwesomeClass.DATA", "class_id": "moduleName.AwesomeClass", "module_id": "moduleName", "name": "DATA", "prefix": "static", "value": "42", "line_number": 8, "description": "Attribute doc.\\n\\nDetailed description." } } """ environment = {} lines = content.split("\n") # The comment filter is made during the collapse content process to # preserve the entire value (with semi-colons and docstrings!) content, collapsed_content = collapse_all(content, filter_comment=True) for match in _CLASS_ATTRIBUTE_PATTERN.finditer(content): attribute_id = ".".join([class_id, match.group("name")]) prefix = match.group("prefix") if prefix is not None: prefix = prefix.strip() value = match.group("value") _line_number = ( content[:match.start()].count("\n") + match.group("start_regex").count("\n") + 1 ) for _value_line_number in range( _line_number, _line_number + value.count("\n") + 1 ): if "{}" in value and _value_line_number in collapsed_content.keys(): value = value.replace( "{}", collapsed_content[_value_line_number] ) # Do not keep semi-colon in value if value.endswith(";"): value = value[:-1] attribute_environment = { "id": attribute_id, "class_id": class_id, "module_id": class_id.rsplit(".", 1)[0], "name": match.group("name"), "prefix": prefix, "value": functools.reduce(_clean_value, value.split('\n')).strip(), "line_number": _line_number + line_number, "description": get_docstring(_line_number, lines) } environment[attribute_id] = attribute_environment return environment
def _clean_value(line1, line2): """Clean up attribute value for display.""" _line1 = line1.strip() _line2 = line2.strip() # Let trailing space to make the code easier to read if _line1[-1:] in ["{", "}", "(", ")", "[", "]", ";", ","]: _line1 += " " return _line1 + _line2