omit_ranges.rs

  1use rope::Rope;
  2use std::{cmp::Ordering, ops::Range};
  3
  4pub(crate) fn text_in_range_omitting_ranges(
  5    rope: &Rope,
  6    range: Range<usize>,
  7    omit_ranges: &[Range<usize>],
  8) -> String {
  9    let mut content = String::with_capacity(range.len());
 10    let mut omit_ranges = omit_ranges
 11        .iter()
 12        .skip_while(|omit_range| omit_range.end <= range.start)
 13        .peekable();
 14    let mut offset = range.start;
 15    let mut chunks = rope.chunks_in_range(range.clone());
 16    while let Some(chunk) = chunks.next() {
 17        if let Some(omit_range) = omit_ranges.peek() {
 18            match offset.cmp(&omit_range.start) {
 19                Ordering::Less => {
 20                    let max_len = omit_range.start - offset;
 21                    if chunk.len() < max_len {
 22                        content.push_str(chunk);
 23                        offset += chunk.len();
 24                    } else {
 25                        content.push_str(&chunk[..max_len]);
 26                        chunks.seek(omit_range.end.min(range.end));
 27                        offset = omit_range.end;
 28                        omit_ranges.next();
 29                    }
 30                }
 31                Ordering::Equal | Ordering::Greater => {
 32                    chunks.seek(omit_range.end.min(range.end));
 33                    offset = omit_range.end;
 34                    omit_ranges.next();
 35                }
 36            }
 37        } else {
 38            content.push_str(chunk);
 39            offset += chunk.len();
 40        }
 41    }
 42
 43    content
 44}
 45
 46#[cfg(test)]
 47mod tests {
 48    use super::*;
 49    use rand::{rngs::StdRng, Rng as _};
 50    use util::RandomCharIter;
 51
 52    #[gpui::test(iterations = 100)]
 53    fn test_text_in_range_omitting_ranges(mut rng: StdRng) {
 54        let text = RandomCharIter::new(&mut rng).take(1024).collect::<String>();
 55        let rope = Rope::from(text.as_str());
 56
 57        let mut start = rng.gen_range(0..=text.len() / 2);
 58        let mut end = rng.gen_range(text.len() / 2..=text.len());
 59        while !text.is_char_boundary(start) {
 60            start -= 1;
 61        }
 62        while !text.is_char_boundary(end) {
 63            end += 1;
 64        }
 65        let range = start..end;
 66
 67        let mut ix = 0;
 68        let mut omit_ranges = Vec::new();
 69        for _ in 0..rng.gen_range(0..10) {
 70            let mut start = rng.gen_range(ix..=text.len());
 71            while !text.is_char_boundary(start) {
 72                start += 1;
 73            }
 74            let mut end = rng.gen_range(start..=text.len());
 75            while !text.is_char_boundary(end) {
 76                end += 1;
 77            }
 78            omit_ranges.push(start..end);
 79            ix = end;
 80            if ix == text.len() {
 81                break;
 82            }
 83        }
 84
 85        let mut expected_text = text[range.clone()].to_string();
 86        for omit_range in omit_ranges.iter().rev() {
 87            let start = omit_range
 88                .start
 89                .saturating_sub(range.start)
 90                .min(range.len());
 91            let end = omit_range.end.saturating_sub(range.start).min(range.len());
 92            expected_text.replace_range(start..end, "");
 93        }
 94
 95        assert_eq!(
 96            text_in_range_omitting_ranges(&rope, range.clone(), &omit_ranges),
 97            expected_text,
 98            "text: {text:?}\nrange: {range:?}\nomit_ranges: {omit_ranges:?}"
 99        );
100    }
101}