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