diff options
Diffstat (limited to 'stag/src/text_range.rs')
-rw-r--r-- | stag/src/text_range.rs | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/stag/src/text_range.rs b/stag/src/text_range.rs new file mode 100644 index 0000000..5b1ec67 --- /dev/null +++ b/stag/src/text_range.rs | |||
@@ -0,0 +1,121 @@ | |||
1 | use std::cmp::{Ord, Ordering}; | ||
2 | |||
3 | use serde::{Deserialize, Serialize}; | ||
4 | |||
5 | /// A singular position in a text document | ||
6 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
7 | pub struct Point { | ||
8 | /// The byte index | ||
9 | pub byte: usize, | ||
10 | |||
11 | /// 0-indexed line number | ||
12 | pub line: usize, | ||
13 | |||
14 | /// Position within the line | ||
15 | pub column: usize, | ||
16 | } | ||
17 | |||
18 | impl PartialOrd for Point { | ||
19 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
20 | Some(self.cmp(other)) | ||
21 | } | ||
22 | } | ||
23 | |||
24 | impl Ord for Point { | ||
25 | fn cmp(&self, other: &Self) -> Ordering { | ||
26 | self.byte.cmp(&other.byte) | ||
27 | } | ||
28 | } | ||
29 | |||
30 | impl Point { | ||
31 | pub fn new(byte: usize, line: usize, column: usize) -> Self { | ||
32 | Self { byte, line, column } | ||
33 | } | ||
34 | |||
35 | pub fn from_byte(byte: usize, line_end_indices: &[u32]) -> Self { | ||
36 | let line = line_end_indices | ||
37 | .iter() | ||
38 | .position(|&line_end_byte| (line_end_byte as usize) > byte) | ||
39 | .unwrap_or(0); | ||
40 | |||
41 | let column = line | ||
42 | .checked_sub(1) | ||
43 | .and_then(|idx| line_end_indices.get(idx)) | ||
44 | .map(|&prev_line_end| byte.saturating_sub(prev_line_end as usize)) | ||
45 | .unwrap_or(byte); | ||
46 | |||
47 | Self::new(byte, line, column) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
52 | pub struct TextRange { | ||
53 | pub start: Point, | ||
54 | pub end: Point, | ||
55 | } | ||
56 | |||
57 | impl PartialOrd for TextRange { | ||
58 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
59 | Some(self.cmp(other)) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | impl Ord for TextRange { | ||
64 | fn cmp(&self, other: &Self) -> Ordering { | ||
65 | let compare_start_byte = self.start.byte.cmp(&other.start.byte); | ||
66 | let compare_size = self.size().cmp(&other.size()); | ||
67 | |||
68 | compare_start_byte.then(compare_size) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | impl TextRange { | ||
73 | pub fn new(start: Point, end: Point) -> Self { | ||
74 | assert!(start <= end); | ||
75 | Self { start, end } | ||
76 | } | ||
77 | |||
78 | pub fn contains(&self, other: &TextRange) -> bool { | ||
79 | // (self.start ... [other.start ... other.end] ... self.end) | ||
80 | self.start <= other.start && other.end <= self.end | ||
81 | } | ||
82 | |||
83 | #[allow(unused)] | ||
84 | pub fn contains_strict(&self, other: TextRange) -> bool { | ||
85 | // (self.start ... (other.start ... other.end) ... self.end) | ||
86 | self.start < other.start && other.end <= self.end | ||
87 | } | ||
88 | |||
89 | pub fn size(&self) -> usize { | ||
90 | self.end.byte.saturating_sub(self.start.byte) | ||
91 | } | ||
92 | |||
93 | pub fn from_byte_range(range: std::ops::Range<usize>, line_end_indices: &[u32]) -> Self { | ||
94 | let start = Point::from_byte(range.start, line_end_indices); | ||
95 | let end = Point::from_byte(range.end, line_end_indices); | ||
96 | Self::new(start, end) | ||
97 | } | ||
98 | } | ||
99 | |||
100 | impl From<tree_sitter::Range> for TextRange { | ||
101 | fn from(r: tree_sitter::Range) -> Self { | ||
102 | Self { | ||
103 | start: Point { | ||
104 | byte: r.start_byte, | ||
105 | line: r.start_point.row, | ||
106 | column: r.start_point.column, | ||
107 | }, | ||
108 | end: Point { | ||
109 | byte: r.end_byte, | ||
110 | line: r.end_point.row, | ||
111 | column: r.end_point.column, | ||
112 | }, | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | impl From<TextRange> for std::ops::Range<usize> { | ||
118 | fn from(r: TextRange) -> std::ops::Range<usize> { | ||
119 | r.start.byte..r.end.byte | ||
120 | } | ||
121 | } | ||