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

TextPublisher.lines()   F

Complexity

Conditions 19

Size

Total Lines 80
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 50
dl 0
loc 80
rs 0.5999
c 0
b 0
f 0
cc 19
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like doorstop.core.publishers.text.TextPublisher.lines() 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
import os
6
import textwrap
7
8
from doorstop import common, settings
9
from doorstop.core.publishers.base import BasePublisher, format_level
10
from doorstop.core.types import iter_items
11
12
log = common.logger(__name__)
13
14
15
class TextPublisher(BasePublisher):
16
    """Text publisher."""
17
18
    def __init__(self, obj, ext):
19
        super().__init__(obj, ext)
20
        self.indent = 8
21
        self.width = 79
22
23
    def create_matrix(self, directory):
24
        """No traceability matrix for text."""
25
26
    def create_index(self, directory, index=None, extensions=(".html",), tree=None):
27
        """No index for text."""
28
29
    def format_attr_list(self, item, linkify):
30
        """No attribute lists for text."""
31
32
    def format_item_link(self, item, linkify=True):
33
        """No links for text."""
34
35
    def format_label_links(self, label, links, linkify):
36
        """No links for text."""
37
38
    def format_links(self, items, linkify, to_html=False):
39
        """No links for text."""
40
41
    def lines(self, obj, **_):
42
        """Yield lines for a text report.
43
44
        :param obj: Item, list of Items, or Document to publish
45
        :param indent: number of spaces to indent text
46
        :param width: maximum line length
47
48
        :return: iterator of lines of text
49
50
        """
51
        for item in iter_items(obj):
52
            level = format_level(item.level)
53
54
            if item.heading:
55
                text_lines = item.text.splitlines()
56
                if item.header:
57
                    text_lines.insert(0, item.header)
58
                text = os.linesep.join(text_lines)
59
                # Level and Text
60
                if settings.PUBLISH_HEADING_LEVELS:
61
                    yield "{lev:<{s}}{t}".format(lev=level, s=self.indent, t=text)
62
                else:
63
                    yield "{t}".format(t=text)
64
65
            else:
66
                # Level and UID
67
                if item.header:
68
                    yield "{lev:<{s}}{u} {header}".format(
69
                        lev=level, s=self.indent, u=item.uid, header=item.header
70
                    )
71
                else:
72
                    yield "{lev:<{s}}{u}".format(lev=level, s=self.indent, u=item.uid)
73
74
                # Text
75
                if item.text:
76
                    yield ""  # break before text
77
                    for line in item.text.splitlines():
78
                        yield from self._chunks(line)
79
80
                        if not line:
81
                            yield ""  # break between paragraphs
82
83
                # Reference
84
                if item.ref:
85
                    yield ""  # break before reference
86
                    ref = self.format_ref(item)
87
                    yield from self._chunks(ref)
88
89
                # References
90
                if item.references:
91
                    yield ""  # break before references
92
                    ref = self.format_references(item)
93
                    yield from self._chunks(ref)
94
95
                # Links
96
                if item.links:
97
                    yield ""  # break before links
98
                    if settings.PUBLISH_CHILD_LINKS:
99
                        label = "Parent links: "
100
                    else:
101
                        label = "Links: "
102
                    slinks = label + ", ".join(str(l) for l in item.links)
103
                    yield from self._chunks(slinks)
104
                if settings.PUBLISH_CHILD_LINKS:
105
                    links = item.find_child_links()
106
                    if links:
107
                        yield ""  # break before links
108
                        slinks = "Child links: " + ", ".join(str(l) for l in links)
109
                        yield from self._chunks(slinks)
110
111
                # Attributes
112
                if item.document and item.document.publish:
113
                    yield ""
114
                    for attr in item.document.publish:
115
                        if not item.attribute(attr):
116
                            continue
117
                        attr_line = "{}: {}".format(attr, item.attribute(attr))
118
                        yield from self._chunks(attr_line)
119
120
            yield ""  # break between items
121
122 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...
123
        """Format an external reference in text."""
124
        if settings.CHECK_REF:
125
            path, line = item.find_ref()
126
            path = path.replace("\\", "/")  # always use unix-style paths
127
            if line:
128
                return "Reference: {p} (line {line})".format(p=path, line=line)
129
            else:
130
                return "Reference: {p}".format(p=path)
131
        else:
132
            return "Reference: '{r}'".format(r=item.ref)
133
134 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...
135
        """Format an external reference in text."""
136
        if settings.CHECK_REF:
137
            ref = item.find_references()
138
            text_refs = []
139
            for ref_item in ref:
140
                path, line = ref_item
141
                path = path.replace("\\", "/")  # always use unix-style paths
142
                if line:
143
                    text_refs.append("{p} (line {line})".format(p=path, line=line))
144
                else:
145
                    text_refs.append("{p}".format(p=path))
146
            return "Reference: {}".format(", ".join(ref for ref in text_refs))
147
        else:
148
            references = item.references
149
            text_refs = []
150
            for ref_item in references:
151
                path = ref_item["path"]
152
                path = path.replace("\\", "/")  # always use unix-style paths
153
                text_refs.append("'{p}'".format(p=path))
154
            return "Reference: {}".format(", ".join(text_ref for text_ref in text_refs))
155
156
    def _chunks(self, text):
157
        """Yield wrapped lines of text."""
158
        yield from textwrap.wrap(
159
            text,
160
            self.width,
161
            initial_indent=" " * self.indent,
162
            subsequent_indent=" " * self.indent,
163
        )
164