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