coalib.bearlib.languages.documentation.DocumentationComment — coala 0.9.0.dev99999999999999 documentation

# Source code for coalib.bearlib.languages.documentation.DocumentationComment

    from collections import namedtuple

    from coala_utils.decorators import generate_eq, generate_repr

    @generate_repr()
    @generate_eq('documentation', 'language', 'docstyle',
                 'indent', 'marker', 'range')
    class DocumentationComment:
        """
        The DocumentationComment holds information about a documentation comment
        inside source-code, like position etc.
        """
        Parameter = namedtuple('Parameter', 'name, desc')
        ReturnValue = namedtuple('ReturnValue', 'desc')
        Description = namedtuple('Description', 'desc')

        def __init__(self, documentation, docstyle_definition,
                     indent, marker, range):
            """
            Instantiates a new DocumentationComment.

            :param documentation: The documentation text.
            :param language:      The language of the documention.
            :param docstyle:      The docstyle used in the documentation.
            :param indent:        The string of indentation used in front
                                  of the first marker of the documentation.
            :param marker:        The three-element tuple with marker strings,
                                  that identified this documentation comment.
            :param range:         The position range of type TextRange.
            """
            self.documentation = documentation
            self.docstyle_definition = docstyle_definition
            self.indent = indent
            self.marker = marker
            self.range = range

        def __str__(self):
            return self.documentation

        @property
        def language(self):
            return self.docstyle_definition.language

        @property
        def docstyle(self):
            return self.docstyle_definition.docstyle

        @property
        def metadata(self):
            return self.docstyle_definition.metadata

        def parse(self):
            """
            Parses documentation independent of language and docstyle.

            :return:
                The list of all the parsed sections of the documentation. Every
                section is a namedtuple of either ``Description`` or ``Parameter``
                or ``ReturnValue``.
            :raises NotImplementedError:
                When no parsing method is present for the given language and
                docstyle.
            """
            if self.language == 'python' and self.docstyle == 'default':
                return self._parse_documentation_with_symbols(
                    (':param ', ':'), ':return:')
            elif self.language == 'python' and self.docstyle == 'doxygen':
                return self._parse_documentation_with_symbols(
                    ('@param ', ' '), '@return ')
            elif self.language == 'java' and self.docstyle == 'default':
                return self._parse_documentation_with_symbols(
                    ('@param  ', ' '), '@return ')
            else:
                raise NotImplementedError(
                    'Documentation parsing for {0.language!r} in {0.docstyle!r}'
                    ' has not been implemented yet'.format(self))

        def _parse_documentation_with_symbols(self, param_identifiers,
                                              return_identifiers):
            """
            Parses documentation based on parameter and return symbols.

            :param param_identifiers:
                A tuple of two strings with which a parameter starts and ends.
            :param return_identifiers:
                The string with which a return description starts.
            :return:
                The list of all the parsed sections of the documentation. Every
                section is a namedtuple of either ``Description`` or ``Parameter``
                or ``ReturnValue``.
            """
            lines = self.documentation.splitlines(keepends=True)

            parse_mode = self.Description

            cur_param = ''

            desc = ''
            parsed = []

            for line in lines:

                stripped_line = line.strip()

                if stripped_line.startswith(param_identifiers[0]):
                    parse_mode = self.Parameter
                    param_offset = line.find(
                        param_identifiers[0]) + len(param_identifiers[0])
                    splitted = line[param_offset:].split(param_identifiers[1], 1)
                    cur_param = splitted[0].strip()

                    param_desc = splitted[1]
                    parsed.append(self.Parameter(name=cur_param, desc=param_desc))

                elif stripped_line.startswith(return_identifiers):
                    parse_mode = self.ReturnValue
                    return_offset = line.find(
                        return_identifiers) + len(return_identifiers)
                    retval_desc = line[return_offset:]
                    parsed.append(self.ReturnValue(desc=retval_desc))

                elif parse_mode == self.ReturnValue:
                    retval_desc += line
                    parsed.pop()
                    parsed.append(self.ReturnValue(desc=retval_desc))

                elif parse_mode == self.Parameter:
                    param_desc += line
                    parsed.pop()
                    parsed.append(self.Parameter(name=cur_param, desc=param_desc))

                else:
                    desc += line
                    # This is inside a try-except for cases where the list
                    # is empty and has nothing to pop.
                    try:
                        parsed.pop()
                    except IndexError:
                        pass
                    parsed.append(self.Description(desc=desc))

            return parsed

        @classmethod
        def from_metadata(cls, doccomment, docstyle_definition,
                          marker, indent, range):
            r"""
            Assembles a list of parsed documentation comment metadata.

            This function just assembles the documentation comment
            itself, without the markers and indentation.

            >>> from coalib.bearlib.languages.documentation.DocumentationComment \
            ...     import DocumentationComment
            >>> from coalib.bearlib.languages.documentation.DocstyleDefinition \
            ...     import DocstyleDefinition
            >>> from coalib.results.TextRange import TextRange
            >>> Description = DocumentationComment.Description
            >>> Parameter = DocumentationComment.Parameter
            >>> python_default = DocstyleDefinition.load("python3", "default")
            >>> parsed_doc = [Description(desc='\nDescription\n'),
            ...               Parameter(name='age', desc=' Age\n')]
            >>> str(DocumentationComment.from_metadata(
            ...         parsed_doc, python_default,
            ...         python_default.markers[0], 4,
            ...         TextRange.from_values(0, 0, 0, 0)))
            '\nDescription\n:param age: Age\n'

            :param doccomment:
                The list of parsed documentation comment metadata.
            :param docstyle_definition:
                The ``DocstyleDefinition`` instance that defines what docstyle is
                being used in a documentation comment.
            :param marker:
                The markers to be used in the documentation comment.
            :param indent:
                The indentation to be used in the documentation comment.
            :param range:
                The range of the documentation comment.
            :return:
                A ``DocumentationComment`` instance of the assembled documentation.
            """
            assembled_doc = ''
            for section in doccomment:
                section_desc = section.desc.splitlines(keepends=True)

                if isinstance(section, cls.Parameter):
                    assembled_doc += (docstyle_definition.metadata.param_start +
                                      section.name +
                                      docstyle_definition.metadata.param_end)

                elif isinstance(section, cls.ReturnValue):
                    assembled_doc += docstyle_definition.metadata.return_sep

                assembled_doc += ''.join(section_desc)

            return DocumentationComment(assembled_doc, docstyle_definition, indent,
                                        marker, range)

        def assemble(self):
            """
            Assembles parsed documentation to the original documentation.

            This function assembles the whole documentation comment, with the
            given markers and indentation.
            """
            lines = self.documentation.splitlines(keepends=True)
            assembled = self.indent + self.marker[0]
            if len(lines) == 0:
                return self.marker[0] + self.marker[2]
            assembled += lines[0]
            assembled += ''.join('\n' if line == '\n' and not self.marker[1]
                                 else self.indent + self.marker[1] + line
                                 for line in lines[1:])
            return (assembled +
                    (self.indent if lines[-1][-1] == '\n' else '') +
                    self.marker[2])