mirror of https://github.com/helix-editor/helix
Add Selection::{overlaps,without} and fix contains
Adds two helper functions to `Selection`: * `overlaps`: tests whether two `Selection`s contain any ranges which overlap with each other * `without`: Computes a new `Selection` that is the set difference of two `Selection`s, i.e. everything in the first `Selection` with everything that overlaps in the second `Selection` removed, potentially leaving holes in the original ranges. It also fixes a bug with `Selection::contains`: it assumes that if the second `Selection` has a greater number of ranges than the first, then the first cannot contain the second; but this is false, since one range from the first could contain multiple smaller ranges in the second.pull/6198/head
parent
8fbb9566fb
commit
4b3aad7830
|
@ -703,22 +703,161 @@ impl Selection {
|
||||||
pub fn contains(&self, other: &Selection) -> bool {
|
pub fn contains(&self, other: &Selection) -> bool {
|
||||||
is_subset::<true>(self.range_bounds(), other.range_bounds())
|
is_subset::<true>(self.range_bounds(), other.range_bounds())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns true if self has at least one range that overlaps with at least one range from other
|
||||||
|
pub fn overlaps(&self, other: &Selection) -> bool {
|
||||||
|
let (mut iter_self, mut iter_other) = (self.iter(), other.iter());
|
||||||
|
let (mut ele_self, mut ele_other) = (iter_self.next(), iter_other.next());
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match (ele_self, ele_other) {
|
||||||
|
(Some(ra), Some(rb)) => {
|
||||||
|
if ra.overlaps(rb) {
|
||||||
|
return true;
|
||||||
|
} else if ra.from() > rb.from() {
|
||||||
|
ele_self = iter_self.next();
|
||||||
|
} else {
|
||||||
|
ele_other = iter_other.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the given selection with the overlapping portions of `other`
|
||||||
|
/// cut out. If one range from this selection is equal to one from `other`,
|
||||||
|
/// this range is removed. If this results in an entirely empty selection,
|
||||||
|
/// `None` is returned.
|
||||||
|
pub fn without(self, other: &Selection) -> Option<Self> {
|
||||||
|
if other.contains(&self) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut primary_index = self.primary_index;
|
||||||
|
let mut ranges = smallvec!();
|
||||||
|
let (mut iter_self, mut iter_other) = (self.into_iter(), other.iter());
|
||||||
|
let (mut ele_self, mut ele_other) = (iter_self.next(), iter_other.next());
|
||||||
|
let mut cur_index = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match (ele_self, ele_other) {
|
||||||
|
(Some(ra), Some(rb)) => {
|
||||||
|
if !ra.overlaps(rb) {
|
||||||
|
// there's no overlap and it's on the left of rb
|
||||||
|
if ra.to() <= rb.from() {
|
||||||
|
ranges.push(ra);
|
||||||
|
ele_self = iter_self.next();
|
||||||
|
cur_index += 1;
|
||||||
|
|
||||||
|
// otherwise it must be on the right, so move rb forward
|
||||||
|
} else {
|
||||||
|
ele_other = iter_other.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise there is overlap, so truncate or split
|
||||||
|
if rb.contains_range(&ra) {
|
||||||
|
ele_self = iter_self.next();
|
||||||
|
cur_index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [ ra ]
|
||||||
|
// [ rb ]
|
||||||
|
if ra.from() <= rb.from() && ra.to() <= rb.to() && ra.to() >= rb.from() {
|
||||||
|
let new = if ra.direction() == Direction::Backward {
|
||||||
|
Range::new(rb.from(), ra.head)
|
||||||
|
} else {
|
||||||
|
Range::new(ra.anchor, rb.from())
|
||||||
|
};
|
||||||
|
|
||||||
|
ranges.push(new);
|
||||||
|
ele_self = iter_self.next();
|
||||||
|
cur_index += 1;
|
||||||
|
|
||||||
|
// [ ra ]
|
||||||
|
// [ rb ]
|
||||||
|
} else if ra.from() >= rb.from() && ra.to() >= rb.to() && ra.from() <= rb.to() {
|
||||||
|
let new = if ra.direction() == Direction::Backward {
|
||||||
|
Range::new(ra.anchor, rb.to() + 1)
|
||||||
|
} else {
|
||||||
|
Range::new(rb.to(), ra.head)
|
||||||
|
};
|
||||||
|
|
||||||
|
// don't settle on the new range yet because the next
|
||||||
|
// rb could chop off the other end of ra
|
||||||
|
ele_self = Some(new);
|
||||||
|
ele_other = iter_other.next();
|
||||||
|
|
||||||
|
// [ ra ]
|
||||||
|
// [ rb ]
|
||||||
|
} else if ra.from() < rb.from() && ra.to() > rb.to() {
|
||||||
|
// we must split the range into two
|
||||||
|
let left = if ra.direction() == Direction::Backward {
|
||||||
|
Range::new(rb.from(), ra.head)
|
||||||
|
} else {
|
||||||
|
Range::new(ra.anchor, rb.from())
|
||||||
|
};
|
||||||
|
|
||||||
|
let right = if ra.direction() == Direction::Backward {
|
||||||
|
Range::new(ra.anchor, rb.to())
|
||||||
|
} else {
|
||||||
|
Range::new(rb.to(), ra.head)
|
||||||
|
};
|
||||||
|
|
||||||
|
// We do NOT push right onto the results right away.
|
||||||
|
// We must put it back into the iterator and check it
|
||||||
|
// again in case a further range splits it again.
|
||||||
|
ranges.push(left);
|
||||||
|
ele_other = iter_other.next();
|
||||||
|
|
||||||
|
// offset the primary index whenever we split
|
||||||
|
if cur_index < primary_index {
|
||||||
|
primary_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_index += 1;
|
||||||
|
ele_self = Some(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the rest just get included as is
|
||||||
|
(Some(range), None) => {
|
||||||
|
ranges.push(range);
|
||||||
|
ele_self = iter_self.next();
|
||||||
|
cur_index += 1;
|
||||||
|
}
|
||||||
|
// exhausted `self`, nothing left to do
|
||||||
|
(None, _) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ranges.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Selection::new(ranges, primary_index))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a Selection {
|
impl<'a> IntoIterator for &'a Selection {
|
||||||
type Item = &'a Range;
|
type Item = &'a Range;
|
||||||
type IntoIter = std::slice::Iter<'a, Range>;
|
type IntoIter = std::slice::Iter<'a, Range>;
|
||||||
|
|
||||||
fn into_iter(self) -> std::slice::Iter<'a, Range> {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.ranges().iter()
|
self.ranges().iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for Selection {
|
impl IntoIterator for Selection {
|
||||||
type Item = Range;
|
type Item = Range;
|
||||||
type IntoIter = smallvec::IntoIter<[Range; 1]>;
|
type IntoIter = smallvec::IntoIter<[Self::Item; 1]>;
|
||||||
|
|
||||||
fn into_iter(self) -> smallvec::IntoIter<[Range; 1]> {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.ranges.into_iter()
|
self.ranges.into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -885,6 +1024,7 @@ pub fn split_on_matches(text: RopeSlice, selection: &Selection, regex: &rope::Re
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::test;
|
||||||
use crate::Rope;
|
use crate::Rope;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -975,7 +1115,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_overlaps() {
|
fn test_range_overlaps() {
|
||||||
fn overlaps(a: (usize, usize), b: (usize, usize)) -> bool {
|
fn overlaps(a: (usize, usize), b: (usize, usize)) -> bool {
|
||||||
Range::new(a.0, a.1).overlaps(&Range::new(b.0, b.1))
|
Range::new(a.0, a.1).overlaps(&Range::new(b.0, b.1))
|
||||||
}
|
}
|
||||||
|
@ -1025,6 +1165,160 @@ mod test {
|
||||||
assert!(overlaps((1, 1), (1, 1)));
|
assert!(overlaps((1, 1), (1, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_selection_overlaps() {
|
||||||
|
fn overlaps(a: &[(usize, usize)], b: &[(usize, usize)]) -> bool {
|
||||||
|
let a = Selection::new(
|
||||||
|
a.iter()
|
||||||
|
.map(|(anchor, head)| Range::new(*anchor, *head))
|
||||||
|
.collect(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
let b = Selection::new(
|
||||||
|
b.iter()
|
||||||
|
.map(|(anchor, head)| Range::new(*anchor, *head))
|
||||||
|
.collect(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
a.overlaps(&b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two non-zero-width ranges, no overlap.
|
||||||
|
assert!(!overlaps(&[(0, 3)], &[(3, 6)]));
|
||||||
|
assert!(!overlaps(&[(0, 3)], &[(6, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 0)], &[(3, 6)]));
|
||||||
|
assert!(!overlaps(&[(3, 0)], &[(6, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 6)], &[(0, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 6)], &[(3, 0)]));
|
||||||
|
assert!(!overlaps(&[(6, 3)], &[(0, 3)]));
|
||||||
|
assert!(!overlaps(&[(6, 3)], &[(3, 0)]));
|
||||||
|
|
||||||
|
// more ranges in one or the other, no overlap
|
||||||
|
assert!(!overlaps(&[(6, 3), (9, 6)], &[(3, 0)]));
|
||||||
|
assert!(!overlaps(&[(6, 3), (6, 9)], &[(3, 0)]));
|
||||||
|
assert!(!overlaps(&[(3, 6), (9, 6)], &[(3, 0)]));
|
||||||
|
assert!(!overlaps(&[(3, 6), (6, 9)], &[(3, 0)]));
|
||||||
|
assert!(!overlaps(&[(6, 3), (9, 6)], &[(0, 3)]));
|
||||||
|
assert!(!overlaps(&[(6, 3), (6, 9)], &[(0, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 6), (9, 6)], &[(0, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 6), (6, 9)], &[(0, 3)]));
|
||||||
|
|
||||||
|
assert!(!overlaps(&[(6, 3)], &[(3, 0), (9, 6)]));
|
||||||
|
assert!(!overlaps(&[(6, 3)], &[(3, 0), (6, 9)]));
|
||||||
|
assert!(!overlaps(&[(3, 6)], &[(3, 0), (9, 6)]));
|
||||||
|
assert!(!overlaps(&[(3, 6)], &[(3, 0), (6, 9)]));
|
||||||
|
assert!(!overlaps(&[(6, 3)], &[(0, 3), (9, 6)]));
|
||||||
|
assert!(!overlaps(&[(6, 3)], &[(0, 3), (6, 9)]));
|
||||||
|
assert!(!overlaps(&[(3, 6)], &[(0, 3), (9, 6)]));
|
||||||
|
assert!(!overlaps(&[(3, 6)], &[(0, 3), (6, 9)]));
|
||||||
|
|
||||||
|
// Two non-zero-width ranges, overlap.
|
||||||
|
assert!(overlaps(&[(0, 4)], &[(3, 6)]));
|
||||||
|
assert!(overlaps(&[(0, 4)], &[(6, 3)]));
|
||||||
|
assert!(overlaps(&[(4, 0)], &[(3, 6)]));
|
||||||
|
assert!(overlaps(&[(4, 0)], &[(6, 3)]));
|
||||||
|
assert!(overlaps(&[(3, 6)], &[(0, 4)]));
|
||||||
|
assert!(overlaps(&[(3, 6)], &[(4, 0)]));
|
||||||
|
assert!(overlaps(&[(6, 3)], &[(0, 4)]));
|
||||||
|
assert!(overlaps(&[(6, 3)], &[(4, 0)]));
|
||||||
|
|
||||||
|
// Two non-zero-width ranges, overlap, extra from one or the other
|
||||||
|
assert!(overlaps(&[(0, 4), (7, 10)], &[(3, 6)]));
|
||||||
|
assert!(overlaps(&[(0, 4), (7, 10)], &[(6, 3)]));
|
||||||
|
assert!(overlaps(&[(4, 0), (7, 10)], &[(3, 6)]));
|
||||||
|
assert!(overlaps(&[(4, 0), (7, 10)], &[(6, 3)]));
|
||||||
|
assert!(overlaps(&[(3, 6), (7, 10)], &[(0, 4)]));
|
||||||
|
assert!(overlaps(&[(3, 6), (7, 10)], &[(4, 0)]));
|
||||||
|
assert!(overlaps(&[(6, 3), (7, 10)], &[(0, 4)]));
|
||||||
|
assert!(overlaps(&[(6, 3), (7, 10)], &[(4, 0)]));
|
||||||
|
assert!(overlaps(&[(0, 4), (10, 7)], &[(3, 6)]));
|
||||||
|
assert!(overlaps(&[(0, 4), (10, 7)], &[(6, 3)]));
|
||||||
|
assert!(overlaps(&[(4, 0), (10, 7)], &[(3, 6)]));
|
||||||
|
assert!(overlaps(&[(4, 0), (10, 7)], &[(6, 3)]));
|
||||||
|
assert!(overlaps(&[(3, 6), (10, 7)], &[(0, 4)]));
|
||||||
|
assert!(overlaps(&[(3, 6), (10, 7)], &[(4, 0)]));
|
||||||
|
assert!(overlaps(&[(6, 3), (10, 7)], &[(0, 4)]));
|
||||||
|
assert!(overlaps(&[(6, 3), (10, 7)], &[(4, 0)]));
|
||||||
|
|
||||||
|
assert!(overlaps(&[(0, 4)], &[(3, 6), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(0, 4)], &[(6, 3), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(4, 0)], &[(3, 6), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(4, 0)], &[(6, 3), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(3, 6)], &[(0, 4), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(3, 6)], &[(4, 0), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(6, 3)], &[(0, 4), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(6, 3)], &[(4, 0), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(0, 4)], &[(3, 6), (10, 7)]));
|
||||||
|
assert!(overlaps(&[(0, 4)], &[(6, 3), (10, 7)]));
|
||||||
|
assert!(overlaps(&[(4, 0)], &[(3, 6), (10, 7)]));
|
||||||
|
assert!(overlaps(&[(4, 0)], &[(6, 3), (10, 7)]));
|
||||||
|
assert!(overlaps(&[(3, 6)], &[(0, 4), (10, 7)]));
|
||||||
|
assert!(overlaps(&[(3, 6)], &[(4, 0), (10, 7)]));
|
||||||
|
assert!(overlaps(&[(6, 3)], &[(0, 4), (10, 7)]));
|
||||||
|
assert!(overlaps(&[(6, 3)], &[(4, 0), (10, 7)]));
|
||||||
|
|
||||||
|
// Zero-width and non-zero-width range, no overlap.
|
||||||
|
assert!(!overlaps(&[(0, 3)], &[(3, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 0)], &[(3, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 3)], &[(0, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 3)], &[(3, 0)]));
|
||||||
|
|
||||||
|
assert!(!overlaps(&[(0, 3), (7, 10)], &[(3, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 0), (7, 10)], &[(3, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 3), (7, 10)], &[(0, 3)]));
|
||||||
|
assert!(!overlaps(&[(3, 3), (7, 10)], &[(3, 0)]));
|
||||||
|
|
||||||
|
assert!(!overlaps(&[(0, 3)], &[(3, 3), (7, 10)]));
|
||||||
|
assert!(!overlaps(&[(3, 0)], &[(3, 3), (7, 10)]));
|
||||||
|
assert!(!overlaps(&[(3, 3)], &[(0, 3), (7, 10)]));
|
||||||
|
assert!(!overlaps(&[(3, 3)], &[(3, 0), (7, 10)]));
|
||||||
|
|
||||||
|
// Zero-width and non-zero-width range, overlap.
|
||||||
|
assert!(overlaps(&[(1, 4)], &[(1, 1)]));
|
||||||
|
assert!(overlaps(&[(4, 1)], &[(1, 1)]));
|
||||||
|
assert!(overlaps(&[(1, 1)], &[(1, 4)]));
|
||||||
|
assert!(overlaps(&[(1, 1)], &[(4, 1)]));
|
||||||
|
|
||||||
|
assert!(overlaps(&[(1, 4)], &[(3, 3)]));
|
||||||
|
assert!(overlaps(&[(4, 1)], &[(3, 3)]));
|
||||||
|
assert!(overlaps(&[(3, 3)], &[(1, 4)]));
|
||||||
|
assert!(overlaps(&[(3, 3)], &[(4, 1)]));
|
||||||
|
|
||||||
|
assert!(overlaps(&[(1, 4), (7, 10)], &[(1, 1)]));
|
||||||
|
assert!(overlaps(&[(4, 1), (7, 10)], &[(1, 1)]));
|
||||||
|
assert!(overlaps(&[(1, 1), (7, 10)], &[(1, 4)]));
|
||||||
|
assert!(overlaps(&[(1, 1), (7, 10)], &[(4, 1)]));
|
||||||
|
|
||||||
|
assert!(overlaps(&[(1, 4), (7, 10)], &[(3, 3)]));
|
||||||
|
assert!(overlaps(&[(4, 1), (7, 10)], &[(3, 3)]));
|
||||||
|
assert!(overlaps(&[(3, 3), (7, 10)], &[(1, 4)]));
|
||||||
|
assert!(overlaps(&[(3, 3), (7, 10)], &[(4, 1)]));
|
||||||
|
|
||||||
|
assert!(overlaps(&[(1, 4)], &[(1, 1), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(4, 1)], &[(1, 1), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(1, 1)], &[(1, 4), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(1, 1)], &[(4, 1), (7, 10)]));
|
||||||
|
|
||||||
|
assert!(overlaps(&[(1, 4)], &[(3, 3), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(4, 1)], &[(3, 3), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(3, 3)], &[(1, 4), (7, 10)]));
|
||||||
|
assert!(overlaps(&[(3, 3)], &[(4, 1), (7, 10)]));
|
||||||
|
|
||||||
|
// Two zero-width ranges, no overlap.
|
||||||
|
assert!(!overlaps(&[(0, 0)], &[(1, 1)]));
|
||||||
|
assert!(!overlaps(&[(1, 1)], &[(0, 0)]));
|
||||||
|
|
||||||
|
assert!(!overlaps(&[(0, 0), (2, 2)], &[(1, 1)]));
|
||||||
|
assert!(!overlaps(&[(0, 0), (2, 2)], &[(1, 1)]));
|
||||||
|
assert!(!overlaps(&[(1, 1)], &[(0, 0), (2, 2)]));
|
||||||
|
assert!(!overlaps(&[(1, 1)], &[(0, 0), (2, 2)]));
|
||||||
|
|
||||||
|
// Two zero-width ranges, overlap.
|
||||||
|
assert!(overlaps(&[(1, 1)], &[(1, 1)]));
|
||||||
|
assert!(overlaps(&[(1, 1), (2, 2)], &[(1, 1)]));
|
||||||
|
assert!(overlaps(&[(1, 1)], &[(1, 1), (2, 2)]));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_grapheme_aligned() {
|
fn test_grapheme_aligned() {
|
||||||
let r = Rope::from_str("\r\nHi\r\n");
|
let r = Rope::from_str("\r\nHi\r\n");
|
||||||
|
@ -1381,9 +1675,15 @@ mod test {
|
||||||
// multiple matches
|
// multiple matches
|
||||||
assert!(contains(vec!((1, 1), (2, 2)), vec!((1, 1), (2, 2))));
|
assert!(contains(vec!((1, 1), (2, 2)), vec!((1, 1), (2, 2))));
|
||||||
|
|
||||||
// smaller set can't contain bigger
|
// extra items out of range
|
||||||
assert!(!contains(vec!((1, 1)), vec!((1, 1), (2, 2))));
|
assert!(!contains(vec!((1, 1)), vec!((1, 1), (2, 2))));
|
||||||
|
|
||||||
|
// one big range can contain multiple smaller ranges
|
||||||
|
assert!(contains(
|
||||||
|
vec!((1, 10)),
|
||||||
|
vec!((1, 1), (2, 2), (3, 3), (3, 5), (7, 10))
|
||||||
|
));
|
||||||
|
|
||||||
assert!(contains(
|
assert!(contains(
|
||||||
vec!((1, 1), (2, 4), (5, 6), (7, 9), (10, 13)),
|
vec!((1, 1), (2, 4), (5, 6), (7, 9), (10, 13)),
|
||||||
vec!((3, 4), (7, 9))
|
vec!((3, 4), (7, 9))
|
||||||
|
@ -1396,4 +1696,143 @@ mod test {
|
||||||
vec!((1, 2), (3, 4), (7, 9))
|
vec!((1, 2), (3, 4), (7, 9))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_selection_without() {
|
||||||
|
let without = |one, two, expected: Option<_>| {
|
||||||
|
println!("one: {:?}", one);
|
||||||
|
println!("two: {:?}", two);
|
||||||
|
println!("expected: {:?}", expected);
|
||||||
|
|
||||||
|
let (one_text, one_sel) = test::print(one);
|
||||||
|
let (two_text, two_sel) = test::print(two);
|
||||||
|
assert_eq!(one_text, two_text); // sanity
|
||||||
|
let actual_sel = one_sel.without(&two_sel);
|
||||||
|
|
||||||
|
let expected_sel = expected.map(|exp| {
|
||||||
|
let (expected_text, expected_sel) = test::print(exp);
|
||||||
|
assert_eq!(two_text, expected_text);
|
||||||
|
expected_sel
|
||||||
|
});
|
||||||
|
let actual = actual_sel
|
||||||
|
.as_ref()
|
||||||
|
.map(|sel| test::plain(two_text.to_string(), sel));
|
||||||
|
|
||||||
|
println!("actual: {:?}\n\n", actual);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
expected_sel,
|
||||||
|
actual_sel,
|
||||||
|
"expected: {:?}, got: {:?}",
|
||||||
|
expected_sel
|
||||||
|
.as_ref()
|
||||||
|
.map(|sel| test::plain(two_text.to_string(), sel)),
|
||||||
|
actual,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
without(
|
||||||
|
"#[foo bar baz|]#",
|
||||||
|
"foo #[bar|]# baz",
|
||||||
|
Some("#[foo |]#bar#( baz|)#"),
|
||||||
|
);
|
||||||
|
|
||||||
|
without("#[foo bar baz|]#", "#[foo bar baz|]#", None);
|
||||||
|
without("#[foo bar|]# baz", "#[foo bar baz|]#", None);
|
||||||
|
without("foo #[bar|]# baz", "#[foo bar baz|]#", None);
|
||||||
|
|
||||||
|
// direction is preserved
|
||||||
|
without(
|
||||||
|
"#[|foo bar baz]#",
|
||||||
|
"foo #[|bar]# baz",
|
||||||
|
Some("#[|foo ]#bar#(| baz)#"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// preference for direction is given to the left
|
||||||
|
without(
|
||||||
|
"#[|foo bar baz]#",
|
||||||
|
"foo #[bar|]# baz",
|
||||||
|
Some("#[|foo ]#bar#(| baz)#"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// disjoint ranges on the right are ignored
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# baz",
|
||||||
|
"foo bar #[baz|]#",
|
||||||
|
Some("#[foo bar|]# baz"),
|
||||||
|
);
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# baz",
|
||||||
|
"foo bar#[ baz|]#",
|
||||||
|
Some("#[foo bar|]# baz"),
|
||||||
|
);
|
||||||
|
without(
|
||||||
|
"#(foo|)# #[bar|]# baz",
|
||||||
|
"foo#[ b|]#ar ba#(z|)#",
|
||||||
|
Some("#(foo|)# b#[ar|]# baz"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// ranges contained by those one on the right are removed
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# #(b|)#az",
|
||||||
|
"foo bar#[ b|]#a#(z|)#",
|
||||||
|
Some("#[foo bar|]# baz"),
|
||||||
|
);
|
||||||
|
without(
|
||||||
|
"#[foo|]# bar #(baz|)#",
|
||||||
|
"foo bar#[ b|]#a#(z|)#",
|
||||||
|
Some("#[foo|]# bar b#(a|)#z"),
|
||||||
|
);
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# #(b|)#az",
|
||||||
|
"foo bar #[b|]#a#(z|)#",
|
||||||
|
Some("#[foo bar|]# baz"),
|
||||||
|
);
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# #(b|)#a#(z|)#",
|
||||||
|
"foo bar #[b|]#a#(z|)#",
|
||||||
|
Some("#[foo bar|]# baz"),
|
||||||
|
);
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# #(b|)#a#(z|)#",
|
||||||
|
"foo bar #[b|]#a#(z|)#",
|
||||||
|
Some("#[foo bar|]# baz"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// more than one range intersected by a single range on the right
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# #(baz|)#",
|
||||||
|
"foo b#[ar ba|]#z",
|
||||||
|
Some("#[foo b|]#ar ba#(z|)#"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// partial overlap
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# baz",
|
||||||
|
"foo #[bar baz|]#",
|
||||||
|
Some("#[foo |]#bar baz"),
|
||||||
|
);
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# baz",
|
||||||
|
"foo#[ bar baz|]#",
|
||||||
|
Some("#[foo|]# bar baz"),
|
||||||
|
);
|
||||||
|
without(
|
||||||
|
"#[foo bar|]# baz",
|
||||||
|
"foo ba#[r baz|]#",
|
||||||
|
Some("#[foo ba|]#r baz"),
|
||||||
|
);
|
||||||
|
without(
|
||||||
|
"foo ba#[r baz|]#",
|
||||||
|
"#[foo bar|]# baz",
|
||||||
|
Some("foo bar#[ baz|]#"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// primary selection is moved - preference given to the left of a split
|
||||||
|
without(
|
||||||
|
"#(|foo)# #(|bar baz)# #[|quux]#",
|
||||||
|
"f#(o|)#o ba#[r b|]#az q#(uu|)#x",
|
||||||
|
Some("#(|f)#o#(|o)# #(|ba)#r b#(|az)# #[|q]#uu#(|x)#"),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue