Passed
Push — develop ( e91dc8...2ae66f )
by Jace
04:37 queued 14s
created

doorstop.core.publishers.markdown   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 199
Duplicated Lines 27.14 %

Importance

Changes 0
Metric Value
wmc 43
eloc 122
dl 54
loc 199
rs 8.96
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A MarkdownPublisher.create_matrix() 0 2 1
F MarkdownPublisher._lines_markdown() 10 100 22
A MarkdownPublisher.format_label_links() 0 6 2
A MarkdownPublisher.create_index() 0 2 1
A MarkdownPublisher.format_links() 0 7 2
A MarkdownPublisher.lines() 0 10 1
A MarkdownPublisher.format_item_link() 10 10 4
A MarkdownPublisher.format_references() 23 23 5
A MarkdownPublisher.format_ref() 11 11 3
A MarkdownPublisher.format_attr_list() 0 3 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like doorstop.core.publishers.markdown often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# SPDX-License-Identifier: LGPL-3.0-only
2
3
"""Functions to publish documents and items."""
4
5
6
from doorstop import common, settings
7
from doorstop.core.publishers.base import BasePublisher, format_level
8
from doorstop.core.types import is_item, iter_items
9
10
log = common.logger(__name__)
11
12
13
class MarkdownPublisher(BasePublisher):
14
    """Markdown publisher."""
15
16
    def create_index(self, directory, index=None, extensions=(".html",), tree=None):
17
        """No index for Markdown."""
18
19
    def create_matrix(self, directory):
20
        """No traceability matrix for Markdown."""
21
22
    def format_attr_list(self, item, linkify):
23
        """Create a Markdown attribute list for a heading."""
24
        return " {{#{u} }}".format(u=item.uid) if linkify else ""
25
26 View Code Duplication
    def format_ref(self, item):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
27
        """Format an external reference in Markdown."""
28
        if settings.CHECK_REF:
29
            path, line = item.find_ref()
30
            path = path.replace("\\", "/")  # always use unix-style paths
31
            if line:
32
                return "> `{p}` (line {line})".format(p=path, line=line)
33
            else:
34
                return "> `{p}`".format(p=path)
35
        else:
36
            return "> '{r}'".format(r=item.ref)
37
38 View Code Duplication
    def format_references(self, item):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
39
        """Format an external reference in Markdown."""
40
        if settings.CHECK_REF:
41
            references = item.find_references()
42
            text_refs = []
43
            for ref_item in references:
44
                path, line = ref_item
45
                path = path.replace("\\", "/")  # always use unix-style paths
46
47
                if line:
48
                    text_refs.append("> `{p}` (line {line})".format(p=path, line=line))
49
                else:
50
                    text_refs.append("> `{p}`".format(p=path))
51
52
            return "\n".join(ref for ref in text_refs)
53
        else:
54
            references = item.references
55
            text_refs = []
56
            for ref_item in references:
57
                path = ref_item["path"]
58
                path = path.replace("\\", "/")  # always use unix-style paths
59
                text_refs.append("> '{r}'".format(r=path))
60
            return "\n".join(ref for ref in text_refs)
61
62
    def format_links(self, items, linkify, to_html=False):
63
        """Format a list of linked items in Markdown."""
64
        links = []
65
        for item in items:
66
            link = self.format_item_link(item, linkify=linkify)
67
            links.append(link)
68
        return ", ".join(links)
69
70 View Code Duplication
    def format_item_link(self, item, linkify=True):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
71
        """Format an item link in Markdown."""
72
        if linkify and is_item(item):
73
            if item.header:
74
                return "[{u} {h}]({p}.md#{u})".format(
75
                    u=item.uid, h=item.header, p=item.document.prefix
76
                )
77
            return "[{u}]({p}.md#{u})".format(u=item.uid, p=item.document.prefix)
78
        else:
79
            return str(item.uid)  # if not `Item`, assume this is an `UnknownItem`
80
81
    def format_label_links(self, label, links, linkify):
82
        """Join a string of label and links with formatting."""
83
        if linkify:
