1use crate::{Anchor, BufferSnapshot, TextDimension};
  2use std::cmp::Ordering;
  3use std::ops::Range;
  4
  5#[derive(Copy, Clone, Debug, PartialEq)]
  6pub enum SelectionGoal {
  7    None,
  8    HorizontalPosition(f64),
  9    HorizontalRange { start: f64, end: f64 },
 10    WrappedHorizontalPosition((u32, f32)),
 11}
 12
 13#[derive(Clone, Debug, PartialEq)]
 14pub struct Selection<T> {
 15    pub id: usize,
 16    pub start: T,
 17    pub end: T,
 18    pub reversed: bool,
 19    pub goal: SelectionGoal,
 20}
 21
 22impl Default for SelectionGoal {
 23    fn default() -> Self {
 24        Self::None
 25    }
 26}
 27
 28impl<T: Clone> Selection<T> {
 29    /// A place where the selection had stopped at.
 30    pub fn head(&self) -> T {
 31        if self.reversed {
 32            self.start.clone()
 33        } else {
 34            self.end.clone()
 35        }
 36    }
 37
 38    /// A place where selection was initiated from.
 39    pub fn tail(&self) -> T {
 40        if self.reversed {
 41            self.end.clone()
 42        } else {
 43            self.start.clone()
 44        }
 45    }
 46
 47    pub fn map<F, S>(&self, f: F) -> Selection<S>
 48    where
 49        F: Fn(T) -> S,
 50    {
 51        Selection::<S> {
 52            id: self.id,
 53            start: f(self.start.clone()),
 54            end: f(self.end.clone()),
 55            reversed: self.reversed,
 56            goal: self.goal,
 57        }
 58    }
 59
 60    pub fn collapse_to(&mut self, point: T, new_goal: SelectionGoal) {
 61        self.start = point.clone();
 62        self.end = point;
 63        self.goal = new_goal;
 64        self.reversed = false;
 65    }
 66}
 67
 68impl<T: Copy + Ord> Selection<T> {
 69    pub fn is_empty(&self) -> bool {
 70        self.start == self.end
 71    }
 72
 73    pub fn set_head(&mut self, head: T, new_goal: SelectionGoal) {
 74        if head.cmp(&self.tail()) < Ordering::Equal {
 75            if !self.reversed {
 76                self.end = self.start;
 77                self.reversed = true;
 78            }
 79            self.start = head;
 80        } else {
 81            if self.reversed {
 82                self.start = self.end;
 83                self.reversed = false;
 84            }
 85            self.end = head;
 86        }
 87        self.goal = new_goal;
 88    }
 89
 90    pub fn set_tail(&mut self, tail: T, new_goal: SelectionGoal) {
 91        if tail.cmp(&self.head()) <= Ordering::Equal {
 92            if self.reversed {
 93                self.end = self.start;
 94                self.reversed = false;
 95            }
 96            self.start = tail;
 97        } else {
 98            if !self.reversed {
 99                self.start = self.end;
100                self.reversed = true;
101            }
102            self.end = tail;
103        }
104        self.goal = new_goal;
105    }
106
107    pub fn set_head_tail(&mut self, head: T, tail: T, new_goal: SelectionGoal) {
108        if head < tail {
109            self.reversed = true;
110            self.start = head;
111            self.end = tail;
112        } else {
113            self.reversed = false;
114            self.start = tail;
115            self.end = head;
116        }
117        self.goal = new_goal;
118    }
119
120    pub fn swap_head_tail(&mut self) {
121        if self.reversed {
122            self.reversed = false;
123        } else {
124            std::mem::swap(&mut self.start, &mut self.end);
125        }
126    }
127}
128
129impl<T: Copy> Selection<T> {
130    pub fn range(&self) -> Range<T> {
131        self.start..self.end
132    }
133}
134
135impl Selection<usize> {
136    #[cfg(feature = "test-support")]
137    pub fn from_offset(offset: usize) -> Self {
138        Selection {
139            id: 0,
140            start: offset,
141            end: offset,
142            goal: SelectionGoal::None,
143            reversed: false,
144        }
145    }
146
147    pub fn equals(&self, offset_range: &Range<usize>) -> bool {
148        self.start == offset_range.start && self.end == offset_range.end
149    }
150}
151
152impl Selection<Anchor> {
153    pub fn resolve<'a, D: 'a + TextDimension>(
154        &'a self,
155        snapshot: &'a BufferSnapshot,
156    ) -> Selection<D> {
157        Selection {
158            id: self.id,
159            start: snapshot.summary_for_anchor(&self.start),
160            end: snapshot.summary_for_anchor(&self.end),
161            reversed: self.reversed,
162            goal: self.goal,
163        }
164    }
165}