selection.rs

  1use sum_tree::Bias;
  2
  3use crate::{rope::TextDimension, Anchor, Snapshot};
  4
  5use super::{Buffer, 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<[Selection<Anchor>]>,
 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 Selection<Anchor> {
102    pub fn resolve<'a, D: 'a + TextDimension<'a>>(
103        &'a self,
104        snapshot: &'a Snapshot,
105    ) -> Selection<D> {
106        Selection {
107            id: self.id,
108            start: snapshot.summary_for_anchor(&self.start),
109            end: snapshot.summary_for_anchor(&self.end),
110            reversed: self.reversed,
111            goal: self.goal,
112        }
113    }
114}
115
116impl SelectionSet {
117    pub fn len(&self) -> usize {
118        self.selections.len()
119    }
120
121    pub fn selections<'a, D>(
122        &'a self,
123        snapshot: &'a Snapshot,
124    ) -> impl 'a + Iterator<Item = Selection<D>>
125    where
126        D: 'a + TextDimension<'a>,
127    {
128        self.selections.iter().map(|s| s.resolve(snapshot))
129    }
130
131    pub fn intersecting_selections<'a, D, I>(
132        &'a self,
133        range: Range<(I, Bias)>,
134        snapshot: &'a Snapshot,
135    ) -> impl 'a + Iterator<Item = Selection<D>>
136    where
137        D: 'a + TextDimension<'a>,
138        I: 'a + ToOffset,
139    {
140        let start = snapshot.anchor_at(range.start.0, range.start.1);
141        let end = snapshot.anchor_at(range.end.0, range.end.1);
142        let start_ix = match self
143            .selections
144            .binary_search_by(|probe| probe.start.cmp(&start, snapshot).unwrap())
145        {
146            Ok(ix) | Err(ix) => ix,
147        };
148        let end_ix = match self
149            .selections
150            .binary_search_by(|probe| probe.end.cmp(&end, snapshot).unwrap())
151        {
152            Ok(ix) | Err(ix) => ix,
153        };
154        self.selections[start_ix..end_ix]
155            .iter()
156            .map(|s| s.resolve(snapshot))
157    }
158
159    pub fn oldest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option<Selection<D>>
160    where
161        D: 'a + TextDimension<'a>,
162    {
163        self.selections
164            .iter()
165            .min_by_key(|s| s.id)
166            .map(|s| s.resolve(snapshot))
167    }
168
169    pub fn newest_selection<'a, D>(&'a self, snapshot: &'a Snapshot) -> Option<Selection<D>>
170    where
171        D: 'a + TextDimension<'a>,
172    {
173        self.selections
174            .iter()
175            .max_by_key(|s| s.id)
176            .map(|s| s.resolve(snapshot))
177    }
178}