selection.rs

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