Coverage for src / renaissance / syntax_tree / text_segment.py: 100%

21 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-09-09 14:04 +0000

1"""Protocol defining a text segment: a consecutive piece of text within a larger text.""" 

2 

3from typing import Protocol, runtime_checkable 

4 

5 

6@runtime_checkable 

7class TextSegment(Protocol): 

8 """Protocol for anything that represents a text segment. 

9 

10 A text segment is a consecutive piece, a.k.a. a slice, within a text. 

11 Instances include comments, whitespace (incl. empty lines), and syntax nodes. 

12 

13 Read-only access is enforced "as much as possible" by 

14 exposing only @property getters in the protocol. 

15 

16 A concrete implementation of this protocol can make some of the properties lazy by using 

17 functools.cached_property or manual memoized attributes, 

18 such that it only triggers computation the first time it's needed. 

19 For example, start_line and start_column can be computed from start_offset and full_text, 

20 but only when they are first accessed. 

21 Furthermore, a concrete implementation can also make a line-start-offset table once per document 

22 and reuse it for every node in that document, 

23 turning each node's line/column lookup into an O(log lines) binary search. 

24 """ 

25 

26 @property 

27 def full_text(self) -> str: 

28 """The full text that contains the text segment.""" 

29 ... 

30 

31 @property 

32 def location(self) -> str: 

33 """The location of the full text that contains the text segment. 

34 

35 For example, when text originates from disk the location is a file path. 

36 """ 

37 ... 

38 

39 @property 

40 def start_offset(self) -> int: 

41 """Start offset of text segment. 

42 

43 start_offset is an integer in [0, len(full_text)]. 

44 """ 

45 ... 

46 

47 @property 

48 def start_line(self) -> int: 

49 """Start line of text segment - 0 based.""" 

50 ... 

51 

52 @property 

53 def start_column(self) -> int: 

54 """Start column of text segment - 0 based.""" 

55 ... 

56 

57 @property 

58 def end_offset(self) -> int: 

59 """Exclusive end offset of text segment. 

60 

61 end_offset is an integer in [0, len(full_text)]. 

62 """ 

63 ... 

64 

65 @property 

66 def end_line(self) -> int: 

67 """End line of text segment - 0 based.""" 

68 ... 

69 

70 @property 

71 def end_column(self) -> int: 

72 """End column of text segment - 0 based.""" 

73 ... 

74 

75 @property 

76 def text_segment(self) -> str: 

77 """The text segment is a slice of the full text. 

78 

79 The text segment is represented by the half-open interval [self.start_offset, self.end_offset). 

80 

81 The returned text segment is equal to self.full_text[self.start_offset:self.end_offset]. 

82 """ 

83 ...