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(f32),
9 HorizontalRange { start: f32, end: f32 },
10 WrappedHorizontalPosition((u32, f32)),
11 WrappedHorizontalRange { start: (u32, f32), end: (u32, f32) },
12}
13
14#[derive(Clone, Debug, PartialEq)]
15pub struct Selection<T> {
16 pub id: usize,
17 pub start: T,
18 pub end: T,
19 pub reversed: bool,
20 pub goal: SelectionGoal,
21}
22
23impl Default for SelectionGoal {
24 fn default() -> Self {
25 Self::None
26 }
27}
28
29impl<T: Clone> Selection<T> {
30 pub fn head(&self) -> T {
31 if self.reversed {
32 self.start.clone()
33 } else {
34 self.end.clone()
35 }
36 }
37
38 pub fn tail(&self) -> T {
39 if self.reversed {
40 self.end.clone()
41 } else {
42 self.start.clone()
43 }
44 }
45
46 pub fn map<F, S>(&self, f: F) -> Selection<S>
47 where
48 F: Fn(T) -> S,
49 {
50 Selection::<S> {
51 id: self.id,
52 start: f(self.start.clone()),
53 end: f(self.end.clone()),
54 reversed: self.reversed,
55 goal: self.goal,
56 }
57 }
58
59 pub fn collapse_to(&mut self, point: T, new_goal: SelectionGoal) {
60 self.start = point.clone();
61 self.end = point;
62 self.goal = new_goal;
63 self.reversed = false;
64 }
65}
66
67impl<T: Copy + Ord> Selection<T> {
68 pub fn is_empty(&self) -> bool {
69 self.start == self.end
70 }
71
72 pub fn set_head(&mut self, head: T, new_goal: SelectionGoal) {
73 if head.cmp(&self.tail()) < Ordering::Equal {
74 if !self.reversed {
75 self.end = self.start;
76 self.reversed = true;
77 }
78 self.start = head;
79 } else {
80 if self.reversed {
81 self.start = self.end;
82 self.reversed = false;
83 }
84 self.end = head;
85 }
86 self.goal = new_goal;
87 }
88
89 pub fn range(&self) -> Range<T> {
90 self.start..self.end
91 }
92}
93
94impl Selection<usize> {
95 #[cfg(feature = "test-support")]
96 pub fn from_offset(offset: usize) -> Self {
97 Selection {
98 id: 0,
99 start: offset,
100 end: offset,
101 goal: SelectionGoal::None,
102 reversed: false,
103 }
104 }
105
106 pub fn equals(&self, offset_range: &Range<usize>) -> bool {
107 self.start == offset_range.start && self.end == offset_range.end
108 }
109}
110
111impl Selection<Anchor> {
112 pub fn resolve<'a, D: 'a + TextDimension>(
113 &'a self,
114 snapshot: &'a BufferSnapshot,
115 ) -> Selection<D> {
116 Selection {
117 id: self.id,
118 start: snapshot.summary_for_anchor(&self.start),
119 end: snapshot.summary_for_anchor(&self.end),
120 reversed: self.reversed,
121 goal: self.goal,
122 }
123 }
124}