diff options
Diffstat (limited to 'crates/ide/src/syntax_highlighting/injector.rs')
-rw-r--r-- | crates/ide/src/syntax_highlighting/injector.rs | 81 |
1 files changed, 81 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..e8f17eb69 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/injector.rs | |||
@@ -0,0 +1,81 @@ | |||
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 | use super::highlights::ordering; | ||
9 | |||
10 | #[derive(Default)] | ||
11 | pub(super) struct Injector { | ||
12 | buf: String, | ||
13 | ranges: Vec<(TextRange, Option<Delta<TextSize>>)>, | ||
14 | } | ||
15 | |||
16 | impl Injector { | ||
17 | pub(super) fn add(&mut self, text: &str, source_range: TextRange) { | ||
18 | let len = TextSize::of(text); | ||
19 | assert_eq!(len, source_range.len()); | ||
20 | self.add_impl(text, Some(source_range.start())); | ||
21 | } | ||
22 | pub(super) fn add_unmapped(&mut self, text: &str) { | ||
23 | self.add_impl(text, None); | ||
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| Delta::new(target_range.start(), it)))); | ||
29 | self.buf.push_str(text); | ||
30 | } | ||
31 | |||
32 | pub(super) fn text(&self) -> &str { | ||
33 | &self.buf | ||
34 | } | ||
35 | pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ { | ||
36 | let (start, len) = equal_range_by(&self.ranges, |&(r, _)| ordering(r, range)); | ||
37 | (start..start + len).filter_map(move |i| { | ||
38 | let (target_range, delta) = self.ranges[i]; | ||
39 | let intersection = target_range.intersect(range).unwrap(); | ||
40 | Some(intersection + delta?) | ||
41 | }) | ||
42 | } | ||
43 | } | ||
44 | |||
45 | #[derive(Clone, Copy)] | ||
46 | enum Delta<T> { | ||
47 | Add(T), | ||
48 | Sub(T), | ||
49 | } | ||
50 | |||
51 | impl<T> Delta<T> { | ||
52 | fn new(from: T, to: T) -> Delta<T> | ||
53 | where | ||
54 | T: Ord + Sub<Output = T>, | ||
55 | { | ||
56 | if to >= from { | ||
57 | Delta::Add(to - from) | ||
58 | } else { | ||
59 | Delta::Sub(from - to) | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | impl ops::Add<Delta<TextSize>> for TextSize { | ||
65 | type Output = TextSize; | ||
66 | |||
67 | fn add(self, rhs: Delta<TextSize>) -> TextSize { | ||
68 | match rhs { | ||
69 | Delta::Add(it) => self + it, | ||
70 | Delta::Sub(it) => self - it, | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | impl ops::Add<Delta<TextSize>> for TextRange { | ||
76 | type Output = TextRange; | ||
77 | |||
78 | fn add(self, rhs: Delta<TextSize>) -> TextRange { | ||
79 | TextRange::at(self.start() + rhs, self.len()) | ||
80 | } | ||
81 | } | ||