point.rs

  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}