xi_core_lib/
selection.rs

1// Copyright 2017 The xi-editor Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Data structures representing (multiple) selections and cursors.
16
17use std::cmp::{max, min};
18use std::fmt;
19use std::ops::Deref;
20
21use crate::annotations::{AnnotationRange, AnnotationSlice, AnnotationType, ToAnnotation};
22use crate::index_set::remove_n_at;
23use crate::view::View;
24use xi_rope::{Interval, Rope, RopeDelta, Transformer};
25
26/// A type representing horizontal measurements. This is currently in units
27/// that are not very well defined except that ASCII characters count as
28/// 1 each. It will change.
29pub type HorizPos = usize;
30
31/// Indicates if an edit should try to drift inside or outside nearby selections. If the selection
32/// is zero width, that is, it is a caret, this value will be ignored, the equivalent of the
33/// `Default` value.
34#[derive(Copy, Clone)]
35pub enum InsertDrift {
36    /// Indicates this edit should happen within any (non-caret) selections if possible.
37    Inside,
38    /// Indicates this edit should happen outside any selections if possible.
39    Outside,
40    /// Indicates to do whatever the `after` bool says to do
41    Default,
42}
43
44/// A set of zero or more selection regions, representing a selection state.
45#[derive(Default, Debug, Clone)]
46pub struct Selection {
47    // An invariant: regions[i].max() <= regions[i+1].min()
48    // and < if either is_caret()
49    regions: Vec<SelRegion>,
50}
51
52impl Selection {
53    /// Creates a new empty selection.
54    pub fn new() -> Selection {
55        Selection::default()
56    }
57
58    /// Creates a selection with a single region.
59    pub fn new_simple(region: SelRegion) -> Selection {
60        Selection { regions: vec![region] }
61    }
62
63    /// Clear the selection.
64    pub fn clear(&mut self) {
65        self.regions.clear();
66    }
67
68    /// Collapse all selections into a single caret.
69    pub fn collapse(&mut self) {
70        self.regions.truncate(1);
71        self.regions[0].start = self.regions[0].end;
72    }
73
74    // The smallest index so that offset > region.max() for all preceding
75    // regions.
76    pub fn search(&self, offset: usize) -> usize {
77        if self.regions.is_empty() || offset > self.regions.last().unwrap().max() {
78            return self.regions.len();
79        }
80        match self.regions.binary_search_by(|r| r.max().cmp(&offset)) {
81            Ok(ix) => ix,
82            Err(ix) => ix,
83        }
84    }
85
86    /// Add a region to the selection. This method implements merging logic.
87    ///
88    /// Two non-caret regions merge if their interiors intersect; merely
89    /// touching at the edges does not cause a merge. A caret merges with
90    /// a non-caret if it is in the interior or on either edge. Two carets
91    /// merge if they are the same offset.
92    ///
93    /// Performance note: should be O(1) if the new region strictly comes
94    /// after all the others in the selection, otherwise O(n).
95    pub fn add_region(&mut self, region: SelRegion) {
96        let mut ix = self.search(region.min());
97        if ix == self.regions.len() {
98            self.regions.push(region);
99            return;
100        }
101        let mut region = region;
102        let mut end_ix = ix;
103        if self.regions[ix].min() <= region.min() {
104            if self.regions[ix].should_merge(region) {
105                region = self.regions[ix].merge_with(region);
106            } else {
107                ix += 1;
108            }
109            end_ix += 1;
110        }
111        while end_ix < self.regions.len() && region.should_merge(self.regions[end_ix]) {
112            region = region.merge_with(self.regions[end_ix]);
113            end_ix += 1;
114        }
115        if ix == end_ix {
116            self.regions.insert(ix, region);
117        } else {
118            self.regions[ix] = region;
119            remove_n_at(&mut self.regions, ix + 1, end_ix - ix - 1);
120        }
121    }
122
123    /// Gets a slice of regions that intersect the given range. Regions that
124    /// merely touch the range at the edges are also included, so it is the
125    /// caller's responsibility to further trim them, in particular to only
126    /// display one caret in the upstream/downstream cases.
127    ///
128    /// Performance note: O(log n).
129    pub fn regions_in_range(&self, start: usize, end: usize) -> &[SelRegion] {
130        let first = self.search(start);
131        let mut last = self.search(end);
132        if last < self.regions.len() && self.regions[last].min() <= end {
133            last += 1;
134        }
135        &self.regions[first..last]
136    }
137
138    /// Deletes all the regions that intersect or (if delete_adjacent = true) touch the given range.
139    pub fn delete_range(&mut self, start: usize, end: usize, delete_adjacent: bool) {
140        let mut first = self.search(start);
141        let mut last = self.search(end);
142        if first >= self.regions.len() {
143            return;
144        }
145        if !delete_adjacent && self.regions[first].max() == start {
146            first += 1;
147        }
148        if last < self.regions.len()
149            && ((delete_adjacent && self.regions[last].min() <= end)
150                || (!delete_adjacent && self.regions[last].min() < end))
151        {
152            last += 1;
153        }
154        remove_n_at(&mut self.regions, first, last - first);
155    }
156
157    /// Add a region to the selection. This method does not merge regions and does not allow
158    /// ambiguous regions (regions that overlap).
159    ///
160    /// On ambiguous regions, the region with the lower start position wins. That is, in such a
161    /// case, the new region is either not added at all, because there is an ambiguous region with
162    /// a lower start position, or existing regions that intersect with the new region but do
163    /// not start before the new region, are deleted.
164    pub fn add_range_distinct(&mut self, region: SelRegion) -> (usize, usize) {
165        let mut ix = self.search(region.min());
166
167        if ix < self.regions.len() && self.regions[ix].max() == region.min() {
168            ix += 1;
169        }
170
171        if ix < self.regions.len() {
172            // in case of ambiguous regions the region closer to the left wins
173            let occ = &self.regions[ix];
174            let is_eq = occ.min() == region.min() && occ.max() == region.max();
175            let is_intersect_before = region.min() >= occ.min() && occ.max() > region.min();
176            if is_eq || is_intersect_before {
177                return (occ.min(), occ.max());
178            }
179        }
180
181        // delete ambiguous regions to the right
182        let mut last = self.search(region.max());
183        if last < self.regions.len() && self.regions[last].min() < region.max() {
184            last += 1;
185        }
186        remove_n_at(&mut self.regions, ix, last - ix);
187
188        if ix == self.regions.len() {
189            self.regions.push(region);
190        } else {
191            self.regions.insert(ix, region);
192        }
193
194        (self.regions[ix].min(), self.regions[ix].max())
195    }
196
197    /// Computes a new selection based on applying a delta to the old selection.
198    ///
199    /// When new text is inserted at a caret, the new caret can be either before
200    /// or after the inserted text, depending on the `after` parameter.
201    ///
202    /// Whether or not the preceding selections are restored depends on the keep_selections
203    /// value (only set to true on transpose).
204    pub fn apply_delta(&self, delta: &RopeDelta, after: bool, drift: InsertDrift) -> Selection {
205        let mut result = Selection::new();
206        let mut transformer = Transformer::new(delta);
207        for region in self.iter() {
208            let is_caret = region.start == region.end;
209            let is_region_forward = region.start < region.end;
210
211            let (start_after, end_after) = match (drift, is_caret) {
212                (InsertDrift::Inside, false) => (!is_region_forward, is_region_forward),
213                (InsertDrift::Outside, false) => (is_region_forward, !is_region_forward),
214                _ => (after, after),
215            };
216
217            let new_region = SelRegion::new(
218                transformer.transform(region.start, start_after),
219                transformer.transform(region.end, end_after),
220            )
221            .with_affinity(region.affinity);
222            result.add_region(new_region);
223        }
224        result
225    }
226}
227
228/// Implementing the `ToAnnotation` trait allows to convert selections to annotations.
229impl ToAnnotation for Selection {
230    fn get_annotations(&self, interval: Interval, view: &View, text: &Rope) -> AnnotationSlice {
231        let regions = self.regions_in_range(interval.start(), interval.end());
232        let ranges = regions
233            .iter()
234            .map(|region| {
235                let (start_line, start_col) = view.offset_to_line_col(text, region.min());
236                let (end_line, end_col) = view.offset_to_line_col(text, region.max());
237
238                AnnotationRange { start_line, start_col, end_line, end_col }
239            })
240            .collect::<Vec<AnnotationRange>>();
241        AnnotationSlice::new(AnnotationType::Selection, ranges, None)
242    }
243}
244
245/// Implementing the Deref trait allows callers to easily test `is_empty`, iterate
246/// through all ranges, etc.
247impl Deref for Selection {
248    type Target = [SelRegion];
249
250    fn deref(&self) -> &[SelRegion] {
251        &self.regions
252    }
253}
254
255/// The "affinity" of a cursor which is sitting exactly on a line break.
256///
257/// We say "cursor" here rather than "caret" because (depending on presentation)
258/// the front-end may draw a cursor even when the region is not a caret.
259#[derive(Clone, Copy, PartialEq, Eq, Debug)]
260pub enum Affinity {
261    /// The cursor should be displayed downstream of the line break. For
262    /// example, if the buffer is "abcd", and the cursor is on a line break
263    /// after "ab", it should be displayed on the second line before "cd".
264    Downstream,
265    /// The cursor should be displayed upstream of the line break. For
266    /// example, if the buffer is "abcd", and the cursor is on a line break
267    /// after "ab", it should be displayed on the previous line after "ab".
268    Upstream,
269}
270
271impl Default for Affinity {
272    fn default() -> Affinity {
273        Affinity::Downstream
274    }
275}
276
277/// A type representing a single contiguous region of a selection. We use the
278/// term "caret" (sometimes also "cursor", more loosely) to refer to a selection
279/// region with an empty interior. A "non-caret region" is one with a non-empty
280/// interior (i.e. `start != end`).
281#[derive(Clone, Copy, PartialEq, Eq, Debug)]
282pub struct SelRegion {
283    /// The inactive edge of a selection, as a byte offset. When
284    /// equal to end, the selection range acts as a caret.
285    pub start: usize,
286
287    /// The active edge of a selection, as a byte offset.
288    pub end: usize,
289
290    /// A saved horizontal position (used primarily for line up/down movement).
291    pub horiz: Option<HorizPos>,
292
293    /// The affinity of the cursor.
294    pub affinity: Affinity,
295}
296
297impl SelRegion {
298    /// Returns a new region.
299    pub fn new(start: usize, end: usize) -> Self {
300        Self { start, end, horiz: None, affinity: Affinity::default() }
301    }
302
303    /// Returns a new caret region (`start == end`).
304    pub fn caret(pos: usize) -> Self {
305        Self { start: pos, end: pos, horiz: None, affinity: Affinity::default() }
306    }
307
308    /// Returns a region with the given horizontal position.
309    pub fn with_horiz(self, horiz: Option<HorizPos>) -> Self {
310        Self { horiz, ..self }
311    }
312
313    /// Returns a region with the given affinity.
314    pub fn with_affinity(self, affinity: Affinity) -> Self {
315        Self { affinity, ..self }
316    }
317
318    /// Gets the earliest offset within the region, ie the minimum of both edges.
319    pub fn min(self) -> usize {
320        min(self.start, self.end)
321    }
322
323    /// Gets the latest offset within the region, ie the maximum of both edges.
324    pub fn max(self) -> usize {
325        max(self.start, self.end)
326    }
327
328    /// Determines whether the region is a caret (ie has an empty interior).
329    pub fn is_caret(self) -> bool {
330        self.start == self.end
331    }
332
333    /// Determines whether the region's affinity is upstream.
334    pub fn is_upstream(self) -> bool {
335        self.affinity == Affinity::Upstream
336    }
337
338    // Indicate whether this region should merge with the next.
339    // Assumption: regions are sorted (self.min() <= other.min())
340    fn should_merge(self, other: SelRegion) -> bool {
341        other.min() < self.max()
342            || ((self.is_caret() || other.is_caret()) && other.min() == self.max())
343    }
344
345    fn merge_with(self, other: SelRegion) -> SelRegion {
346        let is_forward = self.end > self.start || other.end > other.start;
347        let new_min = min(self.min(), other.min());
348        let new_max = max(self.max(), other.max());
349        let (start, end) = if is_forward { (new_min, new_max) } else { (new_max, new_min) };
350        // Could try to preserve horiz/affinity from one of the
351        // sources, but very likely not worth it.
352        SelRegion::new(start, end)
353    }
354}
355
356impl<'a> From<&'a SelRegion> for Interval {
357    fn from(src: &'a SelRegion) -> Interval {
358        Interval::new(src.min(), src.max())
359    }
360}
361
362impl From<Interval> for SelRegion {
363    fn from(src: Interval) -> SelRegion {
364        SelRegion::new(src.start, src.end)
365    }
366}
367
368impl From<SelRegion> for Selection {
369    fn from(region: SelRegion) -> Self {
370        Self::new_simple(region)
371    }
372}
373
374impl fmt::Display for Selection {
375    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
376        if self.regions.len() == 1 {
377            self.regions[0].fmt(f)?;
378        } else {
379            write!(f, "[ {}", &self.regions[0])?;
380            for region in &self.regions[1..] {
381                write!(f, ", {}", region)?;
382            }
383            write!(f, " ]")?;
384        }
385        Ok(())
386    }
387}
388
389impl fmt::Display for SelRegion {
390    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391        if self.is_caret() {
392            write!(f, "{}|", self.start)?;
393        } else if self.start < self.end {
394            write!(f, "{}..{}|", self.start, self.end)?;
395        } else {
396            write!(f, "|{}..{}", self.end, self.start)?;
397        }
398        Ok(())
399    }
400}
401
402#[cfg(test)]
403mod tests {
404    use super::{InsertDrift, SelRegion, Selection};
405    use std::ops::Deref;
406    use xi_rope::{DeltaBuilder, Interval};
407
408    fn r(start: usize, end: usize) -> SelRegion {
409        SelRegion::new(start, end)
410    }
411
412    #[test]
413    fn empty() {
414        let s = Selection::new();
415        assert!(s.is_empty());
416        assert_eq!(s.deref(), &[]);
417    }
418
419    #[test]
420    fn simple_region() {
421        let s = Selection::new_simple(r(3, 5));
422        assert!(!s.is_empty());
423        assert_eq!(s.deref(), &[r(3, 5)]);
424    }
425
426    #[test]
427    fn from_selregion() {
428        let s: Selection = r(3, 5).into();
429        assert!(!s.is_empty());
430        assert_eq!(s.deref(), &[r(3, 5)]);
431    }
432
433    #[test]
434    fn delete_range() {
435        let mut s = Selection::new_simple(r(3, 5));
436        s.delete_range(1, 2, true);
437        assert_eq!(s.deref(), &[r(3, 5)]);
438        s.delete_range(1, 3, false);
439        assert_eq!(s.deref(), &[r(3, 5)]);
440        s.delete_range(1, 3, true);
441        assert_eq!(s.deref(), &[]);
442
443        let mut s = Selection::new_simple(r(3, 5));
444        s.delete_range(5, 6, false);
445        assert_eq!(s.deref(), &[r(3, 5)]);
446        s.delete_range(5, 6, true);
447        assert_eq!(s.deref(), &[]);
448
449        let mut s = Selection::new_simple(r(3, 5));
450        s.delete_range(2, 4, false);
451        assert_eq!(s.deref(), &[]);
452        assert_eq!(s.deref(), &[]);
453
454        let mut s = Selection::new();
455        s.add_region(r(3, 5));
456        s.add_region(r(7, 8));
457        s.delete_range(2, 10, false);
458        assert_eq!(s.deref(), &[]);
459    }
460
461    #[test]
462    fn simple_regions_in_range() {
463        let s = Selection::new_simple(r(3, 5));
464        assert_eq!(s.regions_in_range(0, 1), &[]);
465        assert_eq!(s.regions_in_range(0, 2), &[]);
466        assert_eq!(s.regions_in_range(0, 3), &[r(3, 5)]);
467        assert_eq!(s.regions_in_range(0, 4), &[r(3, 5)]);
468        assert_eq!(s.regions_in_range(5, 6), &[r(3, 5)]);
469        assert_eq!(s.regions_in_range(6, 7), &[]);
470    }
471
472    #[test]
473    fn caret_regions_in_range() {
474        let s = Selection::new_simple(r(4, 4));
475        assert_eq!(s.regions_in_range(0, 1), &[]);
476        assert_eq!(s.regions_in_range(0, 2), &[]);
477        assert_eq!(s.regions_in_range(0, 3), &[]);
478        assert_eq!(s.regions_in_range(0, 4), &[r(4, 4)]);
479        assert_eq!(s.regions_in_range(4, 4), &[r(4, 4)]);
480        assert_eq!(s.regions_in_range(4, 5), &[r(4, 4)]);
481        assert_eq!(s.regions_in_range(5, 6), &[]);
482    }
483
484    #[test]
485    fn merge_regions() {
486        let mut s = Selection::new();
487        s.add_region(r(3, 5));
488        assert_eq!(s.deref(), &[r(3, 5)]);
489        s.add_region(r(7, 9));
490        assert_eq!(s.deref(), &[r(3, 5), r(7, 9)]);
491        s.add_region(r(1, 3));
492        assert_eq!(s.deref(), &[r(1, 3), r(3, 5), r(7, 9)]);
493        s.add_region(r(4, 6));
494        assert_eq!(s.deref(), &[r(1, 3), r(3, 6), r(7, 9)]);
495        s.add_region(r(2, 8));
496        assert_eq!(s.deref(), &[r(1, 9)]);
497
498        s.clear();
499        assert_eq!(s.deref(), &[]);
500        s.add_region(r(1, 4));
501        s.add_region(r(4, 5));
502        s.add_region(r(5, 6));
503        s.add_region(r(6, 9));
504        assert_eq!(s.deref(), &[r(1, 4), r(4, 5), r(5, 6), r(6, 9)]);
505        s.add_region(r(2, 8));
506        assert_eq!(s.deref(), &[r(1, 9)]);
507    }
508
509    #[test]
510    fn merge_carets() {
511        let mut s = Selection::new();
512        s.add_region(r(1, 1));
513        assert_eq!(s.deref(), &[r(1, 1)]);
514        s.add_region(r(3, 3));
515        assert_eq!(s.deref(), &[r(1, 1), r(3, 3)]);
516        s.add_region(r(2, 2));
517        assert_eq!(s.deref(), &[r(1, 1), r(2, 2), r(3, 3)]);
518        s.add_region(r(1, 1));
519        assert_eq!(s.deref(), &[r(1, 1), r(2, 2), r(3, 3)]);
520    }
521
522    #[test]
523    fn merge_region_caret() {
524        let mut s = Selection::new();
525        s.add_region(r(3, 5));
526        assert_eq!(s.deref(), &[r(3, 5)]);
527        s.add_region(r(3, 3));
528        assert_eq!(s.deref(), &[r(3, 5)]);
529        s.add_region(r(4, 4));
530        assert_eq!(s.deref(), &[r(3, 5)]);
531        s.add_region(r(5, 5));
532        assert_eq!(s.deref(), &[r(3, 5)]);
533        s.add_region(r(6, 6));
534        assert_eq!(s.deref(), &[r(3, 5), r(6, 6)]);
535    }
536
537    #[test]
538    fn merge_reverse() {
539        let mut s = Selection::new();
540        s.add_region(r(5, 3));
541        assert_eq!(s.deref(), &[r(5, 3)]);
542        s.add_region(r(9, 7));
543        assert_eq!(s.deref(), &[r(5, 3), r(9, 7)]);
544        s.add_region(r(3, 1));
545        assert_eq!(s.deref(), &[r(3, 1), r(5, 3), r(9, 7)]);
546        s.add_region(r(6, 4));
547        assert_eq!(s.deref(), &[r(3, 1), r(6, 3), r(9, 7)]);
548        s.add_region(r(8, 2));
549        assert_eq!(s.deref(), &[r(9, 1)]);
550    }
551
552    #[test]
553    fn apply_delta_outside_drift() {
554        let mut s = Selection::new();
555        s.add_region(r(0, 4));
556        s.add_region(r(4, 8));
557        assert_eq!(s.deref(), &[r(0, 4), r(4, 8)]);
558
559        // simulate outside edit between two adjacent selections
560        // like "texthere!" -> "text here!"
561        // the space should be outside the selections
562        let mut builder = DeltaBuilder::new("texthere!".len());
563        builder.replace(Interval::new(4, 4), " ".into());
564        let s2 = s.apply_delta(&builder.build(), true, InsertDrift::Outside);
565
566        assert_eq!(s2.deref(), &[r(0, 4), r(5, 9)]);
567    }
568
569    #[test]
570    fn apply_delta_inside_drift() {
571        let mut s = Selection::new();
572        s.add_region(r(1, 2));
573        assert_eq!(s.deref(), &[r(1, 2)]);
574
575        // simulate inside edit on either end of selection
576        // like "abc" -> "abbbc"
577        // if b was selected at beginning, inside edit should cause all bs to be selected after
578        let mut builder = DeltaBuilder::new("abc".len());
579        builder.replace(Interval::new(1, 1), "b".into());
580        builder.replace(Interval::new(2, 2), "b".into());
581        let s2 = s.apply_delta(&builder.build(), true, InsertDrift::Inside);
582
583        assert_eq!(s2.deref(), &[r(1, 4)]);
584    }
585
586    #[test]
587    fn apply_delta_drift_ignored_for_carets() {
588        let mut s = Selection::new();
589        s.add_region(r(1, 1));
590        assert_eq!(s.deref(), &[r(1, 1)]);
591
592        let mut builder = DeltaBuilder::new("ab".len());
593        builder.replace(Interval::new(1, 1), "b".into());
594        let s2 = s.apply_delta(&builder.build(), true, InsertDrift::Inside);
595        assert_eq!(s2.deref(), &[r(2, 2)]);
596
597        let mut builder = DeltaBuilder::new("ab".len());
598        builder.replace(Interval::new(1, 1), "b".into());
599        let s3 = s.apply_delta(&builder.build(), false, InsertDrift::Inside);
600        assert_eq!(s3.deref(), &[r(1, 1)]);
601    }
602
603    #[test]
604    fn display() {
605        let mut s = Selection::new();
606        s.add_region(r(1, 1));
607        assert_eq!(s.to_string(), "1|");
608        s.add_region(r(3, 5));
609        s.add_region(r(8, 6));
610        assert_eq!(s.to_string(), "[ 1|, 3..5|, |6..8 ]");
611    }
612}