1use std::{
2 cmp::Ordering, fmt::Debug, ops::{Add, AddAssign, Range, Sub}
3};
4
5/// A zero-indexed point in a text buffer consisting of a row and column.
6#[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
7pub struct Point {
8 pub row: u32,
9 pub column: u32,
10}
11
12impl Debug for Point {
13 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 let Self { row, column } = self;
15 write!(f, "Point({row}:{column})")
16 }
17}
18
19impl Point {
20 pub const MAX: Self = Self {
21 row: u32::MAX,
22 column: u32::MAX,
23 };
24
25 pub fn new(row: u32, column: u32) -> Self {
26 Point { row, column }
27 }
28
29 pub fn row_range(range: Range<u32>) -> Range<Self> {
30 Point {
31 row: range.start,
32 column: 0,
33 }..Point {
34 row: range.end,
35 column: 0,
36 }
37 }
38
39 pub fn zero() -> Self {
40 Point::new(0, 0)
41 }
42
43 pub fn parse_str(s: &str) -> Self {
44 let mut point = Self::zero();
45 for (row, line) in s.split('\n').enumerate() {
46 point.row = row as u32;
47 point.column = line.len() as u32;
48 }
49 point
50 }
51
52 pub fn is_zero(&self) -> bool {
53 self.row == 0 && self.column == 0
54 }
55
56 pub fn saturating_sub(self, other: Self) -> Self {
57 if self < other {
58 Self::zero()
59 } else {
60 self - other
61 }
62 }
63}
64
65impl<'a> Add<&'a Self> for Point {
66 type Output = Point;
67
68 fn add(self, other: &'a Self) -> Self::Output {
69 self + *other
70 }
71}
72
73impl Add for Point {
74 type Output = Point;
75
76 fn add(self, other: Self) -> Self::Output {
77 if other.row == 0 {
78 Point::new(self.row, self.column + other.column)
79 } else {
80 Point::new(self.row + other.row, other.column)
81 }
82 }
83}
84
85impl<'a> Sub<&'a Self> for Point {
86 type Output = Point;
87
88 fn sub(self, other: &'a Self) -> Self::Output {
89 self - *other
90 }
91}
92
93impl Sub for Point {
94 type Output = Point;
95
96 fn sub(self, other: Self) -> Self::Output {
97 debug_assert!(other <= self);
98
99 if self.row == other.row {
100 Point::new(0, self.column - other.column)
101 } else {
102 Point::new(self.row - other.row, self.column)
103 }
104 }
105}
106
107impl<'a> AddAssign<&'a Self> for Point {
108 fn add_assign(&mut self, other: &'a Self) {
109 *self += *other;
110 }
111}
112
113impl AddAssign<Self> for Point {
114 fn add_assign(&mut self, other: Self) {
115 if other.row == 0 {
116 self.column += other.column;
117 } else {
118 self.row += other.row;
119 self.column = other.column;
120 }
121 }
122}
123
124impl PartialOrd for Point {
125 fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
126 Some(self.cmp(other))
127 }
128}
129
130impl Ord for Point {
131 #[cfg(target_pointer_width = "64")]
132 fn cmp(&self, other: &Point) -> Ordering {
133 let a = ((self.row as usize) << 32) | self.column as usize;
134 let b = ((other.row as usize) << 32) | other.column as usize;
135 a.cmp(&b)
136 }
137
138 #[cfg(target_pointer_width = "32")]
139 fn cmp(&self, other: &Point) -> Ordering {
140 match self.row.cmp(&other.row) {
141 Ordering::Equal => self.column.cmp(&other.column),
142 comparison @ _ => comparison,
143 }
144 }
145}