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