duat_core/
ranges.rs

1//! The [`Ranges`] struct
2//!
3//! This struct uses the same principle as the `ShiftList` in the
4//! [`crate::text`] module. That is, it has an internal `shift` value
5//! to displace all [`Range`]s ahead of the `from` index within. This
6//! enables efficient localized insertion, like we have with
7//! [`GapBuffer`]s, while maintaining the ability to binary search
8//! over the list, getting both efficient insertion as well as
9//! traversal.
10use std::ops::Range;
11
12use gapbuf::{GapBuffer, gap_buffer};
13
14use crate::utils::merging_range_by_guess_and_lazy_shift;
15
16/// A list of non intersecting exclusive [`Range<usize>`]s
17///
18/// The primary purpose of this struct is to serve [parser]s by
19/// telling Duat which ranges need to be updated. This lets Duat
20/// minimize as much as possible the amount of work done to update
21/// the [`Text`] when it changes in a [`Buffer`].
22///
23/// [`Text`]: crate::text::Text
24/// [`Buffer`]: crate::buffer::Buffer
25/// [parser]: crate::buffer::BufferTracker
26#[derive(Clone, Default, Debug)]
27pub struct Ranges {
28    list: GapBuffer<Range<i32>>,
29    from: usize,
30    shift: i32,
31    min_len: usize,
32}
33
34impl Ranges {
35    /// Return a new instance of [`Ranges`] with an initial
36    /// [`Range`]
37    pub fn new(range: Range<usize>) -> Self {
38        Self {
39            list: gap_buffer![range.start as i32..range.end as i32],
40            ..Self::empty()
41        }
42    }
43
44    /// Returns a new empty [`Ranges`]
45    pub fn empty() -> Self {
46        Self {
47            list: GapBuffer::new(),
48            from: 0,
49            shift: 0,
50            min_len: 1,
51        }
52    }
53
54    /// Sets a minimum length to keep [`Range`]s
55    pub fn set_min_len(&mut self, min: usize) {
56        self.min_len = min.max(1);
57
58        let mut i = 0;
59        self.list.retain(|range| {
60            i += 1;
61
62            if range.len() < self.min_len {
63                if i - 1 < self.from {
64                    self.from -= 1;
65                }
66                false
67            } else {
68                true
69            }
70        });
71    }
72
73    /// Adds a range to the list of [`Range<usize>`]s
74    ///
75    /// This range will be merged in with the others on the list,
76    /// so it may bridge gaps between ranges or for longer
77    /// ranges within, without allowing for the existance
78    /// of intersecting ranges.
79    ///
80    /// This function will return `true` if the `Ranges` were changed
81    /// by this addition.
82    #[track_caller]
83    pub fn add(&mut self, new: Range<usize>) -> bool {
84        assert_range(&new);
85        if new.len() < self.min_len {
86            return false;
87        }
88
89        let new = new.start as i32..new.end as i32;
90
91        let m_range = merging_range_by_guess_and_lazy_shift(
92            (&self.list, self.list.len()),
93            (self.from, [new.start, new.end]),
94            (self.from, self.shift, 0, std::ops::Add::add),
95            (|r| r.start, |r| r.end),
96        );
97
98        // The ranges in this region have not been callibrated yet.
99        if self.from <= m_range.end {
100            for range in self.list.range_mut(self.from..m_range.end).iter_mut() {
101                range.start += self.shift;
102                range.end += self.shift;
103            }
104        }
105
106        let changed_ranges = if m_range.len() == 1 {
107            let range = self.list[m_range.start].clone();
108            !(range.start <= new.start && range.end >= new.end)
109        } else {
110            true
111        };
112
113        let added_range = {
114            let slice = self.list.range(m_range.clone());
115            let mut iter = slice.iter();
116
117            let first = iter.next().cloned().unwrap_or(new.clone());
118            let last = iter.next_back().cloned().unwrap_or(first.clone());
119
120            first.start.min(new.start)..last.end.max(new.end)
121        };
122
123        let new_from = self.from.saturating_sub(m_range.len()).max(m_range.start) + 1;
124        if new_from < self.list.len() + 1 - m_range.len() {
125            self.from = new_from;
126        } else {
127            self.from = 0;
128            self.shift = 0;
129        }
130
131        self.list.splice(m_range.clone(), [added_range]);
132
133        changed_ranges
134    }
135
136    /// Adds many [`Range`]s to be merged with the existing ones
137    ///
138    /// This function will return `true` if the `Ranges` were changed
139    /// by this addition.
140    pub fn extend(&mut self, ranges: impl IntoIterator<Item = Range<usize>>) -> bool {
141        let mut has_changed = false;
142        for range in ranges {
143            has_changed |= self.add(range);
144        }
145        has_changed
146    }
147
148    /// Removes a [`Range`] from the list, returning an
149    /// [`Iterator`] over the [`Range`]s that were taken
150    #[track_caller]
151    pub fn remove_on(&mut self, within: Range<usize>) -> impl Iterator<Item = Range<usize>> {
152        assert_range(&within);
153        let within = within.start as i32..within.end as i32;
154
155        let m_range = merging_range_by_guess_and_lazy_shift(
156            (&self.list, self.list.len()),
157            (self.from, [within.start, within.end]),
158            (self.from, self.shift, 0, std::ops::Add::add),
159            (|r| r.start, |r| r.end),
160        );
161
162        // The ranges in this region have not been callibrated yet.
163        if self.from <= m_range.end {
164            for range in self.list.range_mut(self.from..m_range.end).iter_mut() {
165                range.start += self.shift;
166                range.end += self.shift;
167            }
168        }
169
170        let split_off = {
171            let slice = self.list.range(m_range.clone());
172
173            let first = slice.iter().next().cloned();
174            let last = slice.iter().next_back().cloned().or(first.clone());
175
176            let split_start = first.filter(|f| f.start < within.start).map(|first| {
177                self.list[m_range.start].start = within.start;
178                first.start..within.start
179            });
180            let split_end = last.filter(|l| l.end > within.end).map(|last| {
181                self.list[m_range.end - 1].end = within.end;
182                within.end..last.end
183            });
184
185            let min_len = |range: &Range<i32>| range.len() >= self.min_len;
186
187            [split_start.filter(min_len), split_end.filter(min_len)]
188        };
189
190        let added = split_off.iter().flatten().count();
191
192        let new_from = self.from.saturating_sub(m_range.len()).max(m_range.start) + added;
193        if new_from < self.list.len() + added - m_range.len() {
194            self.from = new_from;
195        } else {
196            self.from = 0;
197            self.shift = 0;
198        }
199
200        self.list
201            .splice(m_range, split_off.into_iter().flatten())
202            .filter_map(|r| (!r.is_empty()).then_some(r.start as usize..r.end as usize))
203    }
204
205    /// Removes all [`Range`]s that intersect with the given one,
206    /// returning an [`Iterator`] over them
207    #[track_caller]
208    pub fn remove_intersecting(
209        &mut self,
210        within: Range<usize>,
211    ) -> impl Iterator<Item = Range<usize>> {
212        assert_range(&within);
213        let within = within.start as i32..within.end as i32;
214
215        let m_range = merging_range_by_guess_and_lazy_shift(
216            (&self.list, self.list.len()),
217            (self.from, [within.start, within.end]),
218            (self.from, self.shift, 0, std::ops::Add::add),
219            (|r| r.start, |r| r.end),
220        );
221
222        // The ranges in this region have not been callibrated yet.
223        if self.from <= m_range.end {
224            for range in self.list.range_mut(self.from..m_range.end).iter_mut() {
225                range.start += self.shift;
226                range.end += self.shift;
227            }
228        }
229
230        let new_from = self.from.saturating_sub(m_range.len()).max(m_range.start);
231        if new_from < self.list.len() - m_range.len() {
232            self.from = new_from;
233        } else {
234            self.from = 0;
235            self.shift = 0;
236        }
237
238        self.list
239            .drain(m_range)
240            .map(|range| range.start as usize..range.end as usize)
241    }
242
243    /// Applies the [`add`] function to another [`Ranges`]s
244    ///
245    /// [`add`]: Self::add
246    #[track_caller]
247    pub fn merge(&mut self, other: Self) -> bool {
248        let mut has_changed = false;
249        for range in other.list {
250            has_changed |= self.add(range.start as usize..range.end as usize);
251        }
252        has_changed
253    }
254
255    /// Shifts the [`Range`]s within by a [`Change`]
256    ///
257    /// If the `diff` is negative (i.e. a part of the ranges were
258    /// removed), then ranges will be removed ahead of `from`
259    /// accordingly.
260    ///
261    /// [`Change`]: crate::buffer::Change
262    pub fn shift_by(&mut self, from: usize, shift: i32) {
263        let from = from as i32;
264
265        // The range of changes that will be drained
266        let m_range = merging_range_by_guess_and_lazy_shift(
267            (&self.list, self.list.len()),
268            (self.from, [from, from - shift.min(0)]),
269            (self.from, self.shift, 0, std::ops::Add::add),
270            (|r| r.start, |r| r.end),
271        );
272
273        // If self.from < m_range.end, I need to shift the changes between
274        // the two, so that they match the shifting of the changes
275        // before self.from.
276        if self.from < m_range.end && self.shift != 0 {
277            for range in self.list.range_mut(self.from..m_range.end).iter_mut() {
278                range.start += self.shift;
279                range.end += self.shift;
280            }
281        // If self.from > m_range.end, then I shift the ranges
282        // inbetween by -self.shift, in order to keep the ranges
283        // after m_range.start equally unshifted by self.shift +
284        // shift.
285        } else if self.from > m_range.end && self.shift != 0 {
286            for range in &mut self.list.range_mut(m_range.end..self.from) {
287                range.start -= self.shift;
288                range.end -= self.shift;
289            }
290        }
291
292        let range_left = {
293            let slice = self.list.range(m_range.clone());
294            let mut iter = slice.iter();
295
296            iter.next().and_then(|first| {
297                let last = iter.next_back().unwrap_or(first);
298                let range = first.start.min(from)..(last.end + shift).max(from);
299
300                (range.len() >= self.min_len).then_some(range)
301            })
302        };
303
304        let new_from = m_range.start + range_left.is_some() as usize;
305
306        self.list.splice(m_range.clone(), range_left);
307
308        if new_from < self.list.len() {
309            self.from = new_from;
310            self.shift += shift;
311        } else {
312            self.from = 0;
313            self.shift = 0;
314        }
315    }
316
317    /// Returns the number of [`Range<usize>`]s
318    pub fn len(&self) -> usize {
319        self.list.len()
320    }
321
322    /// Returns `true` if there are no [`Range<usize>`]s
323    pub fn is_empty(&self) -> bool {
324        self.list.is_empty()
325    }
326
327    /// An [`Iterator`] over the [`Range`]s in this list
328    pub fn iter(&self) -> impl Iterator<Item = Range<usize>> {
329        self.list.iter().enumerate().map(move |(i, range)| {
330            if i >= self.from {
331                (range.start + self.shift) as usize..(range.end + self.shift) as usize
332            } else {
333                range.start as usize..range.end as usize
334            }
335        })
336    }
337
338    /// The same as [`Ranges::remove_on`], but without removing, just
339    /// iterating over the relevant ranges
340    ///
341    /// This method will trim the iterated [`Range`]s to the bounds of
342    /// `within`. If you want a non trimmed version of this method,
343    /// check out [`iter_intersecting`].
344    ///
345    /// [`iter_intersecting`]: Ranges::iter_intersecting
346    #[track_caller]
347    pub fn iter_over(&self, within: Range<usize>) -> impl Iterator<Item = Range<usize>> {
348        self.iter_intersecting(within.clone())
349            .map(move |range| range.start.max(within.start)..range.end.min(within.end))
350            .filter(|range| !range.is_empty())
351    }
352
353    /// Iterates over all [`Range`]s that intersect with `within`
354    ///
355    /// If you want to automatically trim those ranges to the bounds
356    /// of `within`, check out [`iter_over`]. If you want to remove
357    /// the ranges that intersect with the given one, see,
358    /// [`Ranges::remove_intersecting`]
359    ///
360    /// [`iter_over`]: Self::iter_over
361    #[track_caller]
362    pub fn iter_intersecting(&self, within: Range<usize>) -> impl Iterator<Item = Range<usize>> {
363        assert_range(&within);
364        let m_range = merging_range_by_guess_and_lazy_shift(
365            (&self.list, self.list.len()),
366            (self.from, [within.start as i32, within.end as i32]),
367            (self.from, self.shift, 0, std::ops::Add::add),
368            (|r| r.start, |r| r.end),
369        );
370
371        let (s0, s1) = self.list.range(m_range.clone()).as_slices();
372        s0.iter().chain(s1).enumerate().map(move |(i, range)| {
373            let range = if i + m_range.start >= self.from {
374                range.start + self.shift..range.end + self.shift
375            } else {
376                range.clone()
377            };
378
379            range.start as usize..range.end as usize
380        })
381    }
382
383    /// Wether any [`Range`] in this `Ranges` intersects with the
384    /// given `range`
385    #[track_caller]
386    pub fn intersects_with(&self, range: Range<usize>) -> bool {
387        assert_range(&range);
388
389        let m_range = merging_range_by_guess_and_lazy_shift(
390            (&self.list, self.list.len()),
391            (self.from, [range.start as i32, range.end as i32]),
392            (self.from, self.shift, 0, std::ops::Add::add),
393            (|r| r.start, |r| r.end),
394        );
395
396        !m_range.is_empty()
397    }
398}
399
400impl IntoIterator for Ranges {
401    type IntoIter = IntoIter;
402    type Item = Range<usize>;
403
404    fn into_iter(self) -> Self::IntoIter {
405        IntoIter {
406            iter: self.list.into_iter().enumerate(),
407            from: self.from,
408            shift: self.shift,
409        }
410    }
411}
412
413pub struct IntoIter {
414    iter: std::iter::Enumerate<gapbuf::IntoIter<Range<i32>>>,
415    from: usize,
416    shift: i32,
417}
418
419impl Iterator for IntoIter {
420    type Item = Range<usize>;
421
422    fn next(&mut self) -> Option<Self::Item> {
423        self.iter.next().map(|(i, range)| {
424            if i >= self.from {
425                (range.start + self.shift) as usize..(range.end + self.shift) as usize
426            } else {
427                range.start as usize..range.end as usize
428            }
429        })
430    }
431}
432
433impl ExactSizeIterator for IntoIter {}
434
435impl PartialEq for Ranges {
436    fn eq(&self, other: &Self) -> bool {
437        self.len() == other.len() && self.iter().eq(other.iter())
438    }
439}
440
441impl Eq for Ranges {}
442
443impl PartialOrd for Ranges {
444    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
445        Some(self.cmp(other))
446    }
447}
448
449impl Ord for Ranges {
450    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
451        let cmp = |r: Range<usize>| [r.start, r.end];
452        self.iter().flat_map(cmp).cmp(other.iter().flat_map(cmp))
453    }
454}
455
456#[track_caller]
457pub fn assert_range(range: &Range<usize>) {
458    assert!(
459        range.start <= range.end,
460        "range starts at {} but ends at {}",
461        range.start,
462        range.end
463    );
464}