diff options
author | Aleksey Kladov <[email protected]> | 2018-08-28 12:06:30 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-28 12:06:30 +0100 |
commit | 7e74af32268f9b0783ca94107b0b10d52e4ebe5e (patch) | |
tree | 179d818c695a27ceee3f8193e219234854190f9a /crates/libsyntax2/src/yellow | |
parent | 363f466627db373fab23d1df94b7382223b8675a (diff) |
Avoid materializing strings
Diffstat (limited to 'crates/libsyntax2/src/yellow')
-rw-r--r-- | crates/libsyntax2/src/yellow/green.rs | 16 | ||||
-rw-r--r-- | crates/libsyntax2/src/yellow/mod.rs | 10 | ||||
-rw-r--r-- | crates/libsyntax2/src/yellow/syntax.rs | 19 | ||||
-rw-r--r-- | crates/libsyntax2/src/yellow/syntax_text.rs | 101 |
4 files changed, 127 insertions, 19 deletions
diff --git a/crates/libsyntax2/src/yellow/green.rs b/crates/libsyntax2/src/yellow/green.rs index 700f2704f..59aefb0de 100644 --- a/crates/libsyntax2/src/yellow/green.rs +++ b/crates/libsyntax2/src/yellow/green.rs | |||
@@ -43,21 +43,13 @@ impl GreenNode { | |||
43 | } | 43 | } |
44 | } | 44 | } |
45 | 45 | ||
46 | pub fn text(&self) -> String { | 46 | pub fn leaf_text(&self) -> Option<SmolStr> { |
47 | let mut buff = String::new(); | 47 | self.leaf_text_ref().map(Clone::clone) |
48 | go(self, &mut buff); | ||
49 | return buff; | ||
50 | fn go(node: &GreenNode, buff: &mut String) { | ||
51 | match node { | ||
52 | GreenNode::Leaf { text, .. } => buff.push_str(text.as_str()), | ||
53 | GreenNode::Branch(b) => b.children().iter().for_each(|child| go(child, buff)), | ||
54 | } | ||
55 | } | ||
56 | } | 48 | } |
57 | 49 | ||
58 | pub fn leaf_text(&self) -> Option<SmolStr> { | 50 | pub fn leaf_text_ref(&self) -> Option<&SmolStr> { |
59 | match self { | 51 | match self { |
60 | GreenNode::Leaf { text, .. } => Some(text.clone()), | 52 | GreenNode::Leaf { text, .. } => Some(text), |
61 | GreenNode::Branch(_) => None, | 53 | GreenNode::Branch(_) => None, |
62 | } | 54 | } |
63 | } | 55 | } |
diff --git a/crates/libsyntax2/src/yellow/mod.rs b/crates/libsyntax2/src/yellow/mod.rs index b94c794fe..82eda79d6 100644 --- a/crates/libsyntax2/src/yellow/mod.rs +++ b/crates/libsyntax2/src/yellow/mod.rs | |||
@@ -2,6 +2,7 @@ mod builder; | |||
2 | mod green; | 2 | mod green; |
3 | mod red; | 3 | mod red; |
4 | mod syntax; | 4 | mod syntax; |
5 | mod syntax_text; | ||
5 | 6 | ||
6 | use std::{ | 7 | use std::{ |
7 | sync::Arc, | 8 | sync::Arc, |
@@ -12,6 +13,7 @@ pub(crate) use self::{ | |||
12 | builder::GreenBuilder, | 13 | builder::GreenBuilder, |
13 | green::GreenNode, | 14 | green::GreenNode, |
14 | red::RedNode, | 15 | red::RedNode, |
16 | syntax_text::SyntaxText, | ||
15 | }; | 17 | }; |
16 | 18 | ||
17 | #[derive(Debug)] | 19 | #[derive(Debug)] |
@@ -32,6 +34,12 @@ pub struct OwnedRoot(Arc<SyntaxRoot>); | |||
32 | #[derive(Clone, Copy, Debug)] | 34 | #[derive(Clone, Copy, Debug)] |
33 | pub struct RefRoot<'a>(&'a OwnedRoot); // TODO: shared_from_this instead of double indirection | 35 | pub struct RefRoot<'a>(&'a OwnedRoot); // TODO: shared_from_this instead of double indirection |
34 | 36 | ||
37 | impl<'a> RefRoot<'a> { | ||
38 | fn syntax_root(&self) -> &'a SyntaxRoot { | ||
39 | self.0.syntax_root() | ||
40 | } | ||
41 | } | ||
42 | |||
35 | impl TreeRoot for OwnedRoot { | 43 | impl TreeRoot for OwnedRoot { |
36 | fn borrowed(&self) -> RefRoot { | 44 | fn borrowed(&self) -> RefRoot { |
37 | RefRoot(&self) | 45 | RefRoot(&self) |
@@ -78,7 +86,7 @@ impl RedPtr { | |||
78 | RedPtr(red.into()) | 86 | RedPtr(red.into()) |
79 | } | 87 | } |
80 | 88 | ||
81 | unsafe fn get<'a>(self, _root: &'a impl TreeRoot) -> &'a RedNode { | 89 | unsafe fn get<'a>(self, _root: &'a SyntaxRoot) -> &'a RedNode { |
82 | &*self.0.as_ptr() | 90 | &*self.0.as_ptr() |
83 | } | 91 | } |
84 | } | 92 | } |
diff --git a/crates/libsyntax2/src/yellow/syntax.rs b/crates/libsyntax2/src/yellow/syntax.rs index 75b6cb7dc..ede9ad3c8 100644 --- a/crates/libsyntax2/src/yellow/syntax.rs +++ b/crates/libsyntax2/src/yellow/syntax.rs | |||
@@ -6,7 +6,7 @@ use std::{ | |||
6 | use smol_str::SmolStr; | 6 | use smol_str::SmolStr; |
7 | 7 | ||
8 | use { | 8 | use { |
9 | yellow::{GreenNode, RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot}, | 9 | yellow::{GreenNode, RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot, SyntaxText}, |
10 | SyntaxKind::{self, *}, | 10 | SyntaxKind::{self, *}, |
11 | TextRange, TextUnit, | 11 | TextRange, TextUnit, |
12 | }; | 12 | }; |
@@ -58,6 +58,13 @@ impl SyntaxNode<OwnedRoot> { | |||
58 | } | 58 | } |
59 | } | 59 | } |
60 | 60 | ||
61 | impl<'a> SyntaxNode<RefRoot<'a>> { | ||
62 | pub(crate) fn leaf_text_ref(self) -> Option<&'a SmolStr> { | ||
63 | let red = unsafe { self.red.get(self.root.syntax_root()) }; | ||
64 | red.green().leaf_text_ref() | ||
65 | } | ||
66 | } | ||
67 | |||
61 | impl<R: TreeRoot> SyntaxNode<R> { | 68 | impl<R: TreeRoot> SyntaxNode<R> { |
62 | pub fn borrowed<'a>(&'a self) -> SyntaxNodeRef<'a> { | 69 | pub fn borrowed<'a>(&'a self) -> SyntaxNodeRef<'a> { |
63 | SyntaxNode { | 70 | SyntaxNode { |
@@ -66,7 +73,7 @@ impl<R: TreeRoot> SyntaxNode<R> { | |||
66 | } | 73 | } |
67 | } | 74 | } |
68 | 75 | ||
69 | pub fn owned<'a>(&'a self) -> SyntaxNode { | 76 | pub fn owned(&self) -> SyntaxNode { |
70 | SyntaxNode { | 77 | SyntaxNode { |
71 | root: self.root.owned(), | 78 | root: self.root.owned(), |
72 | red: self.red, | 79 | red: self.red, |
@@ -82,8 +89,8 @@ impl<R: TreeRoot> SyntaxNode<R> { | |||
82 | TextRange::offset_len(red.start_offset(), red.green().text_len()) | 89 | TextRange::offset_len(red.start_offset(), red.green().text_len()) |
83 | } | 90 | } |
84 | 91 | ||
85 | pub fn text(&self) -> String { | 92 | pub fn text(&self) -> SyntaxText { |
86 | self.red().green().text() | 93 | SyntaxText::new(self.borrowed()) |
87 | } | 94 | } |
88 | 95 | ||
89 | pub fn children(&self) -> impl Iterator<Item = SyntaxNode<R>> { | 96 | pub fn children(&self) -> impl Iterator<Item = SyntaxNode<R>> { |
@@ -91,7 +98,7 @@ impl<R: TreeRoot> SyntaxNode<R> { | |||
91 | let n_children = self.red().n_children(); | 98 | let n_children = self.red().n_children(); |
92 | let root = self.root.clone(); | 99 | let root = self.root.clone(); |
93 | (0..n_children).map(move |i| { | 100 | (0..n_children).map(move |i| { |
94 | let red = unsafe { red.get(&root) }; | 101 | let red = unsafe { red.get(root.syntax_root()) }; |
95 | SyntaxNode { | 102 | SyntaxNode { |
96 | root: root.clone(), | 103 | root: root.clone(), |
97 | red: red.get_child(i).unwrap(), | 104 | red: red.get_child(i).unwrap(), |
@@ -171,7 +178,7 @@ impl<R: TreeRoot> SyntaxNode<R> { | |||
171 | } | 178 | } |
172 | 179 | ||
173 | fn red(&self) -> &RedNode { | 180 | fn red(&self) -> &RedNode { |
174 | unsafe { self.red.get(&self.root) } | 181 | unsafe { self.red.get(self.root.syntax_root()) } |
175 | } | 182 | } |
176 | } | 183 | } |
177 | 184 | ||
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 @@ | |||
1 | use std::{ | ||
2 | fmt, ops, | ||
3 | }; | ||
4 | |||
5 | use { | ||
6 | SyntaxNodeRef, TextRange, TextUnit, | ||
7 | algo::walk::preorder, | ||
8 | text_utils::{intersect, contains_offset_nonstrict}, | ||
9 | }; | ||
10 | |||
11 | #[derive(Clone)] | ||
12 | pub struct SyntaxText<'a> { | ||
13 | node: SyntaxNodeRef<'a>, | ||
14 | range: TextRange, | ||
15 | } | ||
16 | |||
17 | impl<'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 | |||
63 | impl<'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 | |||
69 | impl<'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 | |||
75 | pub trait SyntaxTextSlice: fmt::Debug { | ||
76 | fn restrict(&self, range: TextRange) -> Option<TextRange>; | ||
77 | } | ||
78 | |||
79 | impl SyntaxTextSlice for TextRange { | ||
80 | fn restrict(&self, range: TextRange) -> Option<TextRange> { | ||
81 | intersect(*self, range) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | impl 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 | |||
94 | impl 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 | } | ||