1use sum_tree::Bias;
2
3use crate::rope::TextDimension;
4
5use super::{AnchorRangeMap, Buffer, Content, 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, C>(&'a self, content: C) -> impl 'a + Iterator<Item = Selection<D>>
107 where
108 D: 'a + TextDimension<'a>,
109 C: 'a + Into<Content<'a>>,
110 {
111 self.selections
112 .ranges(content)
113 .map(|(range, state)| Selection {
114 id: state.id,
115 start: range.start,
116 end: range.end,
117 reversed: state.reversed,
118 goal: state.goal,
119 })
120 }
121
122 pub fn intersecting_selections<'a, D, I, C>(
123 &'a self,
124 range: Range<(I, Bias)>,
125 content: C,
126 ) -> impl 'a + Iterator<Item = Selection<D>>
127 where
128 D: 'a + TextDimension<'a>,
129 I: 'a + ToOffset,
130 C: 'a + Into<Content<'a>>,
131 {
132 self.selections
133 .intersecting_ranges(range, content)
134 .map(|(range, state)| Selection {
135 id: state.id,
136 start: range.start,
137 end: range.end,
138 reversed: state.reversed,
139 goal: state.goal,
140 })
141 }
142
143 pub fn oldest_selection<'a, D, C>(&'a self, content: C) -> Option<Selection<D>>
144 where
145 D: 'a + TextDimension<'a>,
146 C: 'a + Into<Content<'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, C>(&'a self, content: C) -> Option<Selection<D>>
160 where
161 D: 'a + TextDimension<'a>,
162 C: 'a + Into<Content<'a>>,
163 {
164 self.selections
165 .max_by_key(content, |selection| selection.id)
166 .map(|(range, state)| Selection {
167 id: state.id,
168 start: range.start,
169 end: range.end,
170 reversed: state.reversed,
171 goal: state.goal,
172 })
173 }
174}