1use crate::{AnchorRangeMap, Buffer, Content, Point, ToOffset, ToPoint};
2use rpc::proto;
3use std::{cmp::Ordering, ops::Range, sync::Arc};
4use sum_tree::Bias;
5
6pub type SelectionSetId = clock::Lamport;
7pub type SelectionsVersion = usize;
8
9#[derive(Copy, Clone, Debug, Eq, PartialEq)]
10pub enum SelectionGoal {
11 None,
12 Column(u32),
13 ColumnRange { start: u32, end: u32 },
14}
15
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct Selection<T> {
18 pub id: usize,
19 pub start: T,
20 pub end: T,
21 pub reversed: bool,
22 pub goal: SelectionGoal,
23}
24
25#[derive(Clone, Debug, Eq, PartialEq)]
26pub struct SelectionSet {
27 pub id: SelectionSetId,
28 pub active: bool,
29 pub selections: Arc<AnchorRangeMap<SelectionState>>,
30}
31
32#[derive(Debug, Eq, PartialEq)]
33pub struct SelectionState {
34 pub id: usize,
35 pub reversed: bool,
36 pub goal: SelectionGoal,
37}
38
39impl<T: ToOffset + ToPoint + Copy + Ord> Selection<T> {
40 pub fn head(&self) -> T {
41 if self.reversed {
42 self.start
43 } else {
44 self.end
45 }
46 }
47
48 pub fn set_head(&mut self, head: T) {
49 if head.cmp(&self.tail()) < Ordering::Equal {
50 if !self.reversed {
51 self.end = self.start;
52 self.reversed = true;
53 }
54 self.start = head;
55 } else {
56 if self.reversed {
57 self.start = self.end;
58 self.reversed = false;
59 }
60 self.end = head;
61 }
62 }
63
64 pub fn tail(&self) -> T {
65 if self.reversed {
66 self.end
67 } else {
68 self.start
69 }
70 }
71
72 pub fn point_range(&self, buffer: &Buffer) -> Range<Point> {
73 let start = self.start.to_point(buffer);
74 let end = self.end.to_point(buffer);
75 if self.reversed {
76 end..start
77 } else {
78 start..end
79 }
80 }
81
82 pub fn offset_range(&self, buffer: &Buffer) -> Range<usize> {
83 let start = self.start.to_offset(buffer);
84 let end = self.end.to_offset(buffer);
85 if self.reversed {
86 end..start
87 } else {
88 start..end
89 }
90 }
91}
92
93impl SelectionSet {
94 pub fn offset_selections<'a>(
95 &'a self,
96 content: impl Into<Content<'a>> + 'a,
97 ) -> impl 'a + Iterator<Item = Selection<usize>> {
98 self.selections
99 .offset_ranges(content)
100 .map(|(range, state)| Selection {
101 id: state.id,
102 start: range.start,
103 end: range.end,
104 reversed: state.reversed,
105 goal: state.goal,
106 })
107 }
108
109 pub fn point_selections<'a>(
110 &'a self,
111 content: impl Into<Content<'a>> + 'a,
112 ) -> impl 'a + Iterator<Item = Selection<Point>> {
113 self.selections
114 .point_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
125impl<'a> Into<proto::SelectionSet> for &'a SelectionSet {
126 fn into(self) -> proto::SelectionSet {
127 let version = self.selections.version();
128 let entries = self.selections.raw_entries();
129 proto::SelectionSet {
130 replica_id: self.id.replica_id as u32,
131 lamport_timestamp: self.id.value as u32,
132 is_active: self.active,
133 version: version.into(),
134 selections: entries
135 .iter()
136 .map(|(range, state)| proto::Selection {
137 id: state.id as u64,
138 start: range.start.0 as u64,
139 end: range.end.0 as u64,
140 reversed: state.reversed,
141 })
142 .collect(),
143 }
144 }
145}
146
147impl From<proto::SelectionSet> for SelectionSet {
148 fn from(set: proto::SelectionSet) -> Self {
149 Self {
150 id: clock::Lamport {
151 replica_id: set.replica_id as u16,
152 value: set.lamport_timestamp,
153 },
154 active: set.is_active,
155 selections: Arc::new(AnchorRangeMap::from_raw(
156 set.version.into(),
157 set.selections
158 .into_iter()
159 .map(|selection| {
160 let range = (selection.start as usize, Bias::Left)
161 ..(selection.end as usize, Bias::Right);
162 let state = SelectionState {
163 id: selection.id as usize,
164 reversed: selection.reversed,
165 goal: SelectionGoal::None,
166 };
167 (range, state)
168 })
169 .collect(),
170 )),
171 }
172 }
173}