selection.rs

  1use crate::rope::TextDimension;
  2
  3use super::{AnchorRangeMap, Buffer, Content, Point, ToOffset, ToPoint};
  4use std::{cmp::Ordering, ops::Range, sync::Arc};
  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: Clone> Selection<T> {
 40    pub fn head(&self) -> T {
 41        if self.reversed {
 42            self.start.clone()
 43        } else {
 44            self.end.clone()
 45        }
 46    }
 47
 48    pub fn tail(&self) -> T {
 49        if self.reversed {
 50            self.end.clone()
 51        } else {
 52            self.start.clone()
 53        }
 54    }
 55}
 56
 57impl<T: ToOffset + ToPoint + Copy + Ord> Selection<T> {
 58    pub fn is_empty(&self) -> bool {
 59        self.start == self.end
 60    }
 61
 62    pub fn set_head(&mut self, head: T) {
 63        if head.cmp(&self.tail()) < Ordering::Equal {
 64            if !self.reversed {
 65                self.end = self.start;
 66                self.reversed = true;
 67            }
 68            self.start = head;
 69        } else {
 70            if self.reversed {
 71                self.start = self.end;
 72                self.reversed = false;
 73            }
 74            self.end = head;
 75        }
 76    }
 77
 78    pub fn point_range(&self, buffer: &Buffer) -> Range<Point> {
 79        let start = self.start.to_point(buffer);
 80        let end = self.end.to_point(buffer);
 81        if self.reversed {
 82            end..start
 83        } else {
 84            start..end
 85        }
 86    }
 87
 88    pub fn offset_range(&self, buffer: &Buffer) -> Range<usize> {
 89        let start = self.start.to_offset(buffer);
 90        let end = self.end.to_offset(buffer);
 91        if self.reversed {
 92            end..start
 93        } else {
 94            start..end
 95        }
 96    }
 97}
 98
 99impl SelectionSet {
100    pub fn len(&self) -> usize {
101        self.selections.len()
102    }
103
104    pub fn selections<'a, D, C>(&'a self, content: C) -> impl 'a + Iterator<Item = Selection<D>>
105    where
106        D: 'a + TextDimension<'a>,
107        C: 'a + Into<Content<'a>>,
108    {
109        self.selections
110            .ranges(content)
111            .map(|(range, state)| Selection {
112                id: state.id,
113                start: range.start,
114                end: range.end,
115                reversed: state.reversed,
116                goal: state.goal,
117            })
118    }
119
120    pub fn oldest_selection<'a, D, C>(&'a self, content: C) -> Option<Selection<D>>
121    where
122        D: 'a + TextDimension<'a>,
123        C: 'a + Into<Content<'a>>,
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, C>(&'a self, content: C) -> Option<Selection<D>>
137    where
138        D: 'a + TextDimension<'a>,
139        C: 'a + Into<Content<'a>>,
140    {
141        self.selections
142            .max_by_key(content, |selection| selection.id)
143            .map(|(range, state)| Selection {
144                id: state.id,
145                start: range.start,
146                end: range.end,
147                reversed: state.reversed,
148                goal: state.goal,
149            })
150    }
151}