Completed
Pull Request — master (#2432)
by Zatreanu
01:47
created

TextRange   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 108
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 108
rs 10
c 0
b 0
f 0
wmc 12
1
import copy
2
3
from coala-utils.decorators import (
4
    enforce_signature, generate_ordering, generate_repr)
5
from coalib.results.TextPosition import TextPosition
6
7
8
@generate_repr("start", "end")
9
@generate_ordering("start", "end")
10
class TextRange:
11
12
    @enforce_signature
13
    def __init__(self, start: TextPosition, end: (TextPosition, None)=None):
14
        """
15
        Creates a new TextRange.
16
17
        :param start:       A TextPosition indicating the start of the range.
18
                            Can't be ``None``.
19
        :param end:         A TextPosition indicating the end of the range. If
20
                            ``None`` is given, the start object will be used
21
                            here.
22
        :raises TypeError:  Raised when
23
                            - start is no TextPosition or None.
24
                            - end is no TextPosition.
25
        :raises ValueError: Raised when end position is smaller than start
26
                            position, because negative ranges are not allowed.
27
        """
28
29
        self._start = start
30
        self._end = end or copy.deepcopy(start)
31
32
        if self._end < start:
33
            raise ValueError("End position can't be less than start position.")
34
35
    @classmethod
36
    def from_values(cls,
37
                    start_line=None,
38
                    start_column=None,
39
                    end_line=None,
40
                    end_column=None):
41
        """
42
        Creates a new TextRange.
43
44
        :param start_line:   The line number of the start position. The first
45
                             line is 1.
46
        :param start_column: The column number of the start position. The first
47
                             column is 1.
48
        :param end_line:     The line number of the end position. If this
49
                             parameter is ``None``, then the end position is set
50
                             the same like start position and end_column gets
51
                             ignored.
52
        :param end_column:   The column number of the end position.
53
        :return:             A TextRange.
54
        """
55
        start = TextPosition(start_line, start_column)
56
        if end_line is None:
57
            end = None
58
        else:
59
            end = TextPosition(end_line, end_column)
60
61
        return cls(start, end)
62
63
    @classmethod
64
    def join(cls, a, b):
65
        """
66
        Creates a new TextRange that covers the area of two overlapping ones
67
68
        :param a: TextRange (needs to overlap b)
69
        :param b: TextRange (needs to overlap a)
70
        :return:  A new TextRange covering the union of the Area of a and b
71
        """
72
        if not isinstance(a, cls) or not isinstance(b, cls):
73
            raise TypeError(
74
                "only instances of {} can be joined".format(cls.__name__))
75
76
        if not a.overlaps(b):
77
            raise ValueError(
78
                    "{}s must overlap to be joined".format(cls.__name__))
79
80
        return cls(min(a.start, b.start), max(a.end, b.end))
81
82
    @property
83
    def start(self):
84
        return self._start
85
86
    @property
87
    def end(self):
88
        return self._end
89
90
    def overlaps(self, other):
91
        return self.start <= other.end and self.end >= other.start
92
93
    def expand(self, text_lines):
94
        """
95
        Passes a new TextRange that covers the same area of a file as this one
96
        would. All values of None get replaced with absolute values.
97
98
        values of None will be interpreted as follows:
99
        self.start.line is None:   -> 1
100
        self.start.column is None: -> 1
101
        self.end.line is None:     -> last line of file
102
        self.end.column is None:   -> last column of self.end.line
103
104
        :param text_lines: File contents of the applicable file
105
        :return:           TextRange with absolute values
106
        """
107
        start_line = self.start.line or 1
108
        start_column = self.start.column or 1
109
        end_line = self.end.line or len(text_lines)
110
        end_column = self.end.column or len(text_lines[end_line - 1])
111
112
        return TextRange.from_values(start_line,
113
                                     start_column,
114
                                     end_line,
115
                                     end_column)
116