Completed
Pull Request — master (#2423)
by
unknown
01:54
created

DocumentationComment.metadata()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
1
from collections import namedtuple
2
3
from coala_utils.decorators import generate_eq, generate_repr
4
5
6
@generate_repr()
7
@generate_eq("documentation", "language", "docstyle",
8
             "indent", "marker", "range")
9
class DocumentationComment:
10
    """
11
    The DocumentationComment holds information about a documentation comment
12
    inside source-code, like position etc.
13
    """
14
    Parameter = namedtuple('Parameter', 'name, desc')
15
    ReturnValue = namedtuple('ReturnValue', 'desc')
16
    Description = namedtuple('Description', 'desc')
17
18
    def __init__(self, documentation, docstyle_definition,
19
                 indent, marker, range):
20
        """
21
        Instantiates a new DocumentationComment.
22
23
        :param documentation: The documentation text.
24
        :param language:      The language of the documention.
25
        :param docstyle:      The docstyle used in the documentation.
26
        :param indent:        The string of indentation used in front
27
                              of the first marker of the documentation.
28
        :param marker:        The three-element tuple with marker strings,
29
                              that identified this documentation comment.
30
        :param range:         The position range of type TextRange.
31
        """
32
        self.documentation = documentation
33
        self.docstyle_definition = docstyle_definition
34
        self.indent = indent
35
        self.marker = marker
36
        self.range = range
37
38
    def __str__(self):
39
        return self.documentation
40
41
    @property
42
    def language(self):
43
        return self.docstyle_definition.language
44
45
    @property
46
    def docstyle(self):
47
        return self.docstyle_definition.docstyle
48
49
    @property
50
    def metadata(self):
51
        return self.docstyle_definition.metadata
52
53
    def parse(self):
54
        """
55
        Parses documentation independent of language and docstyle.
56
57
        :return:
58
            The list of all the parsed sections of the documentation. Every
59
            section is a namedtuple of either ``Description`` or ``Parameter``
60
            or ``ReturnValue``.
61
        :raises NotImplementedError:
62
            When no parsing method is present for the given language and
63
            docstyle.
64
        """
65
        if self.language == "python" and self.docstyle == "default":
66
            return self._parse_documentation_with_symbols(
67
                (":param ", ":"), ":return:")
68
        elif self.language == "python" and self.docstyle == "doxygen":
69
            return self._parse_documentation_with_symbols(
70
                ("@param ", " "), "@return ")
71
        elif self.language == "java" and self.docstyle == "default":
72
            return self._parse_documentation_with_symbols(
73
                ("@param  ", " "), "@return ")
74
        else:
75
            raise NotImplementedError(
76
                "Documentation parsing for {0.language!r} in {0.docstyle!r}"
77
                " has not been implemented yet".format(self))
78
79
    def _parse_documentation_with_symbols(self, param_identifiers,
80
                                          return_identifiers):
81
        """
82
        Parses documentation based on parameter and return symbols.
83
84
        :param param_identifiers:
85
            A tuple of two strings with which a parameter starts and ends.
86
        :param return_identifiers:
87
            The string with which a return description starts.
88
        :return:
89
            The list of all the parsed sections of the documentation. Every
90
            section is a namedtuple of either ``Description`` or ``Parameter``
91
            or ``ReturnValue``.
92
        """
93
        lines = self.documentation.splitlines(keepends=True)
94
95
        parse_mode = self.Description
96
97
        cur_param = ""
98
99
        desc = ""
100
        parsed = []
101
102
        for line in lines:
103
104
            stripped_line = line.strip()
105
106
            if stripped_line.startswith(param_identifiers[0]):
107
                parse_mode = self.Parameter
108
                param_offset = line.find(
109
                    param_identifiers[0]) + len(param_identifiers[0])
110
                splitted = line[param_offset:].split(param_identifiers[1], 1)
111
                cur_param = splitted[0].strip()
112
113
                param_desc = splitted[1]
114
                parsed.append(self.Parameter(name=cur_param, desc=param_desc))
115
116
            elif stripped_line.startswith(return_identifiers):
117
                parse_mode = self.ReturnValue
118
                return_offset = line.find(
119
                    return_identifiers) + len(return_identifiers)
120
                retval_desc = line[return_offset:]
121
                parsed.append(self.ReturnValue(desc=retval_desc))
122
123
            elif parse_mode == self.ReturnValue:
124
                retval_desc += line
125
                parsed.pop()
126
                parsed.append(self.ReturnValue(desc=retval_desc))
127
128
            elif parse_mode == self.Parameter:
129
                param_desc += line
130
                parsed.pop()
131
                parsed.append(self.Parameter(name=cur_param, desc=param_desc))
132
133
            else:
134
                desc += line
135
                # This is inside a try-except for cases where the list
136
                # is empty and has nothing to pop.
137
                try:
138
                    parsed.pop()
139
                except IndexError:
140
                    pass
141
                parsed.append(self.Description(desc=desc))
142
143
        return parsed
144
145
    @classmethod
146
    def from_metadata(cls, doccomment, docstyle_definition,
147
                      marker, indent, range):
148
        r"""
149
        Assembles a list of parsed documentation comment metadata.
150
151
        This function just assembles the documentation comment
152
        itself, without the markers and indentation.
153
154
        >>> from coalib.bearlib.languages.documentation.DocumentationComment \
155
        ...     import DocumentationComment
156
        >>> from coalib.bearlib.languages.documentation.DocstyleDefinition \
157
        ...     import DocstyleDefinition
158
        >>> from coalib.results.TextRange import TextRange
159
        >>> Description = DocumentationComment.Description
160
        >>> Parameter = DocumentationComment.Parameter
161
        >>> python_default = DocstyleDefinition.load("python3", "default")
162
        >>> parsed_doc = [Description(desc='\nDescription\n'),
163
        ...               Parameter(name='age', desc=' Age\n')]
164
        >>> str(DocumentationComment.from_metadata(
165
        ...         parsed_doc, python_default,
166
        ...         python_default.markers[0], 4,
167
        ...         TextRange.from_values(0, 0, 0, 0)))
168
        '\nDescription\n:param age: Age\n'
169
170
        :param doccomment:
171
            The list of parsed documentation comment metadata.
172
        :param docstyle_definition:
173
            The ``DocstyleDefinition`` instance that defines what docstyle is
174
            being used in a documentation comment.
175
        :param marker:
176
            The markers to be used in the documentation comment.
177
        :param indent:
178
            The indentation to be used in the documentation comment.
179
        :param range:
180
            The range of the documentation comment.
181
        :return:
182
            A ``DocumentationComment`` instance of the assembled documentation.
183
        """
184
        assembled_doc = ""
185
        for section in doccomment:
186
            section_desc = section.desc.splitlines(keepends=True)
187
188
            if isinstance(section, cls.Parameter):
189
                assembled_doc += (docstyle_definition.metadata.param_start +
190
                                  section.name +
191
                                  docstyle_definition.metadata.param_end)
192
193
            elif isinstance(section, cls.ReturnValue):
194
                assembled_doc += docstyle_definition.metadata.return_sep
195
196
            assembled_doc += ''.join(section_desc)
197
198
        return DocumentationComment(assembled_doc, docstyle_definition, indent,
199
                                    marker, range)
200
201
    def assemble(self):
202
        """
203
        Assembles parsed documentation to the original documentation.
204
205
        This function assembles the whole documentation comment, with the
206
        given markers and indentation.
207
        """
208
        lines = self.documentation.splitlines(keepends=True)
209
        assembled = self.indent + self.marker[0]
210
        if len(lines) == 0:
211
            return self.marker[0] + self.marker[2]
212
        assembled += lines[0]
213
        assembled += ''.join('\n' if line == '\n' and not self.marker[1]
214
                             else self.indent + self.marker[1] + line
215
                             for line in lines[1:])
216
        return (assembled +
217
                (self.indent if lines[-1][-1] == '\n' else '') +
218
                self.marker[2])
219