use std::cmp::{max, min};
use std::fmt;
use std::ops::Deref;
use crate::annotations::{AnnotationRange, AnnotationSlice, AnnotationType, ToAnnotation};
use crate::index_set::remove_n_at;
use crate::view::View;
use xi_rope::{Interval, Rope, RopeDelta, Transformer};
pub type HorizPos = usize;
#[derive(Copy, Clone)]
pub enum InsertDrift {
Inside,
Outside,
Default,
}
#[derive(Default, Debug, Clone)]
pub struct Selection {
regions: Vec<SelRegion>,
}
impl Selection {
pub fn new() -> Selection {
Selection::default()
}
pub fn new_simple(region: SelRegion) -> Selection {
Selection { regions: vec![region] }
}
pub fn clear(&mut self) {
self.regions.clear();
}
pub fn collapse(&mut self) {
self.regions.truncate(1);
self.regions[0].start = self.regions[0].end;
}
pub fn search(&self, offset: usize) -> usize {
if self.regions.is_empty() || offset > self.regions.last().unwrap().max() {
return self.regions.len();
}
match self.regions.binary_search_by(|r| r.max().cmp(&offset)) {
Ok(ix) => ix,
Err(ix) => ix,
}
}
pub fn add_region(&mut self, region: SelRegion) {
let mut ix = self.search(region.min());
if ix == self.regions.len() {
self.regions.push(region);
return;
}
let mut region = region;
let mut end_ix = ix;
if self.regions[ix].min() <= region.min() {
if self.regions[ix].should_merge(region) {
region = self.regions[ix].merge_with(region);
} else {
ix += 1;
}
end_ix += 1;
}
while end_ix < self.regions.len() && region.should_merge(self.regions[end_ix]) {
region = region.merge_with(self.regions[end_ix]);
end_ix += 1;
}
if ix == end_ix {
self.regions.insert(ix, region);
} else {
self.regions[ix] = region;
remove_n_at(&mut self.regions, ix + 1, end_ix - ix - 1);
}
}
pub fn regions_in_range(&self, start: usize, end: usize) -> &[SelRegion] {
let first = self.search(start);
let mut last = self.search(end);
if last < self.regions.len() && self.regions[last].min() <= end {
last += 1;
}
&self.regions[first..last]
}
pub fn delete_range(&mut self, start: usize, end: usize, delete_adjacent: bool) {
let mut first = self.search(start);
let mut last = self.search(end);
if first >= self.regions.len() {
return;
}
if !delete_adjacent && self.regions[first].max() == start {
first += 1;
}
if last < self.regions.len()
&& ((delete_adjacent && self.regions[last].min() <= end)
|| (!delete_adjacent && self.regions[last].min() < end))
{
last += 1;
}
remove_n_at(&mut self.regions, first, last - first);
}
pub fn add_range_distinct(&mut self, region: SelRegion) -> (usize, usize) {
let mut ix = self.search(region.min());
if ix < self.regions.len() && self.regions[ix].max() == region.min() {
ix += 1;
}
if ix < self.regions.len() {
let occ = &self.regions[ix];
let is_eq = occ.min() == region.min() && occ.max() == region.max();
let is_intersect_before = region.min() >= occ.min() && occ.max() > region.min();
if is_eq || is_intersect_before {
return (occ.min(), occ.max());
}
}
let mut last = self.search(region.max());
if last < self.regions.len() && self.regions[last].min() < region.max() {
last += 1;
}
remove_n_at(&mut self.regions, ix, last - ix);
if ix == self.regions.len() {
self.regions.push(region);
} else {
self.regions.insert(ix, region);
}
(self.regions[ix].min(), self.regions[ix].max())
}
pub fn apply_delta(&self, delta: &RopeDelta, after: bool, drift: InsertDrift) -> Selection {
let mut result = Selection::new();
let mut transformer = Transformer::new(delta);
for region in self.iter() {
let is_caret = region.start == region.end;
let is_region_forward = region.start < region.end;
let (start_after, end_after) = match (drift, is_caret) {
(InsertDrift::Inside, false) => (!is_region_forward, is_region_forward),
(InsertDrift::Outside, false) => (is_region_forward, !is_region_forward),
_ => (after, after),
};
let new_region = SelRegion::new(
transformer.transform(region.start, start_after),
transformer.transform(region.end, end_after),
)
.with_affinity(region.affinity);
result.add_region(new_region);
}
result
}
}
impl ToAnnotation for Selection {
fn get_annotations(&self, interval: Interval, view: &View, text: &Rope) -> AnnotationSlice {
let regions = self.regions_in_range(interval.start(), interval.end());
let ranges = regions
.iter()
.map(|region| {
let (start_line, start_col) = view.offset_to_line_col(text, region.min());
let (end_line, end_col) = view.offset_to_line_col(text, region.max());
AnnotationRange { start_line, start_col, end_line, end_col }
})
.collect::<Vec<AnnotationRange>>();
AnnotationSlice::new(AnnotationType::Selection, ranges, None)
}
}
impl Deref for Selection {
type Target = [SelRegion];
fn deref(&self) -> &[SelRegion] {
&self.regions
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Affinity {
Downstream,
Upstream,
}
impl Default for Affinity {
fn default() -> Affinity {
Affinity::Downstream
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct SelRegion {
pub start: usize,
pub end: usize,
pub horiz: Option<HorizPos>,
pub affinity: Affinity,
}
impl SelRegion {
pub fn new(start: usize, end: usize) -> Self {
Self { start, end, horiz: None, affinity: Affinity::default() }
}
pub fn caret(pos: usize) -> Self {
Self { start: pos, end: pos, horiz: None, affinity: Affinity::default() }
}
pub fn with_horiz(self, horiz: Option<HorizPos>) -> Self {
Self { horiz, ..self }
}
pub fn with_affinity(self, affinity: Affinity) -> Self {
Self { affinity, ..self }
}
pub fn min(self) -> usize {
min(self.start, self.end)
}
pub fn max(self) -> usize {
max(self.start, self.end)
}
pub fn is_caret(self) -> bool {
self.start == self.end
}
pub fn is_upstream(self) -> bool {
self.affinity == Affinity::Upstream
}
fn should_merge(self, other: SelRegion) -> bool {
other.min() < self.max()
|| ((self.is_caret() || other.is_caret()) && other.min() == self.max())
}
fn merge_with(self, other: SelRegion) -> SelRegion {
let is_forward = self.end > self.start || other.end > other.start;
let new_min = min(self.min(), other.min());
let new_max = max(self.max(), other.max());
let (start, end) = if is_forward { (new_min, new_max) } else { (new_max, new_min) };
SelRegion::new(start, end)
}
}
impl<'a> From<&'a SelRegion> for Interval {
fn from(src: &'a SelRegion) -> Interval {
Interval::new(src.min(), src.max())
}
}
impl From<Interval> for SelRegion {
fn from(src: Interval) -> SelRegion {
SelRegion::new(src.start, src.end)
}
}
impl From<SelRegion> for Selection {
fn from(region: SelRegion) -> Self {
Self::new_simple(region)
}
}
impl fmt::Display for Selection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.regions.len() == 1 {
self.regions[0].fmt(f)?;
} else {
write!(f, "[ {}", &self.regions[0])?;
for region in &self.regions[1..] {
write!(f, ", {}", region)?;
}
write!(f, " ]")?;
}
Ok(())
}
}
impl fmt::Display for SelRegion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_caret() {
write!(f, "{}|", self.start)?;
} else if self.start < self.end {
write!(f, "{}..{}|", self.start, self.end)?;
} else {
write!(f, "|{}..{}", self.end, self.start)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{InsertDrift, SelRegion, Selection};
use std::ops::Deref;
use xi_rope::{DeltaBuilder, Interval};
fn r(start: usize, end: usize) -> SelRegion {
SelRegion::new(start, end)
}
#[test]
fn empty() {
let s = Selection::new();
assert!(s.is_empty());
assert_eq!(s.deref(), &[]);
}
#[test]
fn simple_region() {
let s = Selection::new_simple(r(3, 5));
assert!(!s.is_empty());
assert_eq!(s.deref(), &[r(3, 5)]);
}
#[test]
fn from_selregion() {
let s: Selection = r(3, 5).into();
assert!(!s.is_empty());
assert_eq!(s.deref(), &[r(3, 5)]);
}
#[test]
fn delete_range() {
let mut s = Selection::new_simple(r(3, 5));
s.delete_range(1, 2, true);
assert_eq!(s.deref(), &[r(3, 5)]);
s.delete_range(1, 3, false);
assert_eq!(s.deref(), &[r(3, 5)]);
s.delete_range(1, 3, true);
assert_eq!(s.deref(), &[]);
let mut s = Selection::new_simple(r(3, 5));
s.delete_range(5, 6, false);
assert_eq!(s.deref(), &[r(3, 5)]);
s.delete_range(5, 6, true);
assert_eq!(s.deref(), &[]);
let mut s = Selection::new_simple(r(3, 5));
s.delete_range(2, 4, false);
assert_eq!(s.deref(), &[]);
assert_eq!(s.deref(), &[]);
let mut s = Selection::new();
s.add_region(r(3, 5));
s.add_region(r(7, 8));
s.delete_range(2, 10, false);
assert_eq!(s.deref(), &[]);
}
#[test]
fn simple_regions_in_range() {
let s = Selection::new_simple(r(3, 5));
assert_eq!(s.regions_in_range(0, 1), &[]);
assert_eq!(s.regions_in_range(0, 2), &[]);
assert_eq!(s.regions_in_range(0, 3), &[r(3, 5)]);
assert_eq!(s.regions_in_range(0, 4), &[r(3, 5)]);
assert_eq!(s.regions_in_range(5, 6), &[r(3, 5)]);
assert_eq!(s.regions_in_range(6, 7), &[]);
}
#[test]
fn caret_regions_in_range() {
let s = Selection::new_simple(r(4, 4));
assert_eq!(s.regions_in_range(0, 1), &[]);
assert_eq!(s.regions_in_range(0, 2), &[]);
assert_eq!(s.regions_in_range(0, 3), &[]);
assert_eq!(s.regions_in_range(0, 4), &[r(4, 4)]);
assert_eq!(s.regions_in_range(4, 4), &[r(4, 4)]);
assert_eq!(s.regions_in_range(4, 5), &[r(4, 4)]);
assert_eq!(s.regions_in_range(5, 6), &[]);
}
#[test]
fn merge_regions() {
let mut s = Selection::new();
s.add_region(r(3, 5));
assert_eq!(s.deref(), &[r(3, 5)]);
s.add_region(r(7, 9));
assert_eq!(s.deref(), &[r(3, 5), r(7, 9)]);
s.add_region(r(1, 3));
assert_eq!(s.deref(), &[r(1, 3), r(3, 5), r(7, 9)]);
s.add_region(r(4, 6));
assert_eq!(s.deref(), &[r(1, 3), r(3, 6), r(7, 9)]);
s.add_region(r(2, 8));
assert_eq!(s.deref(), &[r(1, 9)]);
s.clear();
assert_eq!(s.deref(), &[]);
s.add_region(r(1, 4));
s.add_region(r(4, 5));
s.add_region(r(5, 6));
s.add_region(r(6, 9));
assert_eq!(s.deref(), &[r(1, 4), r(4, 5), r(5, 6), r(6, 9)]);
s.add_region(r(2, 8));
assert_eq!(s.deref(), &[r(1, 9)]);
}
#[test]
fn merge_carets() {
let mut s = Selection::new();
s.add_region(r(1, 1));
assert_eq!(s.deref(), &[r(1, 1)]);
s.add_region(r(3, 3));
assert_eq!(s.deref(), &[r(1, 1), r(3, 3)]);
s.add_region(r(2, 2));
assert_eq!(s.deref(), &[r(1, 1), r(2, 2), r(3, 3)]);
s.add_region(r(1, 1));
assert_eq!(s.deref(), &[r(1, 1), r(2, 2), r(3, 3)]);
}
#[test]
fn merge_region_caret() {
let mut s = Selection::new();
s.add_region(r(3, 5));
assert_eq!(s.deref(), &[r(3, 5)]);
s.add_region(r(3, 3));
assert_eq!(s.deref(), &[r(3, 5)]);
s.add_region(r(4, 4));
assert_eq!(s.deref(), &[r(3, 5)]);
s.add_region(r(5, 5));
assert_eq!(s.deref(), &[r(3, 5)]);
s.add_region(r(6, 6));
assert_eq!(s.deref(), &[r(3, 5), r(6, 6)]);
}
#[test]
fn merge_reverse() {
let mut s = Selection::new();
s.add_region(r(5, 3));
assert_eq!(s.deref(), &[r(5, 3)]);
s.add_region(r(9, 7));
assert_eq!(s.deref(), &[r(5, 3), r(9, 7)]);
s.add_region(r(3, 1));
assert_eq!(s.deref(), &[r(3, 1), r(5, 3), r(9, 7)]);
s.add_region(r(6, 4));
assert_eq!(s.deref(), &[r(3, 1), r(6, 3), r(9, 7)]);
s.add_region(r(8, 2));
assert_eq!(s.deref(), &[r(9, 1)]);
}
#[test]
fn apply_delta_outside_drift() {
let mut s = Selection::new();
s.add_region(r(0, 4));
s.add_region(r(4, 8));
assert_eq!(s.deref(), &[r(0, 4), r(4, 8)]);
let mut builder = DeltaBuilder::new("texthere!".len());
builder.replace(Interval::new(4, 4), " ".into());
let s2 = s.apply_delta(&builder.build(), true, InsertDrift::Outside);
assert_eq!(s2.deref(), &[r(0, 4), r(5, 9)]);
}
#[test]
fn apply_delta_inside_drift() {
let mut s = Selection::new();
s.add_region(r(1, 2));
assert_eq!(s.deref(), &[r(1, 2)]);
let mut builder = DeltaBuilder::new("abc".len());
builder.replace(Interval::new(1, 1), "b".into());
builder.replace(Interval::new(2, 2), "b".into());
let s2 = s.apply_delta(&builder.build(), true, InsertDrift::Inside);
assert_eq!(s2.deref(), &[r(1, 4)]);
}
#[test]
fn apply_delta_drift_ignored_for_carets() {
let mut s = Selection::new();
s.add_region(r(1, 1));
assert_eq!(s.deref(), &[r(1, 1)]);
let mut builder = DeltaBuilder::new("ab".len());
builder.replace(Interval::new(1, 1), "b".into());
let s2 = s.apply_delta(&builder.build(), true, InsertDrift::Inside);
assert_eq!(s2.deref(), &[r(2, 2)]);
let mut builder = DeltaBuilder::new("ab".len());
builder.replace(Interval::new(1, 1), "b".into());
let s3 = s.apply_delta(&builder.build(), false, InsertDrift::Inside);
assert_eq!(s3.deref(), &[r(1, 1)]);
}
#[test]
fn display() {
let mut s = Selection::new();
s.add_region(r(1, 1));
assert_eq!(s.to_string(), "1|");
s.add_region(r(3, 5));
s.add_region(r(8, 6));
assert_eq!(s.to_string(), "[ 1|, 3..5|, |6..8 ]");
}
}