aboutsummaryrefslogtreecommitdiff
path: root/crates/libsyntax2/src/yellow/syntax_text.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-28 12:06:30 +0100
committerAleksey Kladov <[email protected]>2018-08-28 12:06:30 +0100
commit7e74af32268f9b0783ca94107b0b10d52e4ebe5e (patch)
tree179d818c695a27ceee3f8193e219234854190f9a /crates/libsyntax2/src/yellow/syntax_text.rs
parent363f466627db373fab23d1df94b7382223b8675a (diff)
Avoid materializing strings
Diffstat (limited to 'crates/libsyntax2/src/yellow/syntax_text.rs')
-rw-r--r--crates/libsyntax2/src/yellow/syntax_text.rs101
1 files changed, 101 insertions, 0 deletions
diff --git a/crates/libsyntax2/src/yellow/syntax_text.rs b/crates/libsyntax2/src/yellow/syntax_text.rs
new file mode 100644
index 000000000..268547470
--- /dev/null
+++ b/crates/libsyntax2/src/yellow/syntax_text.rs
@@ -0,0 +1,101 @@
1use std::{
2 fmt, ops,
3};
4
5use {
6 SyntaxNodeRef, TextRange, TextUnit,
7 algo::walk::preorder,
8 text_utils::{intersect, contains_offset_nonstrict},
9};
10
11#[derive(Clone)]
12pub struct SyntaxText<'a> {
13 node: SyntaxNodeRef<'a>,
14 range: TextRange,
15}
16
17impl<'a> SyntaxText<'a> {
18 pub(crate) fn new(node: SyntaxNodeRef<'a>) -> SyntaxText<'a> {
19 SyntaxText {
20 node,
21 range: node.range()
22 }
23 }
24 pub fn chunks(&self) -> impl Iterator<Item=&'a str> {
25 let range = self.range;
26 preorder(self.node)
27 .filter_map(move |node| {
28 let text = node.leaf_text_ref()?;
29 let range = intersect(range, node.range())?;
30 let range = range - node.range().start();
31 Some(&text[range])
32 })
33 }
34 pub fn to_string(&self) -> String {
35 self.chunks().collect()
36 }
37 pub fn contains(&self, c: char) -> bool {
38 self.chunks().any(|it| it.contains(c))
39 }
40 pub fn find(&self, c: char) -> Option<TextUnit> {
41 let mut acc: TextUnit = 0.into();
42 for chunk in self.chunks() {
43 if let Some(pos) = chunk.find(c) {
44 let pos: TextUnit = (pos as u32).into();
45 return Some(acc + pos);
46 }
47 acc += TextUnit::of_str(chunk);
48 }
49 None
50 }
51 pub fn len(&self) -> TextUnit {
52 self.range.len()
53 }
54 pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> {
55 let range = range.restrict(self.range)
56 .unwrap_or_else(|| {
57 panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range)
58 });
59 SyntaxText { node: self.node, range }
60 }
61}
62
63impl<'a> fmt::Debug for SyntaxText<'a> {
64 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65 fmt::Debug::fmt(&self.to_string(), f)
66 }
67}
68
69impl<'a> fmt::Display for SyntaxText<'a> {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 fmt::Display::fmt(&self.to_string(), f)
72 }
73}
74
75pub trait SyntaxTextSlice: fmt::Debug {
76 fn restrict(&self, range: TextRange) -> Option<TextRange>;
77}
78
79impl SyntaxTextSlice for TextRange {
80 fn restrict(&self, range: TextRange) -> Option<TextRange> {
81 intersect(*self, range)
82 }
83}
84
85impl SyntaxTextSlice for ops::RangeTo<TextUnit> {
86 fn restrict(&self, range: TextRange) -> Option<TextRange> {
87 if !contains_offset_nonstrict(range, self.end) {
88 return None;
89 }
90 Some(TextRange::from_to(range.start(), self.end))
91 }
92}
93
94impl SyntaxTextSlice for ops::RangeFrom<TextUnit> {
95 fn restrict(&self, range: TextRange) -> Option<TextRange> {
96 if !contains_offset_nonstrict(range, self.start) {
97 return None;
98 }
99 Some(TextRange::from_to(self.start, range.end()))
100 }
101}