Skip to content

Commit d1f7fc9

Browse files
committed
fix: Add docstrings to range_spec.py
1 parent 7e8ac1d commit d1f7fc9

File tree

1 file changed

+82
-19
lines changed

1 file changed

+82
-19
lines changed

src/text_manipulation/range_spec.py

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
"""
2+
This module provides utilities for working with ranges of text in source code.
3+
4+
It includes classes and functions for specifying ranges, finding lines,
5+
and manipulating text within those ranges. The main components are:
6+
7+
- RangeSpec: A class representing a range of lines in a text.
8+
- IdentifierBoundaries: A class representing the boundaries of an identifier in code.
9+
- Various utility functions for working with these classes and text manipulation.
10+
"""
11+
112
import re
213
from collections.abc import Sequence
314
from typing import NamedTuple
@@ -12,59 +23,86 @@
1223

1324
@total_ordering
1425
class RangeSpec(NamedTuple):
26+
"""
27+
Represents a range of lines in a text, with start and end indices and indentation.
28+
29+
This class is used to specify a range of lines in a text, typically for
30+
text manipulation operations. It includes methods for comparing ranges,
31+
modifying the range, and performing operations on text using the range.
32+
33+
Attributes:
34+
start (int): The starting line index of the range.
35+
end (int): The ending line index of the range (exclusive).
36+
indent (int): The indentation level of the range.
37+
"""
1538
start: int
1639
end: int
1740
indent: int = 0
1841

1942
def __str__(self):
43+
"""Return a string representation of the RangeSpec."""
2044
return (f'{self.start}:{self.end}' if self.as_index is None else f'%{self.as_index}') + f'@{self.indent}'
2145

2246
def __lt__(self, other):
47+
"""Compare if this range is strictly before another range."""
2348
return self.end < other.start
2449

2550
def __le__(self, other):
51+
"""Compare if this range is before or adjacent to another range."""
2652
return self.end <= other.start
2753

2854
def __gt__(self, other):
55+
"""Compare if this range is strictly after another range."""
2956
return self.start > other.end
3057

3158
def __ge__(self, other):
59+
"""Compare if this range is after or adjacent to another range."""
3260
return self.start >= other.end
3361

3462
@property
3563
def line_count(self):
64+
"""Return the number of lines in the range."""
3665
return self.end - self.start
3766

3867
@property
3968
def as_index(self) -> int | None:
69+
"""Return the start index if the range is empty, otherwise None."""
4070
return None if self.line_count else self.start
4171

4272
@property
4373
def collapsed(self):
74+
"""Return a new RangeSpec with the same start but zero length."""
4475
return self.set_line_count(0)
4576

4677
def set_line_count(self, range_len: int):
78+
"""Return a new RangeSpec with the specified line count."""
4779
return self._replace(end=self.start + range_len)
4880

4981
def inc(self, count: int = 1):
82+
"""Return a new RangeSpec shifted forward by the specified count."""
5083
return self._replace(start=self.start + count, end=self.end + count)
5184

5285
def dec(self, count: int = 1):
86+
"""Return a new RangeSpec shifted backward by the specified count."""
5387
return self._replace(start=self.start - count, end=self.end - count)
5488

5589
def read(self, src: Sequence[str]) -> Sequence[str]:
90+
"""Read and return the lines from the source sequence specified by this range."""
5691
return src[self.start:self.end]
5792

5893
def write(self, src: Sequence[str], target: Sequence[str]):
94+
"""Write the source lines into the target sequence at the position specified by this range."""
5995
target[self.start:self.end] = src
6096

6197
def delete(self, src: Sequence[str]) -> Sequence[str]:
98+
"""Delete the lines specified by this range from the source sequence and return the deleted lines."""
6299
result = self.read(src)
63100
del src[self.start:self.end]
64101
return result
65102

66103
@staticmethod
67104
def normalize_line(line: str):
105+
"""Normalize a line by replacing non-word characters with dots and stripping whitespace."""
68106
return re.sub(r'[^\w]', '.', line.strip(), flags=re.UNICODE)
69107

