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::Parser
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    pub fn add(&mut self, new: Range<usize>) {
80        assert_range(&new);
81        if new.len() < self.min_len {
82            return;
83        }
84
85        let new = new.start as i32..new.end as i32;
86
87        let m_range = merging_range_by_guess_and_lazy_shift(
88            (&self.list, self.list.len()),
89            (self.from, [new.start, new.end]),
90            (self.from, self.shift, 0, std::ops::Add::add),
91            (|r| r.start, |r| r.end),
92        );
93
94        // The ranges in this region have not been callibrated yet.
95        if self.from <= m_range.end {
96            for range in self.list.range_mut(self.from..m_range.end).iter_mut() {
97                range.start += self.shift;
98                range.end += self.shift;
99            }
100        }
101
102        let added_range = {
103            let slice = self.list.range(m_range.clone());
104            let mut iter = slice.iter();
105
106            let first = iter.next().cloned().unwrap_or(new.clone());
107            let last = iter.next_back().cloned().unwrap_or(first.clone());
108
109            first.start.min(new.start)..last.end.max(new.end)
110        };
111
112        let new_from = self.from.saturating_sub(m_range.len()).max(m_range.start) + 1;
113        if new_from < self.list.len() + 1 - m_range.len() {
114            self.from = new_from;
115        } else {
116            self.from = 0;
117            self.shift = 0;
118        }
119
120        self.list.splice(m_range.clone(), [added_range]);
121    }
122
123    /// Removes a [`Range`] from the list, returning an
124    /// [`Iterator`] over the [`Range`]s that were taken
125    pub fn remove(
126        &mut self,
127        within: Range<usize>,
128    ) -> impl ExactSizeIterator<Item = Range<usize>> + '_ {
129        assert_range(&within);
130        let within = within.start as i32..within.end as i32;
131
132        let m_range = merging_range_by_guess_and_lazy_shift(
133            (&self.list, self.list.len()),
134            (self.from, [within.start, within.end]),
135            (self.from, self.shift, 0, std::ops::Add::add),
136            (|r| r.start, |r| r.end),
137        );
138
139        // The ranges in this region have not been callibrated yet.
140        if self.from <= m_range.end {
141            for range in self.list.range_mut(self.from..m_range.end).iter_mut() {
142                range.start += self.shift;
143                range.end += self.shift;
144            }
145        }
146
147        let split_off = {
148            let slice = self.list.range(m_range.clone());
149
150            let first = slice.iter().next().cloned();
151            let last = slice.iter().next_back().cloned().or(first.clone());
152
153            let split_start = first.filter(|f| f.start < within.start).map(|first| {
154                self.list[m_range.start].start = within.start;
155                first.start..within.start
156            });
157            let split_end = last.filter(|l| l.end > within.end).map(|last| {
158                self.list[m_range.end - 1].end = within.end;
159                within.end..last.end
160            });
161
162            let min_len = |range: &Range<i32>| range.len() >= self.min_len;
163
164            [split_start.filter(min_len), split_end.filter(min_len)]
165        };
166
167        let added = split_off.iter().flatten().count();
168
169        let new_from = self.from.saturating_sub(m_range.len()).max(m_range.start) + added;
170        if new_from < self.list.len() + added - m_range.len() {
171            self.from = new_from;
172        } else {
173            self.from = 0;
174            self.shift = 0;
175        }
176
177        self.list
178            .splice(m_range, split_off.into_iter().flatten())
179            .map(|r| r.start as usize..r.end as usize)
180    }
181
182    /// Applies the [`add`] function to another [`Ranges`]s
183    ///
184    /// [`add`]: Self::add
185    pub fn merge(&mut self, other: Self) {
186        for range in other.list {
187            self.add(range.start as usize..range.end as usize)
188        }
189    }
190
191    /// Shifts the [`Range`]s within by a [`Change`]
192    ///
193    /// If the `diff` is negative (i.e. a part of the ranges were
194    /// removed), then ranges will be removed ahead of `from`
195    /// accordingly.
196    ///
197    /// [`Change`]: crate::text::Change
198    pub fn shift_by(&mut self, from: usize, shift: i32) {
199        let from = from as i32;
200
201        // The range of changes that will be drained
202        let m_range = merging_range_by_guess_and_lazy_shift(
203            (&self.list, self.list.len()),
204            (self.from, [from, from - shift.min(0)]),
205            (self.from, self.shift, 0, std::ops::Add::add),
206            (|r| r.start, |r| r.end),
207        );
208
209        // If self.from < m_range.end, I need to shift the changes between
210        // the two, so that they match the shifting of the changes
211        // before self.from.
212        if self.from < m_range.end && self.shift != 0 {
213            for range in self.list.range_mut(self.from..m_range.end).iter_mut() {
214                range.start += self.shift;
215                range.end += self.shift;
216            }
217        // If self.from > m_range.end, then I shift the ranges
218        // inbetween by -self.shift, in order to keep the ranges
219        // after m_range.start equally unshifted by self.shift +
220        // shift.
221        } else if self.from > m_range.end && self.shift != 0 {
222            for range in &mut self.list.range_mut(m_range.end..self.from) {
223                range.start -= self.shift;
224                range.end -= self.shift;
225            }
226        }
227
228        let range_left = {
229            let slice = self.list.range(m_range.clone());
230            let mut iter = slice.iter();
231
232            iter.next().and_then(|first| {
233                let last = iter.next_back().unwrap_or(first);
234                let range = first.start.min(from)..(last.end + shift).max(from);
235
236                (range.len() >= self.min_len).then_some(range)
237            })
238        };
239
240        let new_from = m_range.start + range_left.is_some() as usize;
241
242        self.list.splice(m_range.clone(), range_left);
243
244        if new_from < self.list.len() {
245            self.from = new_from;
246            self.shift += shift;
247        } else {
248            self.from = 0;
249            self.shift = 0;
250        }
251    }
252
253    /// Returns the number of [`Range<usize>`]s
254    pub fn len(&self) -> usize {
255        self.list.len()
256    }
257
258    /// Returns `true` if there are no [`Range<usize>`]s
259    pub fn is_empty(&self) -> bool {
260        self.list.is_empty()
261    }
262
263    /// An [`Iterator`] over the [`Range`]s in this list
264    pub fn iter(&self) -> impl Iterator<Item = Range<usize>> {
265        self.list.iter().enumerate().map(move |(i, range)| {
266            if i >= self.from {
267                (range.start + self.shift) as usize..(range.end + self.shift) as usize
268            } else {
269                range.start as usize..range.end as usize
270            }
271        })
272    }
273
274    /// The same as [`Ranges::remove`], but without removing, just
275    /// iterating over the relevant ranges
276    pub fn iter_over(&self, within: Range<usize>) -> impl Iterator<Item = Range<usize>> {
277        assert_range(&within);
278        let within = within.start as i32..within.end as i32;
279
280        let m_range = merging_range_by_guess_and_lazy_shift(
281            (&self.list, self.list.len()),
282            (self.from, [within.start, within.end]),
283            (self.from, self.shift, 0, std::ops::Add::add),
284            (|r| r.start, |r| r.end),
285        );
286
287        let (s0, s1) = self.list.range(m_range.clone()).as_slices();
288        s0.iter().chain(s1).enumerate().map(move |(i, range)| {
289            let mut range = if i + m_range.start > self.from {
290                range.start + self.shift..range.end + self.shift
291            } else {
292                range.clone()
293            };
294
295            if i == 0 {
296                range.start = range.start.max(within.start);
297            }
298            if i == m_range.len() - 1 {
299                range.end = range.end.min(within.end);
300            }
301
302            range.start as usize..range.end as usize
303        })
304    }
305}
306
307impl IntoIterator for Ranges {
308    type IntoIter = IntoIter;
309    type Item = Range<usize>;
310
311    #[define_opaque(IntoIter)]
312    fn into_iter(self) -> Self::IntoIter {
313        self.list.into_iter().enumerate().map(move |(i, range)| {
314            if i >= self.from {
315                (range.start + self.shift) as usize..(range.end + self.shift) as usize
316            } else {
317                range.start as usize..range.end as usize
318            }
319        })
320    }
321}
322
323pub type IntoIter = impl ExactSizeIterator<Item = Range<usize>>;
324
325fn assert_range(range: &Range<usize>) {
326    assert!(
327        range.start <= range.end,
328        "range starts at {} but ends at {}",
329        range.start,
330        range.end
331    );
332}