1use std::cmp::Ordering;
4
5use stdx::equal_range_by;
6use syntax::{TextRange, TextSize};
7
8#[derive(Default)]
9pub struct RangeMapper {
10 buf: String,
11 ranges: Vec<(TextRange, Option<TextRange>)>,
12}
13
14impl RangeMapper {
15 pub fn add(&mut self, text: &str, source_range: TextRange) {
16 let len = TextSize::of(text);
17 assert_eq!(len, source_range.len());
18 self.add_impl(text, Some(source_range.start()));
19 }
20
21 pub fn add_unmapped(&mut self, text: &str) {
22 self.add_impl(text, None);
23 }
24
25 fn add_impl(&mut self, text: &str, source: Option<TextSize>) {
26 let len = TextSize::of(text);
27 let target_range = TextRange::at(TextSize::of(&self.buf), len);
28 self.ranges.push((target_range, source.map(|it| TextRange::at(it, len))));
29 self.buf.push_str(text);
30 }
31
32 pub fn take_text(&mut self) -> String {
33 std::mem::take(&mut self.buf)
34 }
35
36 pub fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
37 equal_range_by(&self.ranges, |&(r, _)| {
38 if range.is_empty() && r.contains(range.start()) {
39 Ordering::Equal
40 } else {
41 TextRange::ordering(r, range)
42 }
43 })
44 .filter_map(move |i| {
45 let (target_range, source_range) = self.ranges[i];
46 let intersection = target_range.intersect(range).unwrap();
47 let source_range = source_range?;
48 Some(intersection - target_range.start() + source_range.start())
49 })
50 }
51
52 pub fn map_offset_down(&self, offset: TextSize) -> Option<TextSize> {
53 let (target_range, source_range) =
56 self.ranges.iter().find_map(|&(target_range, source_range)| {
57 let source_range = source_range?;
58 if !source_range.contains(offset) {
59 return None;
60 }
61 Some((target_range, source_range))
62 })?;
63 Some(offset - source_range.start() + target_range.start())
64 }
65}