169 lines · 4.1 KB · Rust
RawDownload
use crate::{Anchor, BufferSnapshot, TextDimension};use std::cmp::Ordering;use std::ops::Range; #[derive(Default, Copy, Clone, Debug, PartialEq)]pub enum SelectionGoal {    #[default]    None,    HorizontalPosition(f64),    HorizontalRange {        start: f64,        end: f64,    },    WrappedHorizontalPosition((u32, f32)),} #[derive(Clone, Debug, PartialEq)]pub struct Selection<T> {    pub id: usize,    pub start: T,    pub end: T,    pub reversed: bool,    pub goal: SelectionGoal,} impl<T: Clone> Selection<T> {    /// A place where the selection had stopped at.    pub fn head(&self) -> T {        if self.reversed {            self.start.clone()        } else {            self.end.clone()        }    }     /// A place where selection was initiated from.    pub fn tail(&self) -> T {        if self.reversed {            self.end.clone()        } else {            self.start.clone()        }    }     pub fn map<F, S>(&self, f: F) -> Selection<S>    where        F: Fn(T) -> S,    {        Selection::<S> {            id: self.id,            start: f(self.start.clone()),            end: f(self.end.clone()),            reversed: self.reversed,            goal: self.goal,        }    }     pub fn collapse_to(&mut self, point: T, new_goal: SelectionGoal) {        self.start = point.clone();        self.end = point;        self.goal = new_goal;        self.reversed = false;    }} impl<T: Copy + Ord> Selection<T> {    pub fn is_empty(&self) -> bool {        self.start == self.end    }     pub fn set_head(&mut self, head: T, new_goal: SelectionGoal) {        if head.cmp(&self.tail()) < Ordering::Equal {            if !self.reversed {                self.end = self.start;                self.reversed = true;            }            self.start = head;        } else {            if self.reversed {                self.start = self.end;                self.reversed = false;            }            self.end = head;        }        self.goal = new_goal;    }     pub fn set_tail(&mut self, tail: T, new_goal: SelectionGoal) {        if tail.cmp(&self.head()) <= Ordering::Equal {            if self.reversed {                self.end = self.start;                self.reversed = false;            }            self.start = tail;        } else {            if !self.reversed {                self.start = self.end;                self.reversed = true;            }            self.end = tail;        }        self.goal = new_goal;    }     pub fn set_head_tail(&mut self, head: T, tail: T, new_goal: SelectionGoal) {        if head < tail {            self.reversed = true;            self.start = head;            self.end = tail;        } else {            self.reversed = false;            self.start = tail;            self.end = head;        }        self.goal = new_goal;    }     pub fn swap_head_tail(&mut self) {        if self.reversed {            self.reversed = false;        } else {            std::mem::swap(&mut self.start, &mut self.end);        }    }} impl<T: Copy> Selection<T> {    pub fn range(&self) -> Range<T> {        self.start..self.end    }} impl<T: std::ops::Sub + Copy> Selection<T> {    pub fn len(&self) -> <T as std::ops::Sub>::Output {        self.end - self.start    }} impl<T: Copy + Eq> Selection<T> {    #[cfg(feature = "test-support")]    pub fn from_offset(offset: T) -> Self {        Selection {            id: 0,            start: offset,            end: offset,            goal: SelectionGoal::None,            reversed: false,        }    }     pub fn equals(&self, offset_range: &Range<T>) -> bool {        self.start == offset_range.start && self.end == offset_range.end    }} impl Selection<Anchor> {    pub fn resolve<'a, D: 'a + TextDimension>(        &'a self,        snapshot: &'a BufferSnapshot,    ) -> Selection<D> {        Selection {            id: self.id,            start: snapshot.summary_for_anchor(&self.start),            end: snapshot.summary_for_anchor(&self.end),            reversed: self.reversed,            goal: self.goal,        }    }}