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