Completed
Pull Request — master (#2758)
by John
11:46
created

SpacingHelper.replace_spaces_with_tabs()   F

Complexity

Conditions 11

Size

Total Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
c 0
b 0
f 0
dl 0
loc 65
rs 3.8571

1 Method

Rating   Name   Duplication   Size   Complexity  
A SpacingHelper.previous_whitespace() 0 15 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 SpacingHelper.replace_spaces_with_tabs() 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
from coalib.bearlib.abstractions.SectionCreatable import SectionCreatable
2
from coala_utils.decorators import enforce_signature
3
4
5
class SpacingHelper(SectionCreatable):
6
    DEFAULT_TAB_WIDTH = 4
7
8
    def __init__(self, tab_width: int=DEFAULT_TAB_WIDTH):
9
        """
10
        Creates a helper object for spacing operations.
11
12
        :param tab_width: The number of spaces which visually equals a tab.
13
        """
14
        SectionCreatable.__init__(self)
15
        if not isinstance(tab_width, int):
16
            raise TypeError("The 'tab_width' parameter should be an integer.")
17
18
        self.tab_width = tab_width
19
20
    @enforce_signature
21
    def get_indentation(self, line: str):
22
        """
23
        Checks the lines indentation.
24
25
        :param line: A string to check for indentation.
26
        :return:     The indentation count in spaces.
27
        """
28
        count = 0
29
        for char in line:
30
            if char == ' ':
31
                count += 1
32
                continue
33
34
            if char == '\t':
35
                count += self.tab_width - (count % self.tab_width)
36
                continue
37
38
            break
39
40
        return count
41
42
    @enforce_signature
43
    def replace_tabs_with_spaces(self, line: str):
44
        """
45
        Replaces tabs in this line with the appropriate number of spaces.
46
47
        Example: " \t" will be converted to "    ", assuming the tab_width is
48
        set to 4.
49
50
        :param line: The string with tabs to replace.
51
        :return:     A string with no tabs.
52
        """
53
        for t_position, t_length in sorted(self.yield_tab_lengths(line),
54
                                           reverse=True):
55
            line = line[:t_position] + t_length * ' ' + line[t_position+1:]
56
57
        return line
58
59
    @enforce_signature
60
    def yield_tab_lengths(self, input: str):
61
        """
62
        Yields position and size of tabs in a input string.
63
64
        :param input: The string with tabs.
65
        """
66
        tabless_position = 0
67
        for index, char in enumerate(input):
68
            if char == '\t':
69
                space_count = (self.tab_width - tabless_position
70
                               % self.tab_width)
71
                yield index, space_count
72
                tabless_position += space_count
73
                continue
74
75
            tabless_position += 1
76
77
    @enforce_signature
78
    def replace_spaces_with_tabs(self, line: str):
79
        """
80
        Replaces spaces with tabs where possible. However in no case only one
81
        space will be replaced by a tab.
82
83
        Example: " \t   a_text   another" will be converted to
84
        "\t   a_text\tanother", assuming the tab_width is set to 4.
85
86
        :param line: The string with spaces to replace.
87
        :return:     The converted string.
88
        """
89
        def previous_whitespace():
90
            # Find the previous real character, and remaining chars to fill tab
91
            non_whitespace_position = tabless_position - currspaces
92
            tab_fill = non_whitespace_position % self.tab_width
93
94
            if tab_fill and tab_fill + currspaces >= self.tab_width:
95
                whitespace = "\t"
96
                remaining_spaces = currspaces - (self.tab_width - tab_fill)
97
            else:
98
                whitespace = ""
99
                remaining_spaces = currspaces
100
101
            whitespace += "\t" * (remaining_spaces // self.tab_width)
102
            whitespace += " " * (remaining_spaces % self.tab_width)
103
            return whitespace
104
105
        currspaces = 0
106
        result = ""
107
        # Tracking the index of the string isnt enough because tabs are
108
        # spanning over multiple columns
109
        tabless_position = 0
110
        previous_char = None
111
        for char in line:
112
            if char == " ":
113
                currspaces += 1
114
                tabless_position += 1
115
            elif char == "\t":
116
                space_count = (self.tab_width - tabless_position
117
                               % self.tab_width)
118
                currspaces += space_count
119
                tabless_position += space_count
120
            else:
121
                if currspaces:
122
                    if currspaces == 1:
123
                        result += previous_char
124
                    else:
125
                        ws = previous_whitespace()
126
                        result += ws
127
128
                result += char
129
                currspaces = 0
130
                tabless_position += 1
131
132
            previous_char = char
133
134
        if currspaces:
135
            if currspaces == 1:
136
                result += previous_char
137
            else:
138
                char = None
139
                result += previous_whitespace()
140
141
        return result
142