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}