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: T) => 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: &str| fmt::Display::fmt(self: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: &str| {
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: impl Iterator = self.tokens_with_ranges();
181 let mut rhs: impl Iterator = other.tokens_with_ranges();
182 zip_texts(&mut lhs, &mut rhs).is_none()
183 && lhs.all(|it: (SyntaxToken, TextRange)| it.1.is_empty())
184 && rhs.all(|it: (SyntaxToken, TextRange)| 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: (SyntaxToken, TextRange) = xs.next()?;
190 let mut y: (SyntaxToken, TextRange) = 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: &str = &x.0.text()[x.1];
199 let y_text: &str = &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: TextSize = std::cmp::min(v1:x.1.len(), v2:y.1.len());
204 x.1 = TextRange::new(start:x.1.start() + advance, x.1.end());
205 y.1 = TextRange::new(start: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}
312