1use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
10
11use bincode::{Decode, Encode};
12
13macro_rules! implPartialEq {
14 ($self:ident: $Self:ty, $other:ident: $Other:ty, $($impl:tt)+) => {
15 impl PartialEq<$Other> for $Self {
16 fn eq(&self, other: &$Other) -> bool {
17 let ($self, $other) = (&self, other);
18 $($impl)+
19 }
20 }
21 }
22}
23
24pub(super) use implPartialEq;
25
26use crate::text::Strs;
27
28macro_rules! implTextRange {
29 ($range:ident, $r:ident, $max:ident, $sb:expr, $eb:expr, $sp:expr, $ep:expr) => {
30 impl TextRange for $range<usize> {
31 #[track_caller]
32 fn to_range(self, max: usize) -> Range<usize> {
33 let $max = max;
34 let $r = self;
35 $crate::utils::get_range($sb..$eb, max)
36 }
37
38 fn try_to_range(self, max: usize) -> Option<Range<usize>> {
39 let $max = max;
40 let $r = self;
41 $crate::utils::try_get_range($sb..$eb, max)
42 }
43 }
44
45 impl TextRange for $range<Point> {
46 #[track_caller]
47 fn to_range(self, max: usize) -> Range<usize> {
48 let $max = max;
49 let $r = self;
50 $crate::utils::get_range($sp..$ep, max)
51 }
52
53 fn try_to_range(self, max: usize) -> Option<Range<usize>> {
54 let $max = max;
55 let $r = self;
56 $crate::utils::try_get_range($sp..$ep, max)
57 }
58 }
59 };
60}
61
62macro_rules! implTextRangeOrIndex {
63 ($range:ident) => {
64 impl TextRangeOrIndex for $range<usize> {
65 #[track_caller]
66 fn to_range(self, max: usize) -> Range<usize> {
67 TextRange::to_range(self, max)
68 }
69 }
70
71 impl TextRangeOrIndex for $range<Point> {
72 #[track_caller]
73 fn to_range(self, max: usize) -> Range<usize> {
74 TextRange::to_range(self, max)
75 }
76 }
77 };
78}
79
80#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
87pub struct Point {
88 byte: u32,
89 char: u32,
90 line: u32,
91}
92
93impl Point {
94 pub const fn new() -> Self {
96 Point { byte: 0, char: 0, line: 0 }
97 }
98
99 pub const fn from_raw(b: usize, c: usize, l: usize) -> Self {
101 let (b, c, l) = (b as u32, c as u32, l as u32);
102 Self { byte: b, char: c, line: l }
103 }
104
105 pub const fn to_two_points_before(self) -> TwoPoints {
110 TwoPoints::new_before_ghost(self)
111 }
112
113 pub const fn to_two_points_after(self) -> TwoPoints {
118 TwoPoints::new_after_ghost(self)
119 }
120
121 pub fn end_point_of(str: impl AsRef<str>) -> Self {
132 let str = str.as_ref();
133 Self {
134 byte: str.len() as u32,
135 char: str.chars().count() as u32,
136 line: str.bytes().filter(|c| *c == b'\n').count() as u32,
137 }
138 }
139
140 pub const fn byte(&self) -> usize {
151 self.byte as usize
152 }
153
154 pub const fn char(&self) -> usize {
157 self.char as usize
158 }
159
160 pub const fn line(&self) -> usize {
162 self.line as usize
163 }
164
165 pub fn byte_col(&self, strs: &Strs) -> usize {
168 self.byte() - strs.point_at_coords(self.line(), 0).byte()
169 }
170
171 pub fn char_col(&self, strs: &Strs) -> usize {
177 self.char() - strs.point_at_coords(self.line(), 0).char()
178 }
179
180 pub fn checked_sub(self, rhs: Point) -> Option<Point> {
182 Some(Self {
183 byte: self.byte.checked_sub(rhs.byte)?,
184 char: self.char.checked_sub(rhs.char)?,
185 line: self.line.checked_sub(rhs.line)?,
186 })
187 }
188
189 #[inline(always)]
193 pub(crate) const fn fwd(self, char: char) -> Self {
194 Self {
195 byte: self.byte + char.len_utf8() as u32,
196 char: self.char + 1,
197 line: self.line + (char == '\n') as u32,
198 }
199 }
200
201 #[inline(always)]
203 pub(crate) const fn rev(self, char: char) -> Self {
204 Self {
205 byte: self.byte - char.len_utf8() as u32,
206 char: self.char - 1,
207 line: self.line - (char == '\n') as u32,
208 }
209 }
210
211 pub(crate) const fn shift_by(self, [b, c, l]: [i32; 3]) -> Self {
215 Self {
216 byte: (self.byte as i32 + b) as u32,
217 char: (self.char as i32 + c) as u32,
218 line: (self.line as i32 + l) as u32,
219 }
220 }
221
222 pub(crate) const fn as_signed(self) -> [i32; 3] {
227 [self.byte as i32, self.char as i32, self.line as i32]
228 }
229}
230
231impl std::fmt::Debug for Point {
232 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233 write!(
234 f,
235 "Point {{ b: {}, c: {}, l: {} }}",
236 self.byte, self.char, self.line
237 )
238 }
239}
240
241impl std::fmt::Display for Point {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 write!(f, "{}, {}, {}", self.byte, self.char, self.line)
244 }
245}
246
247impl std::ops::Add for Point {
248 type Output = Self;
249
250 fn add(self, rhs: Self) -> Self::Output {
251 Self {
252 byte: self.byte + rhs.byte,
253 char: self.char + rhs.char,
254 line: self.line + rhs.line,
255 }
256 }
257}
258
259impl std::ops::AddAssign for Point {
260 fn add_assign(&mut self, rhs: Self) {
261 *self = *self + rhs;
262 }
263}
264
265impl std::ops::Sub for Point {
266 type Output = Self;
267
268 fn sub(self, rhs: Self) -> Self::Output {
269 Self {
270 byte: self.byte - rhs.byte,
271 char: self.char - rhs.char,
272 line: self.line - rhs.line,
273 }
274 }
275}
276
277impl std::ops::SubAssign for Point {
278 fn sub_assign(&mut self, rhs: Self) {
279 *self = *self - rhs;
280 }
281}
282
283#[doc(hidden)]
296pub trait TextIndex: Clone + Copy + std::fmt::Debug {
297 fn to_byte_index(self) -> usize;
299}
300
301impl TextIndex for Point {
302 fn to_byte_index(self) -> usize {
303 self.byte()
304 }
305}
306
307impl TextIndex for usize {
308 fn to_byte_index(self) -> usize {
309 self
310 }
311}
312
313#[doc(hidden)]
325pub trait TextRange: Clone + std::fmt::Debug {
326 fn to_range(self, max: usize) -> Range<usize>;
330
331 fn try_to_range(self, max: usize) -> Option<Range<usize>>;
335}
336
337implTextRange!(Range, r, _max, r.start, r.end, r.start.byte(), r.end.byte());
338implTextRange!(
339 RangeInclusive,
340 r,
341 _max,
342 *r.start(),
343 r.end() + 1,
344 r.start().byte(),
345 r.end().byte() + 1
346);
347implTextRange!(RangeTo, r, _max, 0, r.end, 0, r.end.byte());
348implTextRange!(RangeToInclusive, r, _max, 0, r.end, 0, r.end.byte());
349implTextRange!(RangeFrom, r, max, r.start, max, r.start.byte(), max);
350
351impl TextRange for RangeFull {
352 fn to_range(self, max: usize) -> Range<usize> {
353 0..max
354 }
355
356 fn try_to_range(self, max: usize) -> Option<Range<usize>> {
357 Some(0..max)
358 }
359}
360
361#[doc(hidden)]
376pub trait TextRangeOrIndex {
377 fn to_range(self, max: usize) -> Range<usize>;
379}
380
381impl TextRangeOrIndex for usize {
382 #[track_caller]
383 fn to_range(self, max: usize) -> Range<usize> {
384 crate::utils::get_range(self..self + 1, max)
385 }
386}
387
388impl TextRangeOrIndex for Point {
389 #[track_caller]
390 fn to_range(self, max: usize) -> Range<usize> {
391 crate::utils::get_range(self.byte()..self.byte() + 1, max)
392 }
393}
394
395impl TextRangeOrIndex for RangeFull {
396 fn to_range(self, max: usize) -> Range<usize> {
397 0..max
398 }
399}
400
401implTextRangeOrIndex!(Range);
402implTextRangeOrIndex!(RangeInclusive);
403implTextRangeOrIndex!(RangeTo);
404implTextRangeOrIndex!(RangeToInclusive);
405implTextRangeOrIndex!(RangeFrom);
406
407#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, Hash)]
424pub struct TwoPoints {
425 pub real: Point,
429 pub ghost: Option<Point>,
448}
449
450impl TwoPoints {
451 pub const fn new(real: Point, ghost: Point) -> Self {
460 Self { real, ghost: Some(ghost) }
461 }
462
463 pub const fn new_before_ghost(real: Point) -> Self {
468 Self { real, ghost: Some(Point::new()) }
469 }
470
471 pub const fn new_after_ghost(real: Point) -> Self {
476 Self { real, ghost: None }
477 }
478}
479
480impl std::cmp::PartialOrd for TwoPoints {
481 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
482 Some(self.cmp(other))
483 }
484}
485
486impl Ord for TwoPoints {
487 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
488 match self.real.cmp(&other.real) {
489 core::cmp::Ordering::Equal => {}
490 ord => return ord,
491 }
492 match (&self.ghost, &other.ghost) {
493 (Some(l), Some(r)) => l.cmp(r),
494 (Some(_), None) => std::cmp::Ordering::Less,
495 (None, Some(_)) => std::cmp::Ordering::Greater,
496 (None, None) => std::cmp::Ordering::Equal,
497 }
498 }
499}
500
501#[inline]
504pub const fn utf8_char_width(b: u8) -> u32 {
505 const UTF8_CHAR_WIDTH: &[u8; 256] = &[
507 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
525
526 UTF8_CHAR_WIDTH[b as usize] as u32
527}