1use gpui::Pixels;
2
3use crate::{Anchor, BufferSnapshot, TextDimension};
4use std::cmp::Ordering;
5use std::ops::Range;
6
7#[derive(Copy, Clone, Debug, PartialEq)]
8pub enum SelectionGoal {
9 None,
10 HorizontalPosition(Pixels),
11 HorizontalRange { start: Pixels, end: Pixels },
12 WrappedHorizontalPosition((u32, f32)),
13}
14
15#[derive(Clone, Debug, PartialEq)]
16pub struct Selection<T> {
17 pub id: usize,
18 pub start: T,
19 pub end: T,
20 pub reversed: bool,
21 pub goal: SelectionGoal,
22}
23
24impl Default for SelectionGoal {
25 fn default() -> Self {
26 Self::None
27 }
28}
29
30impl<T: Clone> Selection<T> {
31 pub fn head(&self) -> T {
32 if self.reversed {
33 self.start.clone()
34 } else {
35 self.end.clone()
36 }
37 }
38
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 range(&self) -> Range<T> {
91 self.start..self.end
92 }
93}
94
95impl Selection<usize> {
96 #[cfg(feature = "test-support")]
97 pub fn from_offset(offset: usize) -> Self {
98 Selection {
99 id: 0,
100 start: offset,
101 end: offset,
102 goal: SelectionGoal::None,
103 reversed: false,
104 }
105 }
106
107 pub fn equals(&self, offset_range: &Range<usize>) -> bool {
108 self.start == offset_range.start && self.end == offset_range.end
109 }
110}
111
112impl Selection<Anchor> {
113 pub fn resolve<'a, D: 'a + TextDimension>(
114 &'a self,
115 snapshot: &'a BufferSnapshot,
116 ) -> Selection<D> {
117 Selection {
118 id: self.id,
119 start: snapshot.summary_for_anchor(&self.start),
120 end: snapshot.summary_for_anchor(&self.end),
121 reversed: self.reversed,
122 goal: self.goal,
123 }
124 }
125}