1use sum_tree::Bias;
2
3use crate::{rope::TextDimension, Snapshot};
4
5use super::{AnchorRangeMap, 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<AnchorRangeMap<SelectionState>>,
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 SelectionSet {
102 pub fn len(&self) -> usize {
103 self.selections.len()
104 }
105
106 pub fn selections<'a, D>(
107 &'a self,
108 content: &'a Snapshot,
109 ) -> impl 'a + Iterator<Item = Selection<D>>
110 where
111 D: 'a + TextDimension<'a>,
112 {
113 self.selections
114 .ranges(content)
115 .map(|(range, state)| Selection {
116 id: state.id,
117 start: range.start,
118 end: range.end,
119 reversed: state.reversed,
120 goal: state.goal,
121 })
122 }
123
124 pub fn intersecting_selections<'a, D, I>(
125 &'a self,
126 range: Range<(I, Bias)>,
127 content: &'a Snapshot,
128 ) -> impl 'a + Iterator<Item = Selection<D>>
129 where
130 D: 'a + TextDimension<'a>,
131 I: 'a + ToOffset,
132 {
133 self.selections
134 .intersecting_ranges(range, content)
135 .map(|(range, state)| Selection {
136 id: state.id,
137 start: range.start,
138 end: range.end,
139 reversed: state.reversed,
140 goal: state.goal,
141 })
142 }
143
144 pub fn oldest_selection<'a, D>(&'a self, content: &'a Snapshot) -> Option<Selection<D>>
145 where
146 D: 'a + TextDimension<'a>,
147 {
148 self.selections
149 .min_by_key(content, |selection| selection.id)
150 .map(|(range, state)| Selection {
151 id: state.id,
152 start: range.start,
153 end: range.end,
154 reversed: state.reversed,
155 goal: state.goal,
156 })
157 }
158
159 pub fn newest_selection<'a, D>(&'a self, content: &'a Snapshot) -> Option<Selection<D>>
160 where
161 D: 'a + TextDimension<'a>,
162 {
163 self.selections
164 .max_by_key(content, |selection| selection.id)
165 .map(|(range, state)| Selection {
166 id: state.id,
167 start: range.start,
168 end: range.end,
169 reversed: state.reversed,
170 goal: state.goal,
171 })
172 }
173}