rowan/
syntax_text.rs

1use std::fmt;
2
3use crate::{
4    cursor::{SyntaxNode, SyntaxToken},
5    TextRange, TextSize,
6};
7
8#[derive(Clone)]
9pub struct SyntaxText {
10    node: SyntaxNode,
11    range: TextRange,
12}
13
14impl SyntaxText {
15    pub(crate) fn new(node: SyntaxNode) -> SyntaxText {
16        let range = node.text_range();
17        SyntaxText { node, range }
18    }
19
20    pub fn len(&self) -> TextSize {
21        self.range.len()
22    }
23
24    pub fn is_empty(&self) -> bool {
25        self.range.is_empty()
26    }
27
28    pub fn contains_char(&self, c: char) -> bool {
29        self.try_for_each_chunk(|chunk| if chunk.contains(c) { Err(()) } else { Ok(()) }).is_err()
30    }
31
32    pub fn find_char(&self, c: char) -> Option<TextSize> {
33        let mut acc: TextSize = 0.into();
34        let res = self.try_for_each_chunk(|chunk| {
35            if let Some(pos) = chunk.find(c) {
36                let pos: TextSize = (pos as u32).into();
37                return Err(acc + pos);
38            }
39            acc += TextSize::of(chunk);
40            Ok(())
41        });
42        found(res)
43    }
44
45    pub fn char_at(&self, offset: TextSize) -> Option<char> {
46        let offset = offset.into();
47        let mut start: TextSize = 0.into();
48        let res = self.try_for_each_chunk(|chunk| {
49            let end = start + TextSize::of(chunk);
50            if start <= offset && offset < end {
51                let off: usize = u32::from(offset - start) as usize;
52                return Err(chunk[off..].chars().next().unwrap());
53            }
54            start = end;
55            Ok(())
56        });
57        found(res)
58    }
59
60    pub fn slice<R: private::SyntaxTextRange>(&self, range: R) -> SyntaxText {
61        let start = range.start().unwrap_or_default();
62        let end = range.end().unwrap_or(self.len());
63        assert!(start <= end);
64        let len = end - start;
65        let start = self.range.start() + start;
66        let end = start + len;
67        assert!(
68            start <= end,
69            "invalid slice, range: {:?}, slice: {:?}",
70            self.range,
71            (range.start(), range.end()),
72        );
73        let range = TextRange::new(start, end);
74        assert!(
75            self.range.contains_range(range),
76            "invalid slice, range: {:?}, slice: {:?}",
77            self.range,
78            range,
79        );
80        SyntaxText { node: self.node.clone(), range }
81    }
82
83    pub fn try_fold_chunks<T, F, E>(&self, init: T, mut f: F) -> Result<T, E>
84    where
85        F: FnMut(T, &str) -> Result<T, E>,
86    {
87        self.tokens_with_ranges()
88            .try_fold(init, move |acc, (token, range)| f(acc, &token.text()[range]))
89    }
90
91    pub fn try_for_each_chunk<F: FnMut(&str) -> Result<(), E>, E>(
92        &self,
93        mut f: F,
94    ) -> Result<(), E> {
95        self.try_fold_chunks((), move |(), chunk| f(chunk))
96    }
97
98    pub fn for_each_chunk<F: FnMut(&str)>(&self, mut f: F) {
99        enum Void {}
100        match self.try_for_each_chunk(|chunk| Ok::<(), Void>(f(chunk))) {
101            Ok(()) => (),
102            Err(void) => match void {},
103        }
104    }
105
106    fn tokens_with_ranges(&self) -> impl Iterator<Item = (SyntaxToken, TextRange)> {
107        let text_range = self.range;
108        self.node.descendants_with_tokens().filter_map(|element| element.into_token()).filter_map(
109            move |token| {
110                let token_range = token.text_range();
111                let range = text_range.intersect(token_range)?;
112                Some((token, range - token_range.start()))
113            },
114        )
115    }
116}
117
118fn found<T>(res: Result<(), T>) -> Option<T> {
119    match res {
120        Ok(()) => None,
121        Err(it) => Some(it),
122    }
123}
124
125impl fmt::Debug for SyntaxText {
126    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127        fmt::Debug::fmt(&self.to_string(), f)
128    }
129}
130
131impl fmt::Display for SyntaxText {
132    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133        self.try_for_each_chunk(|chunk| fmt::Display::fmt(chunk, f))
134    }
135}
136
137impl From<SyntaxText> for String {
138    fn from(text: SyntaxText) -> String {
139        text.to_string()
140    }
141}
142
143impl PartialEq<str> for SyntaxText {
144    fn eq(&self, mut rhs: &str) -> bool {
145        self.try_for_each_chunk(|chunk| {
146            if !rhs.starts_with(chunk) {
147                return Err(());
148            }
149            rhs = &rhs[chunk.len()..];
150            Ok(())
151        })
152        .is_ok()
153            && rhs.is_empty()
154    }
155}
156
157impl PartialEq<SyntaxText> for str {
158    fn eq(&self, rhs: &SyntaxText) -> bool {
159        rhs == self
160    }
161}
162
163impl PartialEq<&'_ str> for SyntaxText {
164    fn eq(&self, rhs: &&str) -> bool {
165        self == *rhs
166    }
167}
168
169impl PartialEq<SyntaxText> for &'_ str {
170    fn eq(&self, rhs: &SyntaxText) -> bool {
171        rhs == self
172    }
173}
174
175impl PartialEq for SyntaxText {
176    fn eq(&self, other: &SyntaxText) -> bool {
177        if self.range.len() != other.range.len() {
178            return false;
179        }
180        let mut lhs = self.tokens_with_ranges();
181        let mut rhs = other.tokens_with_ranges();
182        zip_texts(&mut lhs, &mut rhs).is_none()
183            && lhs.all(|it| it.1.is_empty())
184            && rhs.all(|it| it.1.is_empty())
185    }
186}
187
188fn zip_texts<I: Iterator<Item = (SyntaxToken, TextRange)>>(xs: &mut I, ys: &mut I) -> Option<()> {
189    let mut x = xs.next()?;
190    let mut y = ys.next()?;
191    loop {
192        while x.1.is_empty() {
193            x = xs.next()?;
194        }
195        while y.1.is_empty() {
196            y = ys.next()?;
197        }
198        let x_text = &x.0.text()[x.1];
199        let y_text = &y.0.text()[y.1];
200        if !(x_text.starts_with(y_text) || y_text.starts_with(x_text)) {
201            return Some(());
202        }
203        let advance = std::cmp::min(x.1.len(), y.1.len());
204        x.1 = TextRange::new(x.1.start() + advance, x.1.end());
205        y.1 = TextRange::new(y.1.start() + advance, y.1.end());
206    }
207}
208
209impl Eq for SyntaxText {}
210
211mod private {
212    use std::ops;
213
214    use crate::{TextRange, TextSize};
215
216    pub trait SyntaxTextRange {
217        fn start(&self) -> Option<TextSize>;
218        fn end(&self) -> Option<TextSize>;
219    }
220
221    impl SyntaxTextRange for TextRange {
222        fn start(&self) -> Option<TextSize> {
223            Some(TextRange::start(*self))
224        }
225        fn end(&self) -> Option<TextSize> {
226            Some(TextRange::end(*self))
227        }
228    }
229
230    impl SyntaxTextRange for ops::Range<TextSize> {
231        fn start(&self) -> Option<TextSize> {
232            Some(self.start)
233        }
234        fn end(&self) -> Option<TextSize> {
235            Some(self.end)
236        }
237    }
238
239    impl SyntaxTextRange for ops::RangeFrom<TextSize> {
240        fn start(&self) -> Option<TextSize> {
241            Some(self.start)
242        }
243        fn end(&self) -> Option<TextSize> {
244            None
245        }
246    }
247
248    impl SyntaxTextRange for ops::RangeTo<TextSize> {
249        fn start(&self) -> Option<TextSize> {
250            None
251        }
252        fn end(&self) -> Option<TextSize> {
253            Some(self.end)
254        }
255    }
256
257    impl SyntaxTextRange for ops::RangeFull {
258        fn start(&self) -> Option<TextSize> {
259            None
260        }
261        fn end(&self) -> Option<TextSize> {
262            None
263        }
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use crate::{green::SyntaxKind, GreenNodeBuilder};
270
271    use super::*;
272
273    fn build_tree(chunks: &[&str]) -> SyntaxNode {
274        let mut builder = GreenNodeBuilder::new();
275        builder.start_node(SyntaxKind(62));
276        for &chunk in chunks.iter() {
277            builder.token(SyntaxKind(92), chunk.into())
278        }
279        builder.finish_node();
280        SyntaxNode::new_root(builder.finish())
281    }
282
283    #[test]
284    fn test_text_equality() {
285        fn do_check(t1: &[&str], t2: &[&str]) {
286            let t1 = build_tree(t1).text();
287            let t2 = build_tree(t2).text();
288            let expected = t1.to_string() == t2.to_string();
289            let actual = t1 == t2;
290            assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (SyntaxText)", t1, t2);
291            let actual = t1 == &*t2.to_string();
292            assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (&str)", t1, t2);
293        }
294        fn check(t1: &[&str], t2: &[&str]) {
295            do_check(t1, t2);
296            do_check(t2, t1)
297        }
298
299        check(&[""], &[""]);
300        check(&["a"], &[""]);
301        check(&["a"], &["a"]);
302        check(&["abc"], &["def"]);
303        check(&["hello", "world"], &["hello", "world"]);
304        check(&["hellowo", "rld"], &["hell", "oworld"]);
305        check(&["hel", "lowo", "rld"], &["helloworld"]);
306        check(&["{", "abc", "}"], &["{", "123", "}"]);
307        check(&["{", "abc", "}", "{"], &["{", "123", "}"]);
308        check(&["{", "abc", "}"], &["{", "123", "}", "{"]);
309        check(&["{", "abc", "}ab"], &["{", "abc", "}", "ab"]);
310    }
311}