on 376c19e help sign in
helix/helix-core/src/selection.rs
1234 lines 42 kB history raw
1//! Selections are the primary editing construct. Even cursors are
2//! defined as a selection range.
3//!
4//! All positioning is done via `char` offsets into the buffer.
5use crate::{
6 graphemes::{
7 ensure_grapheme_boundary_next, ensure_grapheme_boundary_prev, next_grapheme_boundary,
8 prev_grapheme_boundary,
9 },
10 movement::Direction,
11 Assoc, ChangeSet, RopeGraphemes, RopeSlice,
12};
13use smallvec::{smallvec, SmallVec};
14use std::borrow::Cow;
16/// A single selection range.
17///
18/// A range consists of an "anchor" and "head" position in
19/// the text. The head is the part that the user moves when
20/// directly extending a selection. The head and anchor
21/// can be in any order, or even share the same position.
22///
23/// The anchor and head positions use gap indexing, meaning
24/// that their indices represent the gaps *between* `char`s
25/// rather than the `char`s themselves. For example, 1
26/// represents the position between the first and second `char`.
27///
28/// Below are some examples of `Range` configurations.
29/// The anchor and head indices are shown as "(anchor, head)"
30/// tuples, followed by example text with "[" and "]" symbols
31/// representing the anchor and head positions:
32///
33/// - (0, 3): `[Som]e text`.
34/// - (3, 0): `]Som[e text`.
35/// - (2, 7): `So[me te]xt`.
36/// - (1, 1): `S[]ome text`.
37///
38/// Ranges are considered to be inclusive on the left and
39/// exclusive on the right, regardless of anchor-head ordering.
40/// This means, for example, that non-zero-width ranges that
41/// are directly adjecent, sharing an edge, do not overlap.
42/// However, a zero-width range will overlap with the shared
43/// left-edge of another range.
44///
45/// By convention, user-facing ranges are considered to have
46/// a block cursor on the head-side of the range that spans a
47/// single grapheme inward from the range's edge. There are a
48/// variety of helper methods on `Range` for working in terms of
49/// that block cursor, all of which have `cursor` in their name.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub struct Range {
52 /// The anchor of the range: the side that doesn't move when extending.
53 pub anchor: usize,
54 /// The head of the range, moved when extending.
55 pub head: usize,
56 /// The previous visual offset (softwrapped lines and columns) from
57 /// the start of the line
58 pub old_visual_position: Option<(u32, u32)>,
61impl Range {
62 pub fn new(anchor: usize, head: usize) -> Self {
63 Self {
64 anchor,
65 head,
66 old_visual_position: None,
67 }
68 }
70 pub fn point(head: usize) -> Self {
71 Self::new(head, head)
72 }
74 /// Start of the range.
75 #[inline]
76 #[must_use]
77 pub fn from(&self) -> usize {
78 std::cmp::min(self.anchor, self.head)
79 }
81 /// End of the range.
82 #[inline]
83 #[must_use]
84 pub fn to(&self) -> usize {
85 std::cmp::max(self.anchor, self.head)
86 }
88 /// Total length of the range.
89 #[inline]
90 #[must_use]
91 pub fn len(&self) -> usize {
92 self.to() - self.from()
93 }
95 /// The (inclusive) range of lines that the range overlaps.
96 #[inline]
97 #[must_use]
98 pub fn line_range(&self, text: RopeSlice) -> (usize, usize) {
99 let from = self.from();
100 let to = if self.is_empty() {
101 self.to()
102 } else {
103 prev_grapheme_boundary(text, self.to()).max(from)
104 };
106 (text.char_to_line(from), text.char_to_line(to))
107 }
109 /// `true` when head and anchor are at the same position.
110 #[inline]
111 pub fn is_empty(&self) -> bool {
112 self.anchor == self.head
113 }
115 /// `Direction::Backward` when head < anchor.
116 /// `Direction::Backward` otherwise.
117 #[inline]
118 #[must_use]
119 pub fn direction(&self) -> Direction {
120 if self.head < self.anchor {
121 Direction::Backward
122 } else {
123 Direction::Forward
124 }
125 }
127 /// Flips the direction of the selection
128 pub fn flip(&self) -> Self {
129 Self {
130 anchor: self.head,
131 head: self.anchor,
132 old_visual_position: self.old_visual_position,
133 }
134 }
136 /// Returns the selection if it goes in the direction of `direction`,
137 /// flipping the selection otherwise.
138 pub fn with_direction(self, direction: Direction) -> Self {
139 if self.direction() == direction {
140 self
141 } else {
142 self.flip()
143 }
144 }
146 /// Check two ranges for overlap.
147 #[must_use]
148 pub fn overlaps(&self, other: &Self) -> bool {
149 // To my eye, it's non-obvious why this works, but I arrived
150 // at it after transforming the slower version that explicitly
151 // enumerated more cases. The unit tests are thorough.
152 self.from() == other.from() || (self.to() > other.from() && other.to() > self.from())
153 }
155 #[inline]
156 pub fn contains_range(&self, other: &Self) -> bool {
157 self.from() <= other.from() && self.to() >= other.to()
158 }
160 pub fn contains(&self, pos: usize) -> bool {
161 self.from() <= pos && pos < self.to()
162 }
164 /// Map a range through a set of changes. Returns a new range representing the same position
165 /// after the changes are applied.
166 pub fn map(self, changes: &ChangeSet) -> Self {
167 use std::cmp::Ordering;
168 let (anchor, head) = match self.anchor.cmp(&self.head) {
169 Ordering::Equal => (
170 changes.map_pos(self.anchor, Assoc::After),
171 changes.map_pos(self.head, Assoc::After),
172 ),
173 Ordering::Less => (
174 changes.map_pos(self.anchor, Assoc::After),
175 changes.map_pos(self.head, Assoc::Before),
176 ),
177 Ordering::Greater => (
178 changes.map_pos(self.anchor, Assoc::Before),
179 changes.map_pos(self.head, Assoc::After),
180 ),
181 };
183 // We want to return a new `Range` with `horiz == None` every time,
184 // even if the anchor and head haven't changed, because we don't
185 // know if the *visual* position hasn't changed due to
186 // character-width or grapheme changes earlier in the text.
187 Self {
188 anchor,
189 head,
190 old_visual_position: None,
191 }
192 }
194 /// Extend the range to cover at least `from` `to`.
195 #[must_use]
196 pub fn extend(&self, from: usize, to: usize) -> Self {
197 debug_assert!(from <= to);
199 if self.anchor <= self.head {
200 Self {
201 anchor: self.anchor.min(from),
202 head: self.head.max(to),
203 old_visual_position: None,
204 }
205 } else {
206 Self {
207 anchor: self.anchor.max(to),
208 head: self.head.min(from),
209 old_visual_position: None,
210 }
211 }
212 }
214 /// Returns a range that encompasses both input ranges.
215 ///
216 /// This is like `extend()`, but tries to negotiate the
217 /// anchor/head ordering between the two input ranges.
218 #[must_use]
219 pub fn merge(&self, other: Self) -> Self {
220 if self.anchor > self.head && other.anchor > other.head {
221 Range {
222 anchor: self.anchor.max(other.anchor),
223 head: self.head.min(other.head),
224 old_visual_position: None,
225 }
226 } else {
227 Range {
228 anchor: self.from().min(other.from()),
229 head: self.to().max(other.to()),
230 old_visual_position: None,
231 }
232 }
233 }
235 // groupAt
237 /// Returns the text inside this range given the text of the whole buffer.
238 ///
239 /// The returned `Cow` is a reference if the range of text is inside a single
240 /// chunk of the rope. Otherwise a copy of the text is returned. Consider
241 /// using `slice` instead if you do not need a `Cow` or `String` to avoid copying.
242 #[inline]
243 pub fn fragment<'a, 'b: 'a>(&'a self, text: RopeSlice<'b>) -> Cow<'b, str> {
244 self.slice(text).into()
245 }
247 /// Returns the text inside this range given the text of the whole buffer.
248 ///
249 /// The returned value is a reference to the passed slice. This method never
250 /// copies any contents.
251 #[inline]
252 pub fn slice<'a, 'b: 'a>(&'a self, text: RopeSlice<'b>) -> RopeSlice<'b> {
253 text.slice(self.from()..self.to())
254 }
256 //--------------------------------
257 // Alignment methods.
259 /// Compute a possibly new range from this range, with its ends
260 /// shifted as needed to align with grapheme boundaries.
261 ///
262 /// Zero-width ranges will always stay zero-width, and non-zero-width
263 /// ranges will never collapse to zero-width.
264 #[must_use]
265 pub fn grapheme_aligned(&self, slice: RopeSlice) -> Self {
266 use std::cmp::Ordering;
267 let (new_anchor, new_head) = match self.anchor.cmp(&self.head) {
268 Ordering::Equal => {
269 let pos = ensure_grapheme_boundary_prev(slice, self.anchor);
270 (pos, pos)
271 }
272 Ordering::Less => (
273 ensure_grapheme_boundary_prev(slice, self.anchor),
274 ensure_grapheme_boundary_next(slice, self.head),
275 ),
276 Ordering::Greater => (
277 ensure_grapheme_boundary_next(slice, self.anchor),
278 ensure_grapheme_boundary_prev(slice, self.head),
279 ),
280 };
281 Range {
282 anchor: new_anchor,
283 head: new_head,
284 old_visual_position: if new_anchor == self.anchor {
285 self.old_visual_position
286 } else {
287 None
288 },
289 }
290 }
292 /// Compute a possibly new range from this range, attempting to ensure
293 /// a minimum range width of 1 char by shifting the head in the forward
294 /// direction as needed.
295 ///
296 /// This method will never shift the anchor, and will only shift the
297 /// head in the forward direction. Therefore, this method can fail
298 /// at ensuring the minimum width if and only if the passed range is
299 /// both zero-width and at the end of the `RopeSlice`.
300 ///
301 /// If the input range is grapheme-boundary aligned, the returned range
302 /// will also be. Specifically, if the head needs to shift to achieve
303 /// the minimum width, it will shift to the next grapheme boundary.
304 #[must_use]
305 #[inline]
306 pub fn min_width_1(&self, slice: RopeSlice) -> Self {
307 if self.anchor == self.head {
308 Range {
309 anchor: self.anchor,
310 head: next_grapheme_boundary(slice, self.head),
311 old_visual_position: self.old_visual_position,
312 }
313 } else {
314 *self
315 }
316 }
318 //--------------------------------
319 // Block-cursor methods.
321 /// Gets the left-side position of the block cursor.
322 #[must_use]
323 #[inline]
324 pub fn cursor(self, text: RopeSlice) -> usize {
325 if self.head > self.anchor {
326 prev_grapheme_boundary(text, self.head)
327 } else {
328 self.head
329 }
330 }
332 /// Puts the left side of the block cursor at `char_idx`, optionally extending.
333 ///
334 /// This follows "1-width" semantics, and therefore does a combination of anchor
335 /// and head moves to behave as if both the front and back of the range are 1-width
336 /// blocks
337 ///
338 /// This method assumes that the range and `char_idx` are already properly
339 /// grapheme-aligned.
340 #[must_use]
341 #[inline]
342 pub fn put_cursor(self, text: RopeSlice, char_idx: usize, extend: bool) -> Range {
343 if extend {
344 let anchor = if self.head >= self.anchor && char_idx < self.anchor {
345 next_grapheme_boundary(text, self.anchor)
346 } else if self.head < self.anchor && char_idx >= self.anchor {
347 prev_grapheme_boundary(text, self.anchor)
348 } else {
349 self.anchor
350 };
352 if anchor <= char_idx {
353 Range::new(anchor, next_grapheme_boundary(text, char_idx))
354 } else {
355 Range::new(anchor, char_idx)
356 }
357 } else {
358 Range::point(char_idx)
359 }
360 }
362 /// The line number that the block-cursor is on.
363 #[inline]
364 #[must_use]
365 pub fn cursor_line(&self, text: RopeSlice) -> usize {
366 text.char_to_line(self.cursor(text))
367 }
369 /// Returns true if this Range covers a single grapheme in the given text
370 pub fn is_single_grapheme(&self, doc: RopeSlice) -> bool {
371 let mut graphemes = RopeGraphemes::new(doc.slice(self.from()..self.to()));
372 let first = graphemes.next();
373 let second = graphemes.next();
374 first.is_some() && second.is_none()
375 }
378impl From<(usize, usize)> for Range {
379 fn from((anchor, head): (usize, usize)) -> Self {
380 Self {
381 anchor,
382 head,
383 old_visual_position: None,
384 }
385 }
388/// A selection consists of one or more selection ranges.
389/// invariant: A selection can never be empty (always contains at least primary range).
390#[derive(Debug, Clone, PartialEq, Eq)]
391pub struct Selection {
392 ranges: SmallVec<[Range; 1]>,
393 primary_index: usize,
396#[allow(clippy::len_without_is_empty)] // a Selection is never empty
397impl Selection {
398 // eq
400 #[inline]
401 #[must_use]
402 pub fn primary(&self) -> Range {
403 self.ranges[self.primary_index]
404 }
406 #[inline]
407 #[must_use]
408 pub fn primary_mut(&mut self) -> &mut Range {
409 &mut self.ranges[self.primary_index]
410 }
412 /// Ensure selection containing only the primary selection.
413 pub fn into_single(self) -> Self {
414 if self.ranges.len() == 1 {
415 self
416 } else {
417 Self {
418 ranges: smallvec![self.ranges[self.primary_index]],
419 primary_index: 0,
420 }
421 }
422 }
424 /// Adds a new range to the selection and makes it the primary range.
425 pub fn push(mut self, range: Range) -> Self {
426 self.ranges.push(range);
427 self.set_primary_index(self.ranges().len() - 1);
428 self.normalize()
429 }
431 /// Removes a range from the selection.
432 pub fn remove(mut self, index: usize) -> Self {
433 assert!(
434 self.ranges.len() > 1,
435 "can't remove the last range from a selection!"
436 );
438 self.ranges.remove(index);
439 if index < self.primary_index || self.primary_index == self.ranges.len() {
440 self.primary_index -= 1;
441 }
442 self
443 }
445 /// Replace a range in the selection with a new range.
446 pub fn replace(mut self, index: usize, range: Range) -> Self {
447 self.ranges[index] = range;
448 self.normalize()
449 }
451 /// Map selections over a set of changes. Useful for adjusting the selection position after
452 /// applying changes to a document.
453 pub fn map(self, changes: &ChangeSet) -> Self {
454 if changes.is_empty() {
455 return self;
456 }
458 Self::new(
459 self.ranges
460 .into_iter()
461 .map(|range| range.map(changes))
462 .collect(),
463 self.primary_index,
464 )
465 }
467 pub fn ranges(&self) -> &[Range] {
468 &self.ranges
469 }
471 pub fn primary_index(&self) -> usize {
472 self.primary_index
473 }
475 pub fn set_primary_index(&mut self, idx: usize) {
476 assert!(idx < self.ranges.len());
477 self.primary_index = idx;
478 }
480 #[must_use]
481 /// Constructs a selection holding a single range.
482 pub fn single(anchor: usize, head: usize) -> Self {
483 Self {
484 ranges: smallvec![Range {
485 anchor,
486 head,
487 old_visual_position: None
488 }],
489 primary_index: 0,
490 }
491 }
493 /// Constructs a selection holding a single cursor.
494 pub fn point(pos: usize) -> Self {
495 Self::single(pos, pos)
496 }
498 /// Normalizes a `Selection`.
499 fn normalize(mut self) -> Self {
500 let mut primary = self.ranges[self.primary_index];
501 self.ranges.sort_unstable_by_key(Range::from);
503 self.ranges.dedup_by(|curr_range, prev_range| {
504 if prev_range.overlaps(curr_range) {
505 let new_range = curr_range.merge(*prev_range);
506 if prev_range == &primary || curr_range == &primary {
507 primary = new_range;
508 }
509 *prev_range = new_range;
510 true
511 } else {
512 false
513 }
514 });
516 self.primary_index = self
517 .ranges
518 .iter()
519 .position(|&range| range == primary)
520 .unwrap();
522 self
523 }
525 // Merges all ranges that are consecutive
526 pub fn merge_consecutive_ranges(mut self) -> Self {
527 let mut primary = self.ranges[self.primary_index];
529 self.ranges.dedup_by(|curr_range, prev_range| {
530 if prev_range.to() == curr_range.from() {
531 let new_range = curr_range.merge(*prev_range);
532 if prev_range == &primary || curr_range == &primary {
533 primary = new_range;
534 }
535 *prev_range = new_range;
536 true
537 } else {
538 false
539 }
540 });
542 self.primary_index = self
543 .ranges
544 .iter()
545 .position(|&range| range == primary)
546 .unwrap();
548 self
549 }
551 // TODO: consume an iterator or a vec to reduce allocations?
552 #[must_use]
553 pub fn new(ranges: SmallVec<[Range; 1]>, primary_index: usize) -> Self {
554 assert!(!ranges.is_empty());
555 debug_assert!(primary_index < ranges.len());
557 let mut selection = Self {
558 ranges,
559 primary_index,
560 };
562 if selection.ranges.len() > 1 {
563 // TODO: only normalize if needed (any ranges out of order)
564 selection = selection.normalize();
565 }
567 selection
568 }
570 /// Takes a closure and maps each `Range` over the closure.
571 pub fn transform<F>(mut self, mut f: F) -> Self
572 where
573 F: FnMut(Range) -> Range,
574 {
575 for range in self.ranges.iter_mut() {
576 *range = f(*range)
577 }
578 self.normalize()
579 }
581 // Ensures the selection adheres to the following invariants:
582 // 1. All ranges are grapheme aligned.
583 // 2. All ranges are at least 1 character wide, unless at the
584 // very end of the document.
585 // 3. Ranges are non-overlapping.
586 // 4. Ranges are sorted by their position in the text.
587 pub fn ensure_invariants(self, text: RopeSlice) -> Self {
588 self.transform(|r| r.min_width_1(text).grapheme_aligned(text))
589 .normalize()
590 }
592 /// Transforms the selection into all of the left-side head positions,
593 /// using block-cursor semantics.
594 pub fn cursors(self, text: RopeSlice) -> Self {
595 self.transform(|range| Range::point(range.cursor(text)))
596 }
598 pub fn fragments<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = Cow<str>> + 'a {
599 self.ranges.iter().map(move |range| range.fragment(text))
600 }
602 pub fn slices<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = RopeSlice> + 'a {
603 self.ranges.iter().map(move |range| range.slice(text))
604 }
606 #[inline(always)]
607 pub fn iter(&self) -> std::slice::Iter<'_, Range> {
608 self.ranges.iter()
609 }
611 #[inline(always)]
612 pub fn len(&self) -> usize {
613 self.ranges.len()
614 }
616 // returns true if self ⊇ other
617 pub fn contains(&self, other: &Selection) -> bool {
618 // can't contain other if it is larger
619 if other.len() > self.len() {
620 return false;
621 }
623 let (mut iter_self, mut iter_other) = (self.iter(), other.iter());
624 let (mut ele_self, mut ele_other) = (iter_self.next(), iter_other.next());
626 loop {
627 match (ele_self, ele_other) {
628 (Some(ra), Some(rb)) => {
629 if !ra.contains_range(rb) {
630 // `self` doesn't contain next element from `other`, advance `self`, we need to match all from `other`
631 ele_self = iter_self.next();
632 } else {
633 // matched element from `other`, advance `other`
634 ele_other = iter_other.next();
635 };
636 }
637 (None, Some(_)) => {
638 // exhausted `self`, we can't match the reminder of `other`
639 return false;
640 }
641 (_, None) => {
642 // no elements from `other` left to match, `self` contains `other`
643 return true;
644 }
645 }
646 }
647 }
650impl<'a> IntoIterator for &'a Selection {
651 type Item = &'a Range;
652 type IntoIter = std::slice::Iter<'a, Range>;
654 fn into_iter(self) -> std::slice::Iter<'a, Range> {
655 self.ranges().iter()
656 }
659// TODO: checkSelection -> check if valid for doc length && sorted
661pub fn keep_or_remove_matches(
662 text: RopeSlice,
663 selection: &Selection,
664 regex: &crate::regex::Regex,
665 remove: bool,
666) -> Option<Selection> {
667 let result: SmallVec<_> = selection
668 .iter()
669 .filter(|range| regex.is_match(&range.fragment(text)) ^ remove)
670 .copied()
671 .collect();
673 // TODO: figure out a new primary index
674 if !result.is_empty() {
675 return Some(Selection::new(result, 0));
676 }
677 None
680pub fn select_on_matches(
681 text: RopeSlice,
682 selection: &Selection,
683 regex: &crate::regex::Regex,
684) -> Option<Selection> {
685 let mut result = SmallVec::with_capacity(selection.len());
687 for sel in selection {
688 // TODO: can't avoid occasional allocations since Regex can't operate on chunks yet
689 let fragment = sel.fragment(text);
691 let sel_start = sel.from();
692 let start_byte = text.char_to_byte(sel_start);
694 for mat in regex.find_iter(&fragment) {
695 // TODO: retain range direction
697 let start = text.byte_to_char(start_byte + mat.start());
698 let end = text.byte_to_char(start_byte + mat.end());
700 let range = Range::new(start, end);
701 // Make sure the match is not right outside of the selection.
702 // These invalid matches can come from using RegEx anchors like `^`, `$`
703 if range != Range::point(sel.to()) {
704 result.push(range);
705 }
706 }
707 }
709 // TODO: figure out a new primary index
710 if !result.is_empty() {
711 return Some(Selection::new(result, 0));
712 }
714 None
717// TODO: support to split on capture #N instead of whole match
718pub fn split_on_matches(
719 text: RopeSlice,
720 selection: &Selection,
721 regex: &crate::regex::Regex,
722) -> Selection {
723 let mut result = SmallVec::with_capacity(selection.len());
725 for sel in selection {
726 // Special case: zero-width selection.
727 if sel.from() == sel.to() {
728 result.push(*sel);
729 continue;
730 }
732 // TODO: can't avoid occasional allocations since Regex can't operate on chunks yet
733 let fragment = sel.fragment(text);
735 let sel_start = sel.from();
736 let sel_end = sel.to();
738 let start_byte = text.char_to_byte(sel_start);
740 let mut start = sel_start;
742 for mat in regex.find_iter(&fragment) {
743 // TODO: retain range direction
744 let end = text.byte_to_char(start_byte + mat.start());
745 result.push(Range::new(start, end));
746 start = text.byte_to_char(start_byte + mat.end());
747 }
749 if start < sel_end {
750 result.push(Range::new(start, sel_end));
751 }
752 }
754 // TODO: figure out a new primary index
755 Selection::new(result, 0)
758#[cfg(test)]
759mod test {
760 use super::*;
761 use crate::Rope;
763 #[test]
764 #[should_panic]
765 fn test_new_empty() {
766 let _ = Selection::new(smallvec![], 0);
767 }
769 #[test]
770 fn test_create_normalizes_and_merges() {
771 let sel = Selection::new(
772 smallvec![
773 Range::new(10, 12),
774 Range::new(6, 7),
775 Range::new(4, 5),
776 Range::new(3, 4),
777 Range::new(0, 6),
778 Range::new(7, 8),
779 Range::new(9, 13),
780 Range::new(13, 14),
781 ],
782 0,
783 );
785 let res = sel
786 .ranges
787 .into_iter()
788 .map(|range| format!("{}/{}", range.anchor, range.head))
789 .collect::<Vec<String>>()
790 .join(",");
792 assert_eq!(res, "0/6,6/7,7/8,9/13,13/14");
794 // it correctly calculates a new primary index
795 let sel = Selection::new(
796 smallvec![Range::new(0, 2), Range::new(1, 5), Range::new(4, 7)],
797 2,
798 );
800 let res = sel
801 .ranges
802 .into_iter()
803 .map(|range| format!("{}/{}", range.anchor, range.head))
804 .collect::<Vec<String>>()
805 .join(",");
807 assert_eq!(res, "0/7");
808 assert_eq!(sel.primary_index, 0);
809 }
811 #[test]
812 fn test_create_merges_adjacent_points() {
813 let sel = Selection::new(
814 smallvec![
815 Range::new(10, 12),
816 Range::new(12, 12),
817 Range::new(12, 12),
818 Range::new(10, 10),
819 Range::new(8, 10),
820 ],
821 0,
822 );
824 let res = sel
825 .ranges
826 .into_iter()
827 .map(|range| format!("{}/{}", range.anchor, range.head))
828 .collect::<Vec<String>>()
829 .join(",");
831 assert_eq!(res, "8/10,10/12,12/12");
832 }
834 #[test]
835 fn test_contains() {
836 let range = Range::new(10, 12);
838 assert!(!range.contains(9));
839 assert!(range.contains(10));
840 assert!(range.contains(11));
841 assert!(!range.contains(12));
842 assert!(!range.contains(13));
844 let range = Range::new(9, 6);
845 assert!(!range.contains(9));
846 assert!(range.contains(7));
847 assert!(range.contains(6));
848 }
850 #[test]
851 fn test_overlaps() {
852 fn overlaps(a: (usize, usize), b: (usize, usize)) -> bool {
853 Range::new(a.0, a.1).overlaps(&Range::new(b.0, b.1))
854 }
856 // Two non-zero-width ranges, no overlap.
857 assert!(!overlaps((0, 3), (3, 6)));
858 assert!(!overlaps((0, 3), (6, 3)));
859 assert!(!overlaps((3, 0), (3, 6)));
860 assert!(!overlaps((3, 0), (6, 3)));
861 assert!(!overlaps((3, 6), (0, 3)));
862 assert!(!overlaps((3, 6), (3, 0)));
863 assert!(!overlaps((6, 3), (0, 3)));
864 assert!(!overlaps((6, 3), (3, 0)));
866 // Two non-zero-width ranges, overlap.
867 assert!(overlaps((0, 4), (3, 6)));
868 assert!(overlaps((0, 4), (6, 3)));
869 assert!(overlaps((4, 0), (3, 6)));
870 assert!(overlaps((4, 0), (6, 3)));
871 assert!(overlaps((3, 6), (0, 4)));
872 assert!(overlaps((3, 6), (4, 0)));
873 assert!(overlaps((6, 3), (0, 4)));
874 assert!(overlaps((6, 3), (4, 0)));
876 // Zero-width and non-zero-width range, no overlap.
877 assert!(!overlaps((0, 3), (3, 3)));
878 assert!(!overlaps((3, 0), (3, 3)));
879 assert!(!overlaps((3, 3), (0, 3)));
880 assert!(!overlaps((3, 3), (3, 0)));
882 // Zero-width and non-zero-width range, overlap.
883 assert!(overlaps((1, 4), (1, 1)));
884 assert!(overlaps((4, 1), (1, 1)));
885 assert!(overlaps((1, 1), (1, 4)));
886 assert!(overlaps((1, 1), (4, 1)));
888 assert!(overlaps((1, 4), (3, 3)));
889 assert!(overlaps((4, 1), (3, 3)));
890 assert!(overlaps((3, 3), (1, 4)));
891 assert!(overlaps((3, 3), (4, 1)));
893 // Two zero-width ranges, no overlap.
894 assert!(!overlaps((0, 0), (1, 1)));
895 assert!(!overlaps((1, 1), (0, 0)));
897 // Two zero-width ranges, overlap.
898 assert!(overlaps((1, 1), (1, 1)));
899 }
901 #[test]
902 fn test_grapheme_aligned() {
903 let r = Rope::from_str("\r\nHi\r\n");
904 let s = r.slice(..);
906 // Zero-width.
907 assert_eq!(Range::new(0, 0).grapheme_aligned(s), Range::new(0, 0));
908 assert_eq!(Range::new(1, 1).grapheme_aligned(s), Range::new(0, 0));
909 assert_eq!(Range::new(2, 2).grapheme_aligned(s), Range::new(2, 2));
910 assert_eq!(Range::new(3, 3).grapheme_aligned(s), Range::new(3, 3));
911 assert_eq!(Range::new(4, 4).grapheme_aligned(s), Range::new(4, 4));
912 assert_eq!(Range::new(5, 5).grapheme_aligned(s), Range::new(4, 4));
913 assert_eq!(Range::new(6, 6).grapheme_aligned(s), Range::new(6, 6));
915 // Forward.
916 assert_eq!(Range::new(0, 1).grapheme_aligned(s), Range::new(0, 2));
917 assert_eq!(Range::new(1, 2).grapheme_aligned(s), Range::new(0, 2));
918 assert_eq!(Range::new(2, 3).grapheme_aligned(s), Range::new(2, 3));
919 assert_eq!(Range::new(3, 4).grapheme_aligned(s), Range::new(3, 4));
920 assert_eq!(Range::new(4, 5).grapheme_aligned(s), Range::new(4, 6));
921 assert_eq!(Range::new(5, 6).grapheme_aligned(s), Range::new(4, 6));
923 assert_eq!(Range::new(0, 2).grapheme_aligned(s), Range::new(0, 2));
924 assert_eq!(Range::new(1, 3).grapheme_aligned(s), Range::new(0, 3));
925 assert_eq!(Range::new(2, 4).grapheme_aligned(s), Range::new(2, 4));
926 assert_eq!(Range::new(3, 5).grapheme_aligned(s), Range::new(3, 6));
927 assert_eq!(Range::new(4, 6).grapheme_aligned(s), Range::new(4, 6));
929 // Reverse.
930 assert_eq!(Range::new(1, 0).grapheme_aligned(s), Range::new(2, 0));
931 assert_eq!(Range::new(2, 1).grapheme_aligned(s), Range::new(2, 0));
932 assert_eq!(Range::new(3, 2).grapheme_aligned(s), Range::new(3, 2));
933 assert_eq!(Range::new(4, 3).grapheme_aligned(s), Range::new(4, 3));
934 assert_eq!(Range::new(5, 4).grapheme_aligned(s), Range::new(6, 4));
935 assert_eq!(Range::new(6, 5).grapheme_aligned(s), Range::new(6, 4));
937 assert_eq!(Range::new(2, 0).grapheme_aligned(s), Range::new(2, 0));
938 assert_eq!(Range::new(3, 1).grapheme_aligned(s), Range::new(3, 0));
939 assert_eq!(Range::new(4, 2).grapheme_aligned(s), Range::new(4, 2));
940 assert_eq!(Range::new(5, 3).grapheme_aligned(s), Range::new(6, 3));
941 assert_eq!(Range::new(6, 4).grapheme_aligned(s), Range::new(6, 4));
942 }
944 #[test]
945 fn test_min_width_1() {
946 let r = Rope::from_str("\r\nHi\r\n");
947 let s = r.slice(..);
949 // Zero-width.
950 assert_eq!(Range::new(0, 0).min_width_1(s), Range::new(0, 2));
951 assert_eq!(Range::new(1, 1).min_width_1(s), Range::new(1, 2));
952 assert_eq!(Range::new(2, 2).min_width_1(s), Range::new(2, 3));
953 assert_eq!(Range::new(3, 3).min_width_1(s), Range::new(3, 4));
954 assert_eq!(Range::new(4, 4).min_width_1(s), Range::new(4, 6));
955 assert_eq!(Range::new(5, 5).min_width_1(s), Range::new(5, 6));
956 assert_eq!(Range::new(6, 6).min_width_1(s), Range::new(6, 6));
958 // Forward.
959 assert_eq!(Range::new(0, 1).min_width_1(s), Range::new(0, 1));
960 assert_eq!(Range::new(1, 2).min_width_1(s), Range::new(1, 2));
961 assert_eq!(Range::new(2, 3).min_width_1(s), Range::new(2, 3));
962 assert_eq!(Range::new(3, 4).min_width_1(s), Range::new(3, 4));
963 assert_eq!(Range::new(4, 5).min_width_1(s), Range::new(4, 5));
964 assert_eq!(Range::new(5, 6).min_width_1(s), Range::new(5, 6));
966 // Reverse.
967 assert_eq!(Range::new(1, 0).min_width_1(s), Range::new(1, 0));
968 assert_eq!(Range::new(2, 1).min_width_1(s), Range::new(2, 1));
969 assert_eq!(Range::new(3, 2).min_width_1(s), Range::new(3, 2));
970 assert_eq!(Range::new(4, 3).min_width_1(s), Range::new(4, 3));
971 assert_eq!(Range::new(5, 4).min_width_1(s), Range::new(5, 4));
972 assert_eq!(Range::new(6, 5).min_width_1(s), Range::new(6, 5));
973 }
975 #[test]
976 fn test_select_on_matches() {
977 use crate::regex::{Regex, RegexBuilder};
979 let r = Rope::from_str("Nobody expects the Spanish inquisition");
980 let s = r.slice(..);
982 let selection = Selection::single(0, r.len_chars());
983 assert_eq!(
984 select_on_matches(s, &selection, &Regex::new(r"[A-Z][a-z]*").unwrap()),
985 Some(Selection::new(
986 smallvec![Range::new(0, 6), Range::new(19, 26)],
987 0
988 ))
989 );
991 let r = Rope::from_str("This\nString\n\ncontains multiple\nlines");
992 let s = r.slice(..);
994 let start_of_line = RegexBuilder::new(r"^").multi_line(true).build().unwrap();
995 let end_of_line = RegexBuilder::new(r"$").multi_line(true).build().unwrap();
997 // line without ending
998 assert_eq!(
999 select_on_matches(s, &Selection::single(0, 4), &start_of_line),
1000 Some(Selection::single(0, 0))
1001 );
1002 assert_eq!(
1003 select_on_matches(s, &Selection::single(0, 4), &end_of_line),
1004 None
1005 );
1006 // line with ending
1007 assert_eq!(
1008 select_on_matches(s, &Selection::single(0, 5), &start_of_line),
1009 Some(Selection::single(0, 0))
1010 );
1011 assert_eq!(
1012 select_on_matches(s, &Selection::single(0, 5), &end_of_line),
1013 Some(Selection::single(4, 4))
1014 );
1015 // line with start of next line
1016 assert_eq!(
1017 select_on_matches(s, &Selection::single(0, 6), &start_of_line),
1018 Some(Selection::new(
1019 smallvec![Range::point(0), Range::point(5)],
1021 ))
1022 );
1023 assert_eq!(
1024 select_on_matches(s, &Selection::single(0, 6), &end_of_line),
1025 Some(Selection::single(4, 4))
1026 );
1028 // multiple lines
1029 assert_eq!(
1030 select_on_matches(
1031 s,
1032 &Selection::single(0, s.len_chars()),
1033 &RegexBuilder::new(r"^[a-z ]*$")
1034 .multi_line(true)
1035 .build()
1036 .unwrap()
1037 ),
1038 Some(Selection::new(
1039 smallvec![Range::point(12), Range::new(13, 30), Range::new(31, 36)],
1041 ))
1042 );
1045 #[test]
1046 fn test_line_range() {
1047 let r = Rope::from_str("\r\nHi\r\nthere!");
1048 let s = r.slice(..);
1050 // Zero-width ranges.
1051 assert_eq!(Range::new(0, 0).line_range(s), (0, 0));
1052 assert_eq!(Range::new(1, 1).line_range(s), (0, 0));
1053 assert_eq!(Range::new(2, 2).line_range(s), (1, 1));
1054 assert_eq!(Range::new(3, 3).line_range(s), (1, 1));
1056 // Forward ranges.
1057 assert_eq!(Range::new(0, 1).line_range(s), (0, 0));
1058 assert_eq!(Range::new(0, 2).line_range(s), (0, 0));
1059 assert_eq!(Range::new(0, 3).line_range(s), (0, 1));
1060 assert_eq!(Range::new(1, 2).line_range(s), (0, 0));
1061 assert_eq!(Range::new(2, 3).line_range(s), (1, 1));
1062 assert_eq!(Range::new(3, 8).line_range(s), (1, 2));
1063 assert_eq!(Range::new(0, 12).line_range(s), (0, 2));
1065 // Reverse ranges.
1066 assert_eq!(Range::new(1, 0).line_range(s), (0, 0));
1067 assert_eq!(Range::new(2, 0).line_range(s), (0, 0));
1068 assert_eq!(Range::new(3, 0).line_range(s), (0, 1));
1069 assert_eq!(Range::new(2, 1).line_range(s), (0, 0));
1070 assert_eq!(Range::new(3, 2).line_range(s), (1, 1));
1071 assert_eq!(Range::new(8, 3).line_range(s), (1, 2));
1072 assert_eq!(Range::new(12, 0).line_range(s), (0, 2));
1075 #[test]
1076 fn test_cursor() {
1077 let r = Rope::from_str("\r\nHi\r\nthere!");
1078 let s = r.slice(..);
1080 // Zero-width ranges.
1081 assert_eq!(Range::new(0, 0).cursor(s), 0);
1082 assert_eq!(Range::new(2, 2).cursor(s), 2);
1083 assert_eq!(Range::new(3, 3).cursor(s), 3);
1085 // Forward ranges.
1086 assert_eq!(Range::new(0, 2).cursor(s), 0);
1087 assert_eq!(Range::new(0, 3).cursor(s), 2);
1088 assert_eq!(Range::new(3, 6).cursor(s), 4);
1090 // Reverse ranges.
1091 assert_eq!(Range::new(2, 0).cursor(s), 0);
1092 assert_eq!(Range::new(6, 2).cursor(s), 2);
1093 assert_eq!(Range::new(6, 3).cursor(s), 3);
1096 #[test]
1097 fn test_put_cursor() {
1098 let r = Rope::from_str("\r\nHi\r\nthere!");
1099 let s = r.slice(..);
1101 // Zero-width ranges.
1102 assert_eq!(Range::new(0, 0).put_cursor(s, 0, true), Range::new(0, 2));
1103 assert_eq!(Range::new(0, 0).put_cursor(s, 2, true), Range::new(0, 3));
1104 assert_eq!(Range::new(2, 3).put_cursor(s, 4, true), Range::new(2, 6));
1105 assert_eq!(Range::new(2, 8).put_cursor(s, 4, true), Range::new(2, 6));
1106 assert_eq!(Range::new(8, 8).put_cursor(s, 4, true), Range::new(9, 4));
1108 // Forward ranges.
1109 assert_eq!(Range::new(3, 6).put_cursor(s, 0, true), Range::new(4, 0));
1110 assert_eq!(Range::new(3, 6).put_cursor(s, 2, true), Range::new(4, 2));
1111 assert_eq!(Range::new(3, 6).put_cursor(s, 3, true), Range::new(3, 4));
1112 assert_eq!(Range::new(3, 6).put_cursor(s, 4, true), Range::new(3, 6));
1113 assert_eq!(Range::new(3, 6).put_cursor(s, 6, true), Range::new(3, 7));
1114 assert_eq!(Range::new(3, 6).put_cursor(s, 8, true), Range::new(3, 9));
1116 // Reverse ranges.
1117 assert_eq!(Range::new(6, 3).put_cursor(s, 0, true), Range::new(6, 0));
1118 assert_eq!(Range::new(6, 3).put_cursor(s, 2, true), Range::new(6, 2));
1119 assert_eq!(Range::new(6, 3).put_cursor(s, 3, true), Range::new(6, 3));
1120 assert_eq!(Range::new(6, 3).put_cursor(s, 4, true), Range::new(6, 4));
1121 assert_eq!(Range::new(6, 3).put_cursor(s, 6, true), Range::new(4, 7));
1122 assert_eq!(Range::new(6, 3).put_cursor(s, 8, true), Range::new(4, 9));
1125 #[test]
1126 fn test_split_on_matches() {
1127 use crate::regex::Regex;
1129 let text = Rope::from(" abcd efg wrs xyz 123 456");
1131 let selection = Selection::new(smallvec![Range::new(0, 9), Range::new(11, 20),], 0);
1133 let result = split_on_matches(text.slice(..), &selection, &Regex::new(r"\s+").unwrap());
1135 assert_eq!(
1136 result.ranges(),
1137 &[
1138 // TODO: rather than this behavior, maybe we want it
1139 // to be based on which side is the anchor?
1140 //
1141 // We get a leading zero-width range when there's
1142 // a leading match because ranges are inclusive on
1143 // the left. Imagine, for example, if the entire
1144 // selection range were matched: you'd still want
1145 // at least one range to remain after the split.
1146 Range::new(0, 0),
1147 Range::new(1, 5),
1148 Range::new(6, 9),
1149 Range::new(11, 13),
1150 Range::new(16, 19),
1151 // In contrast to the comment above, there is no
1152 // _trailing_ zero-width range despite the trailing
1153 // match, because ranges are exclusive on the right.
1155 );
1157 assert_eq!(
1158 result.fragments(text.slice(..)).collect::<Vec<_>>(),
1159 &["", "abcd", "efg", "rs", "xyz"]
1160 );
1163 #[test]
1164 fn test_merge_consecutive_ranges() {
1165 let selection = Selection::new(
1166 smallvec![
1167 Range::new(0, 1),
1168 Range::new(1, 10),
1169 Range::new(15, 20),
1170 Range::new(25, 26),
1171 Range::new(26, 30)
1172 ],
1173 4,
1174 );
1176 let result = selection.merge_consecutive_ranges();
1178 assert_eq!(
1179 result.ranges(),
1180 &[Range::new(0, 10), Range::new(15, 20), Range::new(25, 30)]
1181 );
1182 assert_eq!(result.primary_index, 2);
1184 let selection = Selection::new(smallvec![Range::new(0, 1)], 0);
1185 let result = selection.merge_consecutive_ranges();
1187 assert_eq!(result.ranges(), &[Range::new(0, 1)]);
1188 assert_eq!(result.primary_index, 0);
1190 let selection = Selection::new(
1191 smallvec![
1192 Range::new(0, 1),
1193 Range::new(1, 5),
1194 Range::new(5, 8),
1195 Range::new(8, 10),
1196 Range::new(10, 15),
1197 Range::new(18, 25)
1198 ],
1199 3,
1200 );
1202 let result = selection.merge_consecutive_ranges();
1204 assert_eq!(result.ranges(), &[Range::new(0, 15), Range::new(18, 25)]);
1205 assert_eq!(result.primary_index, 0);
1208 #[test]
1209 fn test_selection_contains() {
1210 fn contains(a: Vec<(usize, usize)>, b: Vec<(usize, usize)>) -> bool {
1211 let sela = Selection::new(a.iter().map(|a| Range::new(a.0, a.1)).collect(), 0);
1212 let selb = Selection::new(b.iter().map(|b| Range::new(b.0, b.1)).collect(), 0);
1213 sela.contains(&selb)
1216 // exact match
1217 assert!(contains(vec!((1, 1)), vec!((1, 1))));
1219 // larger set contains smaller
1220 assert!(contains(vec!((1, 1), (2, 2), (3, 3)), vec!((2, 2))));
1222 // multiple matches
1223 assert!(contains(vec!((1, 1), (2, 2)), vec!((1, 1), (2, 2))));
1225 // smaller set can't contain bigger
1226 assert!(!contains(vec!((1, 1)), vec!((1, 1), (2, 2))));
1228 assert!(contains(
1229 vec!((1, 1), (2, 4), (5, 6), (7, 9), (10, 13)),
1230 vec!((3, 4), (7, 9))
1231 ));
1232 assert!(!contains(vec!((1, 1), (5, 6)), vec!((1, 6))));