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}
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 pub fn head(&self) -> T {
30 if self.reversed {
31 self.start.clone()
32 } else {
33 self.end.clone()
34 }
35 }
36
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 swap_head_tail(&mut self) {
106 if self.reversed {
107 self.reversed = false;
108 } else {
109 std::mem::swap(&mut self.start, &mut self.end);
110 }
111 }
112}
113
114impl<T: Copy> Selection<T> {
115 pub fn range(&self) -> Range<T> {
116 self.start..self.end
117 }
118}
119
120impl Selection<usize> {
121 #[cfg(feature = "test-support")]
122 pub fn from_offset(offset: usize) -> Self {
123 Selection {
124 id: 0,
125 start: offset,
126 end: offset,
127 goal: SelectionGoal::None,
128 reversed: false,
129 }
130 }
131
132 pub fn equals(&self, offset_range: &Range<usize>) -> bool {
133 self.start == offset_range.start && self.end == offset_range.end
134 }
135}
136
137impl Selection<Anchor> {
138 pub fn resolve<'a, D: 'a + TextDimension>(
139 &'a self,
140 snapshot: &'a BufferSnapshot,
141 ) -> Selection<D> {
142 Selection {
143 id: self.id,
144 start: snapshot.summary_for_anchor(&self.start),
145 end: snapshot.summary_for_anchor(&self.end),
146 reversed: self.reversed,
147 goal: self.goal,
148 }
149 }
150}