Source code for coalib.results.TextRange
import copy
from coala_utils.decorators import (
enforce_signature, generate_ordering, generate_repr)
from coalib.results.TextPosition import TextPosition
[docs]@generate_repr('start', 'end')
@generate_ordering('start', 'end')
class TextRange:
@enforce_signature
def __init__(self, start: TextPosition, end: (TextPosition, None) = None):
"""
Creates a new TextRange.
:param start: A TextPosition indicating the start of the range.
Can't be ``None``.
:param end: A TextPosition indicating the end of the range. If
``None`` is given, the start object will be used
here.
:raises TypeError: Raised when
- start is not of type TextPosition.
- end is neither of type TextPosition, nor is it
None.
:raises ValueError: Raised when end position is smaller than start
position, because negative ranges are not allowed.
"""
self._start = start
self._end = copy.deepcopy(start) if end is None else end
if self._end < start:
raise ValueError("End position can't be less than start position.")
[docs] @classmethod
def from_values(cls,
start_line=None,
start_column=None,
end_line=None,
end_column=None):
"""
Creates a new TextRange.
:param start_line: The line number of the start position. The first
line is 1.
:param start_column: The column number of the start position. The first
column is 1.
:param end_line: The line number of the end position. If this
parameter is ``None``, then the end position is set
the same like start position and end_column gets
ignored.
:param end_column: The column number of the end position.
:return: A TextRange.
"""
start = TextPosition(start_line, start_column)
if end_line is None:
end = None
else:
end = TextPosition(end_line, end_column)
return cls(start, end)
[docs] @classmethod
def join(cls, a, b):
"""
Creates a new TextRange that covers the area of two overlapping ones
:param a: TextRange (needs to overlap b)
:param b: TextRange (needs to overlap a)
:return: A new TextRange covering the union of the Area of a and b
"""
if not isinstance(a, cls) or not isinstance(b, cls):
raise TypeError(
'only instances of {} can be joined'.format(cls.__name__))
if not a.overlaps(b):
raise ValueError(
'{}s must overlap to be joined'.format(cls.__name__))
return cls(min(a.start, b.start), max(a.end, b.end))
@property
def start(self):
return self._start
@property
def end(self):
return self._end
[docs] def expand(self, text_lines):
"""
Passes a new TextRange that covers the same area of a file as this one
would. All values of None get replaced with absolute values.
values of None will be interpreted as follows:
self.start.line is None: -> 1
self.start.column is None: -> 1
self.end.line is None: -> last line of file
self.end.column is None: -> last column of self.end.line
:param text_lines: File contents of the applicable file
:return: TextRange with absolute values
"""
start_line = 1 if self.start.line is None else self.start.line
start_column = 1 if self.start.column is None else self.start.column
end_line = len(text_lines) if self.end.line is None else self.end.line
end_column = (len(text_lines[end_line - 1]) if self.end.column is None
else self.end.column)
return TextRange.from_values(start_line,
start_column,
end_line,
end_column)
def __contains__(self, item):
return item.start >= self.start and item.end <= self.end