1use crate::{Anchor, Buffer, Point, ToOffset as _, ToPoint as _};
2use anyhow::anyhow;
3use rpc::proto;
4use std::{
5 cmp::Ordering,
6 convert::{TryFrom, TryInto},
7 mem,
8 ops::Range,
9 sync::Arc,
10};
11
12pub type SelectionSetId = clock::Lamport;
13pub type SelectionsVersion = usize;
14
15#[derive(Copy, Clone, Debug, Eq, PartialEq)]
16pub enum SelectionGoal {
17 None,
18 Column(u32),
19 ColumnRange { start: u32, end: u32 },
20}
21
22#[derive(Clone, Debug, Eq, PartialEq)]
23pub struct Selection {
24 pub id: usize,
25 pub start: Anchor,
26 pub end: Anchor,
27 pub reversed: bool,
28 pub goal: SelectionGoal,
29}
30
31#[derive(Clone, Debug, Eq, PartialEq)]
32pub struct SelectionSet {
33 pub id: SelectionSetId,
34 pub active: bool,
35 pub selections: Arc<[Selection]>,
36}
37
38impl Selection {
39 pub fn head(&self) -> &Anchor {
40 if self.reversed {
41 &self.start
42 } else {
43 &self.end
44 }
45 }
46
47 pub fn set_head(&mut self, buffer: &Buffer, cursor: Anchor) {
48 if cursor.cmp(self.tail(), buffer).unwrap() < Ordering::Equal {
49 if !self.reversed {
50 mem::swap(&mut self.start, &mut self.end);
51 self.reversed = true;
52 }
53 self.start = cursor;
54 } else {
55 if self.reversed {
56 mem::swap(&mut self.start, &mut self.end);
57 self.reversed = false;
58 }
59 self.end = cursor;
60 }
61 }
62
63 pub fn tail(&self) -> &Anchor {
64 if self.reversed {
65 &self.end
66 } else {
67 &self.start
68 }
69 }
70
71 pub fn point_range(&self, buffer: &Buffer) -> Range<Point> {
72 let start = self.start.to_point(buffer);
73 let end = self.end.to_point(buffer);
74 if self.reversed {
75 end..start
76 } else {
77 start..end
78 }
79 }
80
81 pub fn offset_range(&self, buffer: &Buffer) -> Range<usize> {
82 let start = self.start.to_offset(buffer);
83 let end = self.end.to_offset(buffer);
84 if self.reversed {
85 end..start
86 } else {
87 start..end
88 }
89 }
90}
91
92impl<'a> Into<proto::Selection> for &'a Selection {
93 fn into(self) -> proto::Selection {
94 proto::Selection {
95 id: self.id as u64,
96 start: Some((&self.start).into()),
97 end: Some((&self.end).into()),
98 reversed: self.reversed,
99 }
100 }
101}
102
103impl TryFrom<proto::Selection> for Selection {
104 type Error = anyhow::Error;
105
106 fn try_from(selection: proto::Selection) -> Result<Self, Self::Error> {
107 Ok(Selection {
108 id: selection.id as usize,
109 start: selection
110 .start
111 .ok_or_else(|| anyhow!("missing selection start"))?
112 .try_into()?,
113 end: selection
114 .end
115 .ok_or_else(|| anyhow!("missing selection end"))?
116 .try_into()?,
117 reversed: selection.reversed,
118 goal: SelectionGoal::None,
119 })
120 }
121}
122
123impl TryFrom<proto::SelectionSet> for SelectionSet {
124 type Error = anyhow::Error;
125
126 fn try_from(set: proto::SelectionSet) -> Result<Self, Self::Error> {
127 Ok(Self {
128 id: clock::Lamport {
129 replica_id: set.replica_id as u16,
130 value: set.lamport_timestamp,
131 },
132 active: set.is_active,
133 selections: Arc::from(
134 set.selections
135 .into_iter()
136 .map(TryInto::try_into)
137 .collect::<Result<Vec<Selection>, _>>()?,
138 ),
139 })
140 }
141}