84
            return "*{lb}* {ls}".format(lb=label, ls=links)
85
        else:
86
            return "*{lb} {ls}*".format(lb=label, ls=links)
87
88
    def lines(self, obj, **kwargs):
89
        """Yield lines for a Markdown report.
90
91
        :param obj: Item, list of Items, or Document to publish
92
        :param linkify: turn links into hyperlinks (for conversion to HTML)
93
94
        :return: iterator of lines of text
95
96
        """
97
        yield from self._lines_markdown(obj, **kwargs)
98
99
    def _lines_markdown(self, obj, **kwargs):
100
        """Yield lines for a Markdown report.
101
102
        :param obj: Item, list of Items, or Document to publish
103
        :param linkify: turn links into hyperlinks (for conversion to HTML)
104
105
        :return: iterator of lines of text
106
107
        """
108
        linkify = kwargs.get("linkify", False)
109
        to_html = kwargs.get("to_html", False)
110
        for item in iter_items(obj):
111
            heading = "#" * item.depth
112
            level = format_level(item.level)
113
114
            if item.heading:
115
                text_lines = item.text.splitlines()
116
                if item.header:
117
                    text_lines.insert(0, item.header)
118
                # Level and Text
119
                if settings.PUBLISH_HEADING_LEVELS:
120
                    standard = "{h} {lev} {t}".format(
121
                        h=heading, lev=level, t=text_lines[0] if text_lines else ""
122
                    )
123
                else:
124
                    standard = "{h} {t}".format(
125
                        h=heading, t=text_lines[0] if text_lines else ""
126
                    )
127
                attr_list = self.format_attr_list(item, True)
128
                yield standard + attr_list
129
                yield from text_lines[1:]
130
            else:
131
                uid = item.uid
132
                if settings.ENABLE_HEADERS:
133
                    if item.header:
134
                        uid = "{h} <small>{u}</small>".format(h=item.header, u=item.uid)
135
                    else:
136
                        uid = "{u}".format(u=item.uid)
137
138
                # Level and UID
139
                if settings.PUBLISH_BODY_LEVELS:
140
                    standard = "{h} {lev} {u}".format(h=heading, lev=level, u=uid)
141
                else:
142
                    standard = "{h} {u}".format(h=heading, u=uid)
143
144
                attr_list = self.format_attr_list(item, True)
145
                yield standard + attr_list
146
147
                # Text
148
                if item.text:
149
                    yield ""  # break before text
150
                    yield from item.text.splitlines()
151
152
                # Reference
153
                if item.ref:
154
                    yield ""  # break before reference
155
                    yield self.format_ref(item)
156
157
                # Reference
158
                if item.references:
159
                    yield ""  # break before reference
160
                    yield self.format_references(item)
161
162
                # Parent links
163 View Code Duplication
                if item.links:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
164
                    yield ""  # break before links
165
                    items2 = item.parent_items
166
                    if settings.PUBLISH_CHILD_LINKS:
167
                        label = "Parent links:"
168
                    else:
169
                        label = "Links:"
170
                    links = self.format_links(items2, linkify, to_html=to_html)
171
                    label_links = self.format_label_links(label, links, linkify)
172
                    yield label_links
173
174
                # Child links
175
                if settings.PUBLISH_CHILD_LINKS:
176
                    items2 = item.find_child_items()
177
                    if items2:
178
                        yield ""  # break before links
179
                        label = "Child links:"
180
                        links = self.format_links(items2, linkify, to_html=to_html)
181
                        label_links = self.format_label_links(label, links, linkify)
182
                        yield label_links
183
184
                # Add custom publish attributes
185
                if item.document and item.document.publish:
186
                    header_printed = False
187
                    for attr in item.document.publish:
188
                        if not item.attribute(attr):
189
                            continue
190
                        if not header_printed:
191
                            header_printed = True
192
                            yield ""
193
                            yield "| Attribute | Value |"
194
                            yield "| --------- | ----- |"
195
                        yield "| {} | {} |".format(attr, item.attribute(attr))
196
                    yield ""
197
198
            yield ""  # break between items
199