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}