70108
@classmethod
@@ -77,35 +115,31 @@ def from_line_marker(
77115
"""
78116
Find the index of a specified line within a list of strings, considering different match types and an offset.
79117
80-
This function searches for a given line within a list, considering 4 types of matches in order of priority:
118+
This method searches for a given line within a list, considering 4 types of matches in order of priority:
81119
1. Exact match
82120
2. Stripped match (ignoring leading and trailing whitespace)
83121
3. Normalized match (ignoring non-alphanumeric characters)
84122
4. Partial (Searching for a substring, using `casefold` to ignore upper- and lower-case differences).
85123
86-
The function applies the offset across all match types while maintaining the priority order.
124+
The method applies the offset across all match types while maintaining the priority order.
87125
88-
:Args:
89-
:param lines: The list of strings to search through.
90-
:param search_term:
91-
search_marker.value: The line to search for.
92-
search_marker.offset: The number of matches to skip before returning a result.
126+
Args:
127+
lines (Sequence[str]): The list of strings to search through.
128+
search_term (Marker): A Marker object containing:
129+
- value: The line to search for.
130+
- offset: The number of matches to skip before returning a result.
93131
0 skips no match and returns the first match, 1 returns the second match, and so on.
94-
:param search_range: The index to start the search from and to end the search at (exclusive).
95-
Defaults to (0, -1), which means search to the end of the list.
132+
search_range (RangeSpec, optional): The range to search within. Defaults to None, which means search the entire list.
96133
97-
:returns:
98-
RangeSpec: The index for the desired line in the 'lines' list.
99-
Returns None if no match is found or if the offset exceeds the number of matches within each category.
134+
Returns:
135+
RangeSpec: A RangeSpec object representing the found line, or None if no match is found.
100136
101-
:Example:
102-
>> lines = ["Hello, world!", " Hello, world! ", "Héllo, wörld?", "Another line", "Hello, world!"]
103-
>> _find_line_index(lines, "Hello, world!", 1)
104-
4 # Returns the index of the second exact match
137+
Raises:
138+
ValueError: If there are multiple matches and no offset is specified, or if the offset exceeds the number of matches.
105139
106140
Note:
107-
- The function prioritizes match types in the order: exact, stripped, normalized, partial.
108-
- The offset is considered separately for each type.
141+
- The method prioritizes match types in the order: exact, stripped, normalized, partial.
142+
- The offset is considered separately for each match type.
109143
"""
110144
search_start_index, search_end_index, _ = search_range if search_range is not None else (0, -1, 0)
111145
search_line = search_term.value
@@ -199,26 +233,55 @@ def from_line_marker(
199233

200234

201235
class IdentifierBoundaries(NamedTuple):
236+
"""
237+
Represents the boundaries of an identifier in code, including its whole range and body range.
238+
239+
This class is used to specify the range of an entire identifier (whole) and its body,
240+
which is typically the content inside the identifier's definition.
241+
242+
Attributes:
243+
whole (RangeSpec): The RangeSpec representing the entire identifier.
244+
body (RangeSpec): The RangeSpec representing the body of the identifier.
245+
"""
246+
202247
whole: RangeSpec
203248
body: RangeSpec
204249

205250
def __str__(self):
251+
"""Return a string representation of the IdentifierBoundaries."""
206252
return f'IdentifierBoundaries({self.whole} (BODY: {self.body}) )'
207253

208254
@property
209255
def start_line(self) -> int:
256+
"""Return the 1-indexed start line of the whole identifier."""
210257
return self.whole.start + 1
211258

212259
@property
213260
def body_start_line(self) -> int:
261+
"""Return the 1-indexed start line of the identifier's body."""
214262
return self.body.start + 1
215263

216264
@property
217265
def end_line(self) -> int:
266+
"""Return the 1-indexed end line of the whole identifier."""
218267
return self.whole.end
219268

220-
# See the other bow_to_search_range
221269
def location_to_search_range(self, location: BodyOrWhole | RelativePositionType) -> RangeSpec:
270+
"""
271+
Convert a location specifier to a RangeSpec for searching.
272+
273+
This method interprets various location specifiers and returns the appropriate
274+
RangeSpec for searching within or around the identifier.
275+
276+
Args:
277+
location (BodyOrWhole | RelativePositionType): The location specifier.
278+
279+
Returns:
280+
RangeSpec: The corresponding RangeSpec for the specified location.
281+
282+
Raises:
283+
ValueError: If an invalid location specifier is provided.
284+
"""
222285
match location:
223286
case BodyOrWhole.BODY:
224287
return self.body

0 commit comments

Comments
 (0)