use std::cmp::{Ordering, PartialOrd};
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
pub struct Point {
pub row: usize,
pub column: usize,
}
impl From<tree_sitter::Point> for Point {
fn from(value: tree_sitter::Point) -> Self {
Point {
row: value.row,
column: value.column,
}
}
}
impl From<lsp_types::Position> for Point {
fn from(value: lsp_types::Position) -> Self {
Point {
row: value.line as usize,
column: value.character as usize,
}
}
}
impl Point {
pub fn into_tree_sitter(self) -> tree_sitter::Point {
tree_sitter::Point {
row: self.row,
column: self.column,
}
}
pub fn into_lsp_type(self) -> lsp_types::Position {
lsp_types::Position {
line: self.row as u32,
character: self.column as u32,
}
}
}
impl From<(usize, usize)> for Point {
fn from((row, column): (usize, usize)) -> Self {
Self { row, column }
}
}
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
pub struct Span {
pub start: Point,
pub end: Point,
}
impl From<tree_sitter::Range> for Span {
fn from(value: tree_sitter::Range) -> Self {
Self {
start: value.start_point.into(),
end: value.end_point.into(),
}
}
}
impl From<lsp_types::Range> for Span {
fn from(value: lsp_types::Range) -> Self {
Self {
start: value.start.into(),
end: value.end.into(),
}
}
}
impl From<((usize, usize), (usize, usize))> for Span {
fn from((start, end): ((usize, usize), (usize, usize))) -> Self {
let start = start.into();
let end = end.into();
Self { start, end }
}
}
impl From<(Point, Point)> for Span {
fn from((start, end): (Point, Point)) -> Self {
Self { start, end }
}
}
impl<P> From<std::ops::Range<P>> for Span
where
P: Into<Point>,
{
fn from(value: std::ops::Range<P>) -> Self {
Self {
start: value.start.into(),
end: value.end.into(),
}
}
}
impl Span {
pub fn into_tree_sitter(self, start_byte: usize, end_byte: usize) -> tree_sitter::Range {
tree_sitter::Range {
start_point: self.start.into_tree_sitter(),
end_point: self.end.into_tree_sitter(),
start_byte,
end_byte,
}
}
pub fn into_lsp_types(self) -> lsp_types::Range {
lsp_types::Range {
start: self.start.into_lsp_type(),
end: self.end.into_lsp_type(),
}
}
}
impl Span {
pub fn in_range(&self, range: &Span) -> bool {
range.start <= self.start && self.end <= range.end
}
pub fn contains(&self, point: &Point) -> bool {
self.start <= *point && *point <= self.end
}
pub fn contains_span(&self, other: &Span) -> bool {
self.start <= other.start && other.end <= self.end
}
}
impl PartialOrd<Point> for Span {
fn partial_cmp(&self, other: &Point) -> Option<std::cmp::Ordering> {
match (self.start.cmp(other), self.end.cmp(other)) {
(_, Ordering::Less) => Some(Ordering::Less),
(Ordering::Greater, _) => Some(Ordering::Greater),
(Ordering::Less, Ordering::Greater) => Some(Ordering::Equal),
(_, Ordering::Equal) => Some(Ordering::Equal),
(Ordering::Equal, _) => Some(Ordering::Equal),
}
}
}
impl PartialOrd<Span> for Point {
fn partial_cmp(&self, other: &Span) -> Option<std::cmp::Ordering> {
match (self.cmp(&other.start), self.cmp(&other.end)) {
(Ordering::Less, _) => Some(Ordering::Less),
(_, Ordering::Greater) => Some(Ordering::Greater),
(Ordering::Greater, Ordering::Less) => Some(Ordering::Equal),
(Ordering::Equal, _) => Some(Ordering::Equal),
(_, Ordering::Equal) => Some(Ordering::Equal),
}
}
}
impl PartialEq<Point> for Span {
fn eq(&self, other: &Point) -> bool {
self.contains(other)
}
}
impl PartialEq<Span> for Point {
fn eq(&self, other: &Span) -> bool {
other.contains(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn span_contains() {
let point = Point { row: 0, column: 2 };
let span = Span {
start: Point { row: 0, column: 1 },
end: Point { row: 0, column: 3 },
};
assert!(span.contains(&point));
assert_eq!(span, point);
assert_eq!(span.partial_cmp(&point), Some(Ordering::Equal));
}
#[test]
fn point_on_lower_edge_of_span() {
let point = Point { row: 0, column: 1 };
let span = Span {
start: Point { row: 0, column: 1 },
end: Point { row: 0, column: 3 },
};
assert!(span.contains(&point));
assert_eq!(span, point);
assert_eq!(span.partial_cmp(&point), Some(Ordering::Equal));
}
#[test]
fn point_on_outside_left_of_span() {
let point = Point { row: 0, column: 0 };
let span = Span {
start: Point { row: 0, column: 1 },
end: Point { row: 0, column: 3 },
};
assert!(!span.contains(&point));
assert!(point < span);
assert!(span > point);
assert_eq!(point.partial_cmp(&span), Some(Ordering::Less));
assert_eq!(span.partial_cmp(&point), Some(Ordering::Greater));
}
#[test]
fn point_on_outside_right_of_span() {
let point = Point { row: 0, column: 10 };
let span = Span {
start: Point { row: 0, column: 1 },
end: Point { row: 0, column: 3 },
};
assert!(!span.contains(&point));
assert!(point > span);
assert!(span < point);
assert_eq!(point.partial_cmp(&span), Some(Ordering::Greater));
assert_eq!(span.partial_cmp(&point), Some(Ordering::Less));
}
#[test]
fn point_on_upper_edge_of_span() {
let point = Point { row: 0, column: 3 };
let span = Span {
start: Point { row: 0, column: 1 },
end: Point { row: 0, column: 3 },
};
assert!(span.contains(&point));
assert_eq!(span, point);
assert_eq!(span.partial_cmp(&point), Some(Ordering::Equal));
}
#[test]
fn point_ordering() {
let p0 = Point { row: 0, column: 0 };
let p1 = Point { row: 1, column: 0 };
assert!(p1 > p0);
let p2 = Point { row: 0, column: 1 };
let p3 = Point { row: 0, column: 2 };
assert!(p2 > p0);
assert!(p3 > p0);
assert!(p1 > p2);
assert!(p1 > p3);
}
}