floem_editor_core/
selection.rs

1use std::cmp::{max, min, Ordering};
2
3use lapce_xi_rope::{RopeDelta, Transformer};
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7use crate::cursor::{ColPosition, CursorAffinity};
8
9/// Indicate whether a delta should be applied inside, outside non-caret selection or
10/// after a caret selection (see [`Selection::apply_delta`].
11#[derive(Copy, Clone)]
12pub enum InsertDrift {
13    /// Indicates this edit should happen within any (non-caret) selections if possible.
14    Inside,
15    /// Indicates this edit should happen outside any selections if possible.
16    Outside,
17    /// Indicates to do whatever the `after` bool says to do
18    Default,
19}
20
21#[derive(Clone, Copy, PartialEq, Debug)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23pub struct SelRegion {
24    /// Region start offset
25    pub start: usize,
26    /// Region end offset
27    pub end: usize,
28    /// The placement preference for rendering the cursor at wrapped lines
29    pub affinity: CursorAffinity,
30    /// Horizontal rules for multiple selection
31    pub horiz: Option<ColPosition>,
32}
33
34/// A selection holding one or more [`SelRegion`].
35/// Regions are kept in order from the leftmost selection to the rightmost selection.
36#[derive(Clone, PartialEq, Debug)]
37#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
38pub struct Selection {
39    regions: Vec<SelRegion>,
40    last_inserted: usize,
41}
42
43impl AsRef<Selection> for Selection {
44    fn as_ref(&self) -> &Selection {
45        self
46    }
47}
48
49impl SelRegion {
50    /// Creates new [`SelRegion`] from `start` and `end` offset.
51    pub fn new(
52        start: usize,
53        end: usize,
54        affinity: CursorAffinity,
55        horiz: Option<ColPosition>,
56    ) -> SelRegion {
57        SelRegion {
58            start,
59            end,
60            affinity,
61            horiz,
62        }
63    }
64
65    /// Creates a caret [`SelRegion`],
66    /// i.e. `start` and `end` position are both set to `offset` value.
67    pub fn caret(offset: usize, affinity: CursorAffinity) -> SelRegion {
68        SelRegion {
69            start: offset,
70            end: offset,
71            affinity,
72            horiz: None,
73        }
74    }
75
76    /// Creates a new [`SelRegion`] with meaningless visual properties for passing to buffer edits.
77    pub(crate) fn imaginary_region(start: usize, end: usize) -> SelRegion {
78        SelRegion {
79            start,
80            end,
81            affinity: CursorAffinity::Backward,
82            horiz: None,
83        }
84    }
85
86    /// Creates a caret [`SelRegion`] with meaningless visual properties for passing to buffer edits.
87    /// i.e. `start` and `end` position are both set to `offset` value.
88    pub(crate) fn imaginary_caret(offset: usize) -> SelRegion {
89        SelRegion::imaginary_region(offset, offset)
90    }
91
92    /// Return the minimum value between region's start and end position
93    ///
94    /// **Example:**
95    ///
96    /// ```rust
97    /// # use floem_editor_core::selection::SelRegion;
98    /// # use floem_editor_core::cursor::CursorAffinity;
99    /// let  region = SelRegion::new(1, 10, CursorAffinity::Backward, None);
100    /// assert_eq!(region.min(), region.start);
101    /// let  region = SelRegion::new(42, 1, CursorAffinity::Backward, None);
102    /// assert_eq!(region.min(), region.end);
103    /// ```
104    pub fn min(self) -> usize {
105        min(self.start, self.end)
106    }
107
108    /// Return the maximum value between region's start and end position.
109    ///
110    /// **Example:**
111    ///
112    /// ```rust
113    /// # use floem_editor_core::selection::SelRegion;
114    /// # use floem_editor_core::cursor::CursorAffinity;
115    /// let  region = SelRegion::new(1, 10, CursorAffinity::Backward, None);
116    /// assert_eq!(region.max(), region.end);
117    /// let  region = SelRegion::new(42, 1, CursorAffinity::Backward, None);
118    /// assert_eq!(region.max(), region.start);
119    /// ```
120    pub fn max(self) -> usize {
121        max(self.start, self.end)
122    }
123
124    /// A [`SelRegion`] is considered to be a caret when its start and end position are equal.
125    ///
126    /// **Example:**
127    ///
128    /// ```rust
129    /// # use floem_editor_core::selection::SelRegion;
130    /// # use floem_editor_core::cursor::CursorAffinity;
131    /// let  region = SelRegion::new(1, 1, CursorAffinity::Backward, None);
132    /// assert!(region.is_caret());
133    /// ```
134    pub fn is_caret(self) -> bool {
135        self.start == self.end
136    }
137
138    /// Merge two [`SelRegion`] into a single one.
139    ///
140    /// **Example:**
141    ///
142    /// ```rust
143    /// # use floem_editor_core::selection::SelRegion;
144    /// # use floem_editor_core::cursor::CursorAffinity;
145    /// let  region = SelRegion::new(1, 2, CursorAffinity::Backward, None);
146    /// let  other = SelRegion::new(3, 4, CursorAffinity::Backward, None);
147    /// assert_eq!(region.merge_with(other), SelRegion::new(1, 4, CursorAffinity::Backward, None));
148    /// ```
149    pub fn merge_with(self, other: SelRegion) -> SelRegion {
150        let is_forward = self.end >= self.start;
151        let new_min = min(self.min(), other.min());
152        let new_max = max(self.max(), other.max());
153        let (start, end) = if is_forward {
154            (new_min, new_max)
155        } else {
156            (new_max, new_min)
157        };
158        SelRegion::new(start, end, self.affinity, None)
159    }
160
161    fn should_merge(self, other: SelRegion) -> bool {
162        other.min() < self.max()
163            || ((self.is_caret() || other.is_caret()) && other.min() == self.max())
164    }
165
166    fn contains(&self, offset: usize) -> bool {
167        self.min() <= offset && offset <= self.max()
168    }
169}
170
171impl Selection {
172    /// Creates a new empty [`Selection`]
173    pub fn new() -> Selection {
174        Selection {
175            regions: Vec::new(),
176            last_inserted: 0,
177        }
178    }
179
180    /// Creates a caret [`Selection`], i.e. a selection with a single caret [`SelRegion`]
181    pub fn caret(offset: usize, affinity: CursorAffinity) -> Selection {
182        Selection {
183            regions: vec![SelRegion::caret(offset, affinity)],
184            last_inserted: 0,
185        }
186    }
187
188    /// Creates a region [`Selection`], i.e. a selection with a single [`SelRegion`]
189    /// from `start` to `end` position
190    pub fn region(start: usize, end: usize, affinity: CursorAffinity) -> Self {
191        Self::sel_region(SelRegion {
192            start,
193            end,
194            affinity,
195            horiz: None,
196        })
197    }
198
199    /// Creates a [`Selection`], with a single [`SelRegion`] equal to `region`.
200    pub fn sel_region(region: SelRegion) -> Self {
201        Self {
202            regions: vec![region],
203            last_inserted: 0,
204        }
205    }
206
207    /// Creates a [`Selection`] with meaningless visual properties for passing to buffer edits.
208    pub(crate) fn imaginary_region(start: usize, end: usize) -> Self {
209        Self::sel_region(SelRegion::imaginary_region(start, end))
210    }
211
212    /// Creates a [`Selection`] with meaningless visual properties for passing to buffer edits.
213    pub(crate) fn imaginary_caret(offset: usize) -> Self {
214        Self::sel_region(SelRegion::imaginary_caret(offset))
215    }
216
217    /// Returns whether this [`Selection`], contains the given `offset` position or not.
218    ///
219    /// **Example:**
220    ///
221    /// ```rust
222    /// # use floem_editor_core::selection::Selection;
223    /// # use floem_editor_core::cursor::CursorAffinity;
224    /// let  selection = Selection::region(0, 2, CursorAffinity::Backward);
225    /// assert!(selection.contains(0));
226    /// assert!(selection.contains(1));
227    /// assert!(selection.contains(2));
228    /// assert!(!selection.contains(3));
229    /// ```
230    pub fn contains(&self, offset: usize) -> bool {
231        for region in self.regions.iter() {
232            if region.contains(offset) {
233                return true;
234            }
235        }
236        false
237    }
238
239    /// Returns this selection regions
240    pub fn regions(&self) -> &[SelRegion] {
241        &self.regions
242    }
243
244    /// Returns a mutable reference to this selection regions
245    pub fn regions_mut(&mut self) -> &mut [SelRegion] {
246        &mut self.regions
247    }
248
249    /// Returns a copy of [`self`] with all regions converted to caret region at their respective
250    /// [`SelRegion::min`] offset.
251    ///
252    /// **Examples:**
253    ///
254    /// ```rust
255    /// # use floem_editor_core::selection::{Selection, SelRegion};
256    /// # use floem_editor_core::cursor::CursorAffinity;
257    /// let mut selection = Selection::new();
258    /// selection.add_region(SelRegion::new(1, 3, CursorAffinity::Backward, None));
259    /// selection.add_region(SelRegion::new(6, 12, CursorAffinity::Backward, None));
260    /// selection.add_region(SelRegion::new(24, 48, CursorAffinity::Backward, None));
261    ///
262    /// assert_eq!(selection.min().regions(), vec![
263    ///     SelRegion::caret(1, CursorAffinity::Backward),
264    ///     SelRegion::caret(6, CursorAffinity::Backward),
265    ///     SelRegion::caret(24, CursorAffinity::Backward)
266    /// ]);
267    pub fn min(&self) -> Selection {
268        let mut selection = Self::new();
269        for region in &self.regions {
270            let new_region =
271                SelRegion::new(region.min(), region.min(), CursorAffinity::Backward, None);
272            selection.add_region(new_region);
273        }
274        selection
275    }
276
277    /// Get the leftmost [`SelRegion`] in this selection if present.
278    pub fn first(&self) -> Option<&SelRegion> {
279        self.regions.first()
280    }
281
282    /// Get the rightmost [`SelRegion`] in this selection if present.
283    pub fn last(&self) -> Option<&SelRegion> {
284        self.regions.get(self.len() - 1)
285    }
286
287    /// Get the last inserted [`SelRegion`] in this selection if present.
288    pub fn last_inserted(&self) -> Option<&SelRegion> {
289        self.regions.get(self.last_inserted)
290    }
291
292    /// Get a mutable reference to the last inserted [`SelRegion`] in this selection if present.
293    pub fn last_inserted_mut(&mut self) -> Option<&mut SelRegion> {
294        self.regions.get_mut(self.last_inserted)
295    }
296
297    /// The number of [`SelRegion`] in this selection.
298    pub fn len(&self) -> usize {
299        self.regions.len()
300    }
301
302    /// A [`Selection`] is considered to be a caret if it contains
303    /// only caret [`SelRegion`] (see [`SelRegion::is_caret`])
304    pub fn is_caret(&self) -> bool {
305        self.regions.iter().all(|region| region.is_caret())
306    }
307
308    pub fn current_caret(&self) -> Option<usize> {
309        if self.regions.len() == 1 {
310            if self.regions[0].is_caret() {
311                Some(self.regions[0].start)
312            } else {
313                None
314            }
315        } else {
316            None
317        }
318    }
319
320    /// Returns `true` if `self` has zero [`SelRegion`]
321    pub fn is_empty(&self) -> bool {
322        self.len() == 0
323    }
324
325    /// Returns the minimal offset across all region of this selection.
326    ///
327    /// This function panics if the selection is empty.
328    ///
329    /// **Example:**
330    ///
331    /// ```rust
332    /// # use floem_editor_core::selection::{Selection, SelRegion};
333    /// # use floem_editor_core::cursor::CursorAffinity;
334    /// let mut selection = Selection::new();
335    /// selection.add_region(SelRegion::caret(4, CursorAffinity::Backward));
336    /// selection.add_region(SelRegion::new(0, 12, CursorAffinity::Backward, None));
337    /// selection.add_region(SelRegion::new(24, 48, CursorAffinity::Backward, None));
338    /// assert_eq!(selection.min_offset(), 0);
339    /// ```
340    pub fn min_offset(&self) -> usize {
341        let mut offset = self.regions()[0].min();
342        for region in &self.regions {
343            offset = offset.min(region.min());
344        }
345        offset
346    }
347
348    /// Returns the maximal offset across all region of this selection.
349    ///
350    /// This function panics if the selection is empty.
351    ///
352    /// **Example:**
353    ///
354    /// ```rust
355    /// # use floem_editor_core::selection::{Selection, SelRegion};
356    /// # use floem_editor_core::cursor::CursorAffinity;
357    /// let mut selection = Selection::new();
358    /// selection.add_region(SelRegion::caret(4, CursorAffinity::Backward));
359    /// selection.add_region(SelRegion::new(0, 12, CursorAffinity::Backward, None));
360    /// selection.add_region(SelRegion::new(24, 48, CursorAffinity::Backward, None));
361    /// assert_eq!(selection.max_offset(), 48);
362    /// ```
363    pub fn max_offset(&self) -> usize {
364        let mut offset = self.regions()[0].max();
365        for region in &self.regions {
366            offset = offset.max(region.max());
367        }
368        offset
369    }
370
371    /// Returns regions in [`self`] overlapping or fully enclosed in the provided
372    /// `start` to `end` range.
373    ///
374    /// **Example:**
375    ///
376    /// ```rust
377    /// # use floem_editor_core::selection::{Selection, SelRegion};
378    /// # use floem_editor_core::cursor::CursorAffinity;
379    /// let mut selection = Selection::new();
380    /// selection.add_region(SelRegion::new(0, 3, CursorAffinity::Backward, None));
381    /// selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
382    /// selection.add_region(SelRegion::new(7, 8, CursorAffinity::Backward, None));
383    /// selection.add_region(SelRegion::new(9, 11, CursorAffinity::Backward, None));
384    /// let regions = selection.regions_in_range(5, 10);
385    /// assert_eq!(regions, vec![
386    ///     SelRegion::new(3, 6, CursorAffinity::Backward, None),
387    ///     SelRegion::new(7, 8, CursorAffinity::Backward, None),
388    ///     SelRegion::new(9, 11, CursorAffinity::Backward, None)
389    /// ]);
390    /// ```
391    pub fn regions_in_range(&self, start: usize, end: usize) -> &[SelRegion] {
392        let first = self.search(start);
393        let mut last = self.search(end);
394        if last < self.regions.len() && self.regions[last].min() <= end {
395            last += 1;
396        }
397        &self.regions[first..last]
398    }
399
400    /// Returns regions in [`self`] starting between `start` to `end` range.
401    ///
402    /// **Example:**
403    ///
404    /// ```rust
405    /// # use floem_editor_core::selection::{Selection, SelRegion};
406    /// # use floem_editor_core::cursor::CursorAffinity;
407    /// let mut selection = Selection::new();
408    /// selection.add_region(SelRegion::new(0, 3, CursorAffinity::Backward, None));
409    /// selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
410    /// selection.add_region(SelRegion::new(7, 8, CursorAffinity::Backward, None));
411    /// selection.add_region(SelRegion::new(9, 11, CursorAffinity::Backward, None));
412    /// let regions = selection.full_regions_in_range(5, 10);
413    /// assert_eq!(regions, vec![
414    ///     SelRegion::new(7, 8, CursorAffinity::Backward, None),
415    ///     SelRegion::new(9, 11, CursorAffinity::Backward, None)
416    /// ]);
417    /// ```
418    pub fn full_regions_in_range(&self, start: usize, end: usize) -> &[SelRegion] {
419        let first = self.search_min(start);
420        let mut last = self.search_min(end);
421        if last < self.regions.len() && self.regions[last].min() <= end {
422            last += 1;
423        }
424        &self.regions[first..last]
425    }
426
427    /// Deletes regions in [`self`] overlapping or enclosing in `start` to `end` range.
428    ///
429    /// **Example:**
430    ///
431    /// ```rust
432    /// # use floem_editor_core::selection::{Selection, SelRegion};
433    /// # use floem_editor_core::cursor::CursorAffinity;
434    /// let mut selection = Selection::new();
435    /// selection.add_region(SelRegion::new(0, 3, CursorAffinity::Backward, None));
436    /// selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
437    /// selection.add_region(SelRegion::new(7, 8, CursorAffinity::Backward, None));
438    /// selection.add_region(SelRegion::new(9, 11, CursorAffinity::Backward, None));
439    /// selection.delete_range(5, 10);
440    /// assert_eq!(selection.regions(), vec![SelRegion::new(0, 3, CursorAffinity::Backward, None)]);
441    /// ```
442    pub fn delete_range(&mut self, start: usize, end: usize) {
443        let mut first = self.search(start);
444        let mut last = self.search(end);
445        if first >= self.regions.len() {
446            return;
447        }
448        if self.regions[first].max() == start {
449            first += 1;
450        }
451        if last < self.regions.len() && self.regions[last].min() < end {
452            last += 1;
453        }
454        if first > last {
455            return;
456        }
457        let count = last - first;
458
459        remove_n_at(&mut self.regions, first, last - first);
460
461        if self.last_inserted >= last {
462            self.last_inserted -= count;
463        } else if self.last_inserted >= first {
464            self.last_inserted = first.saturating_sub(1);
465        }
466    }
467
468    /// Add a regions to [`self`]. Note that if provided region overlaps
469    /// one of the selection regions they will be merged in a single region.
470    ///
471    /// **Example:**
472    ///
473    /// ```rust
474    /// # use floem_editor_core::selection::{Selection, SelRegion};
475    /// # use floem_editor_core::cursor::CursorAffinity;
476    /// let mut selection = Selection::new();
477    /// // Overlapping
478    /// selection.add_region(SelRegion::new(0, 4, CursorAffinity::Backward, None));
479    /// selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
480    /// assert_eq!(selection.regions(), vec![SelRegion::new(0, 6, CursorAffinity::Backward, None)]);
481    /// // Non-overlapping
482    /// let mut selection = Selection::new();
483    /// selection.add_region(SelRegion::new(0, 3, CursorAffinity::Backward, None));
484    /// selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
485    /// assert_eq!(selection.regions(), vec![
486    ///     SelRegion::new(0, 3, CursorAffinity::Backward, None),
487    ///     SelRegion::new(3, 6, CursorAffinity::Backward, None)
488    /// ]);
489    /// ```
490    pub fn add_region(&mut self, region: SelRegion) {
491        let mut ix = self.search(region.min());
492        if ix == self.regions.len() {
493            self.regions.push(region);
494            self.last_inserted = self.regions.len() - 1;
495            return;
496        }
497        let mut region = region;
498        let mut end_ix = ix;
499        if self.regions[ix].min() <= region.min() {
500            if self.regions[ix].should_merge(region) {
501                region = self.regions[ix].merge_with(region);
502            } else {
503                ix += 1;
504            }
505            end_ix += 1;
506        }
507        while end_ix < self.regions.len() && region.should_merge(self.regions[end_ix]) {
508            region = self.regions[end_ix].merge_with(region);
509            end_ix += 1;
510        }
511        if ix == end_ix {
512            self.regions.insert(ix, region);
513        } else {
514            self.regions[ix] = region;
515            remove_n_at(&mut self.regions, ix + 1, end_ix - ix - 1);
516        }
517        self.last_inserted = ix;
518    }
519
520    /// Add a region to the selection. This method does not merge regions and does not allow
521    /// ambiguous regions (regions that overlap).
522    ///
523    /// On ambiguous regions, the region with the lower start position wins. That is, in such a
524    /// case, the new region is either not added at all, because there is an ambiguous region with
525    /// a lower start position, or existing regions that intersect with the new region but do
526    /// not start before the new region, are deleted.
527    pub fn add_range_distinct(&mut self, region: SelRegion) -> (usize, usize) {
528        let mut ix = self.search(region.min());
529
530        if ix < self.regions.len() && self.regions[ix].max() == region.min() {
531            ix += 1;
532        }
533
534        if ix < self.regions.len() {
535            // in case of ambiguous regions the region closer to the left wins
536            let occ = &self.regions[ix];
537            let is_eq = occ.min() == region.min() && occ.max() == region.max();
538            let is_intersect_before = region.min() >= occ.min() && occ.max() > region.min();
539            if is_eq || is_intersect_before {
540                return (occ.min(), occ.max());
541            }
542        }
543
544        // delete ambiguous regions to the right
545        let mut last = self.search(region.max());
546        if last < self.regions.len() && self.regions[last].min() < region.max() {
547            last += 1;
548        }
549        remove_n_at(&mut self.regions, ix, last - ix);
550
551        if ix == self.regions.len() {
552            self.regions.push(region);
553        } else {
554            self.regions.insert(ix, region);
555        }
556
557        (self.regions[ix].min(), self.regions[ix].max())
558    }
559
560    /// Apply [`lapce_xi_rope::RopeDelta`] to this selection.
561    /// Typically used to apply an edit to a buffer and update its selections
562    /// **Parameters*:*
563    /// - `delta`[`lapce_xi_rope::RopeDelta`]
564    /// - `after` parameter indicate if the delta should be applied before or after the selection
565    /// - `drift` see [`InsertDrift`]
566    pub fn apply_delta(&self, delta: &RopeDelta, after: bool, drift: InsertDrift) -> Selection {
567        let mut result = Selection::new();
568        let mut transformer = Transformer::new(delta);
569        for region in self.regions() {
570            let is_region_forward = region.start < region.end;
571
572            let (start_after, end_after) = match (drift, region.is_caret()) {
573                (InsertDrift::Inside, false) => (!is_region_forward, is_region_forward),
574                (InsertDrift::Outside, false) => (is_region_forward, !is_region_forward),
575                _ => (after, after),
576            };
577
578            let new_region = SelRegion::new(
579                transformer.transform(region.start, start_after),
580                transformer.transform(region.end, end_after),
581                region.affinity,
582                None,
583            );
584            result.add_region(new_region);
585        }
586        result
587    }
588
589    /// Returns cursor position, which corresponds to last inserted region `end` offset,
590    pub fn get_cursor_offset(&self) -> usize {
591        if self.is_empty() {
592            return 0;
593        }
594        self.regions[self.last_inserted].end
595    }
596
597    /// Returns cursor affinity, which corresponds to last inserted region `affinity`,
598    pub fn get_cursor_affinity(&self) -> CursorAffinity {
599        if self.is_empty() {
600            return CursorAffinity::Backward;
601        }
602        self.regions[self.last_inserted].affinity
603    }
604
605    /// Replaces last inserted [`SelRegion`] of this selection with the provided one.
606    pub fn replace_last_inserted_region(&mut self, region: SelRegion) {
607        if self.is_empty() {
608            self.add_region(region);
609            return;
610        }
611
612        self.regions.remove(self.last_inserted);
613        self.add_region(region);
614    }
615
616    fn search(&self, offset: usize) -> usize {
617        if self.regions.is_empty() || offset > self.regions.last().unwrap().max() {
618            return self.regions.len();
619        }
620        match self.regions.binary_search_by(|r| r.max().cmp(&offset)) {
621            Ok(ix) => ix,
622            Err(ix) => ix,
623        }
624    }
625
626    fn search_min(&self, offset: usize) -> usize {
627        if self.regions.is_empty() || offset > self.regions.last().unwrap().max() {
628            return self.regions.len();
629        }
630        match self
631            .regions
632            .binary_search_by(|r| r.min().cmp(&(offset + 1)))
633        {
634            Ok(ix) => ix,
635            Err(ix) => ix,
636        }
637    }
638}
639
640impl Default for Selection {
641    fn default() -> Self {
642        Self::new()
643    }
644}
645
646fn remove_n_at<T>(v: &mut Vec<T>, index: usize, n: usize) {
647    match n.cmp(&1) {
648        Ordering::Equal => {
649            v.remove(index);
650        }
651        Ordering::Greater => {
652            v.drain(index..index + n);
653        }
654        _ => (),
655    };
656}
657
658#[cfg(test)]
659mod test {
660    use crate::{
661        buffer::Buffer,
662        cursor::CursorAffinity,
663        editor::EditType,
664        selection::{InsertDrift, SelRegion, Selection},
665    };
666
667    #[test]
668    fn should_return_selection_region_min() {
669        let region = SelRegion::new(1, 10, CursorAffinity::Backward, None);
670        assert_eq!(region.min(), region.start);
671
672        let region = SelRegion::new(42, 1, CursorAffinity::Backward, None);
673        assert_eq!(region.min(), region.end);
674    }
675
676    #[test]
677    fn should_return_selection_region_max() {
678        let region = SelRegion::new(1, 10, CursorAffinity::Backward, None);
679        assert_eq!(region.max(), region.end);
680
681        let region = SelRegion::new(42, 1, CursorAffinity::Backward, None);
682        assert_eq!(region.max(), region.start);
683    }
684
685    #[test]
686    fn is_caret_should_return_true() {
687        let region = SelRegion::new(1, 10, CursorAffinity::Backward, None);
688        assert!(!region.is_caret());
689    }
690
691    #[test]
692    fn is_caret_should_return_false() {
693        let region = SelRegion::new(1, 1, CursorAffinity::Backward, None);
694        assert!(region.is_caret());
695    }
696
697    #[test]
698    fn should_merge_regions() {
699        let region = SelRegion::new(1, 2, CursorAffinity::Backward, None);
700        let other = SelRegion::new(3, 4, CursorAffinity::Backward, None);
701        assert_eq!(
702            region.merge_with(other),
703            SelRegion::new(1, 4, CursorAffinity::Backward, None)
704        );
705
706        let region = SelRegion::new(2, 1, CursorAffinity::Backward, None);
707        let other = SelRegion::new(4, 3, CursorAffinity::Backward, None);
708        assert_eq!(
709            region.merge_with(other),
710            SelRegion::new(4, 1, CursorAffinity::Backward, None)
711        );
712
713        let region = SelRegion::new(1, 1, CursorAffinity::Backward, None);
714        let other = SelRegion::new(6, 6, CursorAffinity::Backward, None);
715        assert_eq!(
716            region.merge_with(other),
717            SelRegion::new(1, 6, CursorAffinity::Backward, None)
718        );
719    }
720
721    #[test]
722    fn selection_should_be_caret() {
723        let mut selection = Selection::new();
724        selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
725        selection.add_region(SelRegion::caret(6, CursorAffinity::Backward));
726        assert!(selection.is_caret());
727    }
728
729    #[test]
730    fn selection_should_not_be_caret() {
731        let mut selection = Selection::new();
732        selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
733        selection.add_region(SelRegion::new(4, 6, CursorAffinity::Backward, None));
734        assert!(!selection.is_caret());
735    }
736
737    #[test]
738    fn should_return_min_selection() {
739        let mut selection = Selection::new();
740        selection.add_region(SelRegion::new(1, 3, CursorAffinity::Backward, None));
741        selection.add_region(SelRegion::new(4, 6, CursorAffinity::Backward, None));
742        assert_eq!(
743            selection.min().regions,
744            vec![
745                SelRegion::caret(1, CursorAffinity::Backward),
746                SelRegion::caret(4, CursorAffinity::Backward)
747            ]
748        );
749    }
750
751    #[test]
752    fn selection_should_contains_region() {
753        let selection = Selection::region(0, 2, CursorAffinity::Backward);
754        assert!(selection.contains(0));
755        assert!(selection.contains(1));
756        assert!(selection.contains(2));
757        assert!(!selection.contains(3));
758    }
759
760    #[test]
761    fn should_return_last_inserted_region() {
762        let mut selection = Selection::region(5, 6, CursorAffinity::Backward);
763        selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
764        assert_eq!(
765            selection.last_inserted(),
766            Some(&SelRegion::caret(1, CursorAffinity::Backward))
767        );
768    }
769
770    #[test]
771    fn should_return_last_region() {
772        let mut selection = Selection::region(5, 6, CursorAffinity::Backward);
773        selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
774        assert_eq!(
775            selection.last(),
776            Some(&SelRegion::new(5, 6, CursorAffinity::Backward, None))
777        );
778    }
779
780    #[test]
781    fn should_return_first_region() {
782        let mut selection = Selection::region(5, 6, CursorAffinity::Backward);
783        selection.add_region(SelRegion::caret(1, CursorAffinity::Backward));
784        assert_eq!(
785            selection.first(),
786            Some(&SelRegion::caret(1, CursorAffinity::Backward))
787        );
788    }
789
790    #[test]
791    fn should_return_regions_in_range() {
792        let mut selection = Selection::new();
793        selection.add_region(SelRegion::new(0, 3, CursorAffinity::Backward, None));
794        selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
795        selection.add_region(SelRegion::new(7, 8, CursorAffinity::Backward, None));
796        selection.add_region(SelRegion::new(9, 11, CursorAffinity::Backward, None));
797
798        let regions = selection.regions_in_range(5, 10);
799
800        assert_eq!(
801            regions,
802            vec![
803                SelRegion::new(3, 6, CursorAffinity::Backward, None),
804                SelRegion::new(7, 8, CursorAffinity::Backward, None),
805                SelRegion::new(9, 11, CursorAffinity::Backward, None),
806            ]
807        );
808    }
809
810    #[test]
811    fn should_return_regions_in_full_range() {
812        let mut selection = Selection::new();
813        selection.add_region(SelRegion::new(0, 3, CursorAffinity::Backward, None));
814        selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
815        selection.add_region(SelRegion::new(7, 8, CursorAffinity::Backward, None));
816        selection.add_region(SelRegion::new(9, 11, CursorAffinity::Backward, None));
817
818        let regions = selection.full_regions_in_range(5, 10);
819
820        assert_eq!(
821            regions,
822            vec![
823                SelRegion::new(7, 8, CursorAffinity::Backward, None),
824                SelRegion::new(9, 11, CursorAffinity::Backward, None),
825            ]
826        );
827    }
828
829    #[test]
830    fn should_delete_regions() {
831        let mut selection = Selection::new();
832        selection.add_region(SelRegion::new(0, 3, CursorAffinity::Backward, None));
833        selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
834        selection.add_region(SelRegion::new(7, 8, CursorAffinity::Backward, None));
835        selection.add_region(SelRegion::new(9, 11, CursorAffinity::Backward, None));
836        selection.delete_range(5, 10);
837        assert_eq!(
838            selection.regions(),
839            vec![SelRegion::new(0, 3, CursorAffinity::Backward, None)]
840        );
841    }
842
843    #[test]
844    fn should_add_regions() {
845        let mut selection = Selection::new();
846        selection.add_region(SelRegion::new(0, 3, CursorAffinity::Backward, None));
847        selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
848        assert_eq!(
849            selection.regions(),
850            vec![
851                SelRegion::new(0, 3, CursorAffinity::Backward, None),
852                SelRegion::new(3, 6, CursorAffinity::Backward, None),
853            ]
854        );
855    }
856
857    #[test]
858    fn should_add_and_merge_regions() {
859        let mut selection = Selection::new();
860
861        selection.add_region(SelRegion::new(0, 4, CursorAffinity::Backward, None));
862        selection.add_region(SelRegion::new(3, 6, CursorAffinity::Backward, None));
863        assert_eq!(
864            selection.regions(),
865            vec![SelRegion::new(0, 6, CursorAffinity::Backward, None)]
866        );
867    }
868
869    #[test]
870    fn should_apply_delta_after_insertion() {
871        let selection = Selection::caret(0, CursorAffinity::Backward);
872
873        let (_, mock_delta, _) = {
874            let mut buffer = Buffer::new("");
875            buffer.edit(&[(selection.clone(), "Hello")], EditType::InsertChars)
876        };
877
878        assert_eq!(
879            selection.apply_delta(&mock_delta, true, InsertDrift::Inside),
880            Selection::caret(5, CursorAffinity::Backward)
881        );
882    }
883}