1use sum_tree::Bias;
2
3use crate::{rope::TextDimension, Anchor, Snapshot};
4
5use super::{Buffer, Point, ToOffset, ToPoint};
6use std::{cmp::Ordering, ops::Range, sync::Arc};
7
8pub type SelectionSetId = clock::Lamport;
9pub type SelectionsVersion = usize;
10
11#[derive(Copy, Clone, Debug, Eq, PartialEq)]
12pub enum SelectionGoal {
13 None,
14 Column(u32),
15 ColumnRange { start: u32, end: u32 },
16}
17
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub struct Selection<T> {
20 pub id: usize,
21 pub start: T,
22 pub end: T,
23 pub reversed: bool,
24 pub goal: SelectionGoal,
25}
26
27#[derive(Clone, Debug, Eq, PartialEq)]
28pub struct SelectionSet {
29 pub id: SelectionSetId,
30 pub active: bool,
31 pub selections: Arc<[Selection<Anchor>]>,
32}
33
34#[derive(Debug, Eq, PartialEq)]
35pub struct SelectionState {
36 pub id: usize,
37 pub reversed: bool,
38 pub goal: SelectionGoal,
39}
40
41impl<T: Clone> Selection<T> {
42 pub fn head(&self) -> T {
43 if self.reversed {
44 self.start.clone()
45 } else {
46 self.end.clone()
47 }
48 }
49
50 pub fn tail(&self) -> T {
51 if self.reversed {
52 self.end.clone()
53 } else {
54 self.start.clone()
55 }
56 }
57}
58
59impl<T: ToOffset + ToPoint + Copy + Ord> Selection<T> {
60 pub fn is_empty(&self) -> bool {
61 self.start == self.end
62 }
63
64 pub fn set_head(&mut self, head: T) {
65 if head.cmp(&self.tail()) < Ordering::Equal {
66 if !self.reversed {
67 self.end = self.start;
68 self.reversed = true;
69 }
70 self.start = head;
71 } else {
72 if self.reversed {
73 self.start = self.end;
74 self.reversed = false;
75 }
76 self.end = head;
77 }
78 }
79
80 pub fn point_range(&self, buffer: &Buffer) -> Range<Point> {
81 let start = self.start.to_point(buffer);
82 let end = self.end.to_point(buffer);
83 if self.reversed {
84 end..start
85 } else {
86 start..end
87 }
88 }
89
90 pub fn offset_range(&self, buffer: &Buffer) -> Range<usize> {
91 let start = self.start.to_offset(buffer);
92 let end = self.end.to_offset(buffer);
93 if self.reversed {
94 end..start
95 } else {
96 start..end
97 }
98 }
99}
100
101impl Selection<Anchor> {
102 pub fn resolve<'a, D: 'a + TextDimension<'a>>(
103 &'a self,
104 snapshot: &'a Snapshot,
105 ) -> Selection<D> {
106 Selection {
107 id: self.id,
108 start: snapshot.summary_for_anchor(&self.start),
109 end: snapshot.summary_for_anchor(&self.end),
110 reversed: self.reversed,
111 goal: self.goal,
112 }
113 }
114}
115
116impl SelectionSet {
117 pub fn len(&self) -> usize {
118 self.selections.len()
119 }
120
121 pub fn selections<'a, D>(
122 &'a self,
123 snapshot: &'a Snapshot,
124 ) -> impl 'a + Iterator<Item = Selection<D>>
125 where
126 D: 'a + TextDimension<'a>,
127 {
128 self.selections.iter().map(|s| s.resolve(snapshot))
129 }
130
131 pub fn intersecting_selections<'a, D, I>(
132 &'a self,
133 range: Range<(I, Bias)>,
134 snapshot: &'a Snapshot,
135 ) -> impl 'a + Iterator<Item = Selection<D>>
136 where
137 D: 'a + TextDimension<'a>,
138 I: 'a + ToOffset,
139 {
140 let start = snapshot.anchor_at(range.start.0, range.start.1);
141 let end = snapshot.anchor_at(range.end.0, range.end.1);
142 let start_ix = match self
143 .selections
144 .binary_search_by(|probe| probe.start.cmp(&start, snapshot).unwrap())
145 {
146 Ok(ix) | Err(ix) => ix,
147 };
148 let end_ix = match self
149 .selections
150 .binary_search_by(|probe| probe.end.cmp(&end, snapshot).unwrap())
151 {
152 Ok(ix) | Err(ix) => ix,
153 };
154 self.selections[start_ix..end_ix]
155 .iter()
156 .map(|s| s.resolve(snapshot))
157 }
158
159 pub fn oldest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option<Selection<D>>
160 where
161 D: 'a + TextDimension<'a>,
162 {
163 self.selections
164 .iter()
165 .min_by_key(|s| s.id)
166 .map(|s| s.resolve(snapshot))
167 }
168
169 pub fn newest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option<Selection<D>>
170 where
171 D: 'a + TextDimension<'a>,
172 {
173 self.selections
174 .iter()
175 .max_by_key(|s| s.id)
176 .map(|s| s.resolve(snapshot))
177 }
178}