diff options
Diffstat (limited to 'crates/ide/src/syntax_highlighting/injector.rs')
-rw-r--r-- | crates/ide/src/syntax_highlighting/injector.rs | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/crates/ide/src/syntax_highlighting/injector.rs b/crates/ide/src/syntax_highlighting/injector.rs new file mode 100644 index 000000000..24ff473ec --- /dev/null +++ b/crates/ide/src/syntax_highlighting/injector.rs | |||
@@ -0,0 +1,78 @@ | |||
1 | //! Extracts a subsequence of a text document, remembering the mapping of ranges | ||
2 | //! between original and extracted texts. | ||
3 | use std::ops::{self, Sub}; | ||
4 | |||
5 | use stdx::equal_range_by; | ||
6 | use syntax::{TextRange, TextSize}; | ||
7 | |||
8 | #[derive(Default)] | ||
9 | pub(super) struct Injector { | ||
10 | buf: String, | ||
11 | ranges: Vec<(TextRange, Option<Delta<TextSize>>)>, | ||
12 | } | ||
13 | |||
14 | impl Injector { | ||
15 | pub(super) 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 | pub(super) fn add_unmapped(&mut self, text: &str) { | ||
21 | self.add_impl(text, None); | ||
22 | } | ||
23 | fn add_impl(&mut self, text: &str, source: Option<TextSize>) { | ||
24 | let len = TextSize::of(text); | ||
25 | let target_range = TextRange::at(TextSize::of(&self.buf), len); | ||
26 | self.ranges.push((target_range, source.map(|it| Delta::new(target_range.start(), it)))); | ||
27 | self.buf.push_str(text); | ||
28 | } | ||
29 | |||
30 | pub(super) fn text(&self) -> &str { | ||
31 | &self.buf | ||
32 | } | ||
33 | pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ { | ||
34 | equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| { | ||
35 | let (target_range, delta) = self.ranges[i]; | ||
36 | let intersection = target_range.intersect(range).unwrap(); | ||
37 | Some(intersection + delta?) | ||
38 | }) | ||
39 | } | ||
40 | } | ||
41 | |||
42 | #[derive(Clone, Copy)] | ||
43 | enum Delta<T> { | ||
44 | Add(T), | ||
45 | Sub(T), | ||
46 | } | ||
47 | |||
48 | impl<T> Delta<T> { | ||
49 | fn new(from: T, to: T) -> Delta<T> | ||
50 | where | ||
51 | T: Ord + Sub<Output = T>, | ||
52 | { | ||
53 | if to >= from { | ||
54 | Delta::Add(to - from) | ||
55 | } else { | ||
56 | Delta::Sub(from - to) | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | impl ops::Add<Delta<TextSize>> for TextSize { | ||
62 | type Output = TextSize; | ||
63 | |||
64 | fn add(self, rhs: Delta<TextSize>) -> TextSize { | ||
65 | match rhs { | ||
66 | Delta::Add(it) => self + it, | ||
67 | Delta::Sub(it) => self - it, | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | impl ops::Add<Delta<TextSize>> for TextRange { | ||
73 | type Output = TextRange; | ||
74 | |||
75 | fn add(self, rhs: Delta<TextSize>) -> TextRange { | ||
76 | TextRange::at(self.start() + rhs, self.len()) | ||
77 | } | ||
78 | } | ||