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

21 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-15 10:34 +0000

1# Copyright (C) 2024-2026 TNO 

2# 

3# Licensed under the terms specified in the LICENSE file in the root directory 

4# of this repository, also available at: 

5# https://github.com/TNO/Renaissance.Py/blob/main/LICENSE 

6 

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

8 

9from typing import Protocol, runtime_checkable 

10 

11 

12@runtime_checkable 

13class TextSegment(Protocol): 

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

15 

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

17 Any consecutive piece of text qualifies; 

18 it need not correspond to a single syntactic construct. 

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

20 combinations thereof, such as a syntax node together with its associated trivia, 

21 and even a regular expression match, which has no relation to a syntax tree at all. 

22 

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

24 exposing only @property getters in the protocol. 

25 

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

27 functools.cached_property or manual memoized attributes, 

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

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

30 but only when they are first accessed. 

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

32 and reuse it for every node in that document, 

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

34 """ 

35 

36 @property 

37 def full_text(self) -> str: 

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

39 ... 

40 

41 @property 

42 def location(self) -> str: 

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

44 

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

46 """ 

47 ... 

48 

49 @property 

50 def start_offset(self) -> int: 

51 """Start offset of text segment. 

52 

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

54 """ 

55 ... 

56 

57 @property 

58 def start_line(self) -> int: 

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

60 ... 

61 

62 @property 

63 def start_column(self) -> int: 

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

65 ... 

66 

67 @property 

68 def end_offset(self) -> int: 

69 """Exclusive end offset of text segment. 

70 

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

72 """ 

73 ... 

74 

75 @property 

76 def end_line(self) -> int: 

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

78 ... 

79 

80 @property 

81 def end_column(self) -> int: 

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

83 ... 

84 

85 @property 

86 def text_segment(self) -> str: 

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

88 

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

90 

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

92 """ 

93 ...