1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
use std::{fmt, ops};
use crate::{
text_utils::{contains_offset_nonstrict, intersect},
SyntaxNodeRef, TextRange, TextUnit,
};
#[derive(Clone)]
pub struct SyntaxText<'a> {
node: SyntaxNodeRef<'a>,
range: TextRange,
}
impl<'a> SyntaxText<'a> {
pub(crate) fn new(node: SyntaxNodeRef<'a>) -> SyntaxText<'a> {
SyntaxText {
node,
range: node.range(),
}
}
pub fn chunks(&self) -> impl Iterator<Item = &'a str> {
let range = self.range;
self.node.descendants().filter_map(move |node| {
let text = node.leaf_text()?;
let range = intersect(range, node.range())?;
let range = range - node.range().start();
Some(&text[range])
})
}
pub fn push_to(&self, buf: &mut String) {
self.chunks().for_each(|it| buf.push_str(it));
}
pub fn to_string(&self) -> String {
self.chunks().collect()
}
pub fn contains(&self, c: char) -> bool {
self.chunks().any(|it| it.contains(c))
}
pub fn find(&self, c: char) -> Option<TextUnit> {
let mut acc: TextUnit = 0.into();
for chunk in self.chunks() {
if let Some(pos) = chunk.find(c) {
let pos: TextUnit = (pos as u32).into();
return Some(acc + pos);
}
acc += TextUnit::of_str(chunk);
}
None
}
pub fn len(&self) -> TextUnit {
self.range.len()
}
pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> {
let range = range.restrict(self.range).unwrap_or_else(|| {
panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range)
});
SyntaxText {
node: self.node,
range,
}
}
pub fn char_at(&self, offset: TextUnit) -> Option<char> {
let mut start: TextUnit = 0.into();
for chunk in self.chunks() {
let end = start + TextUnit::of_str(chunk);
if start <= offset && offset < end {
let off: usize = u32::from(offset - start) as usize;
return Some(chunk[off..].chars().next().unwrap());
}
start = end;
}
None
}
}
impl<'a> fmt::Debug for SyntaxText<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.to_string(), f)
}
}
impl<'a> fmt::Display for SyntaxText<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.to_string(), f)
}
}
pub trait SyntaxTextSlice: fmt::Debug {
fn restrict(&self, range: TextRange) -> Option<TextRange>;
}
impl SyntaxTextSlice for TextRange {
fn restrict(&self, range: TextRange) -> Option<TextRange> {
intersect(*self, range)
}
}
impl SyntaxTextSlice for ops::RangeTo<TextUnit> {
fn restrict(&self, range: TextRange) -> Option<TextRange> {
if !contains_offset_nonstrict(range, self.end) {
return None;
}
Some(TextRange::from_to(range.start(), self.end))
}
}
impl SyntaxTextSlice for ops::RangeFrom<TextUnit> {
fn restrict(&self, range: TextRange) -> Option<TextRange> {
if !contains_offset_nonstrict(range, self.start) {
return None;
}
Some(TextRange::from_to(self.start, range.end()))
}
}
impl SyntaxTextSlice for ops::Range<TextUnit> {
fn restrict(&self, range: TextRange) -> Option<TextRange> {
TextRange::from_to(self.start, self.end).restrict(range)
}
}
|