diff options
Diffstat (limited to 'crates/ra_syntax/src/yellow')
-rw-r--r-- | crates/ra_syntax/src/yellow/builder.rs | 64 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/green.rs | 90 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/mod.rs | 100 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/red.rs | 113 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/syntax.rs | 215 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/syntax_text.rs | 122 |
6 files changed, 704 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/yellow/builder.rs b/crates/ra_syntax/src/yellow/builder.rs new file mode 100644 index 000000000..e4ab37899 --- /dev/null +++ b/crates/ra_syntax/src/yellow/builder.rs | |||
@@ -0,0 +1,64 @@ | |||
1 | use { | ||
2 | parser_impl::Sink, | ||
3 | yellow::{GreenNode, SyntaxError}, | ||
4 | SyntaxKind, TextRange, TextUnit, | ||
5 | }; | ||
6 | |||
7 | pub(crate) struct GreenBuilder<'a> { | ||
8 | text: &'a str, | ||
9 | parents: Vec<(SyntaxKind, usize)>, | ||
10 | children: Vec<GreenNode>, | ||
11 | pos: TextUnit, | ||
12 | errors: Vec<SyntaxError>, | ||
13 | } | ||
14 | |||
15 | impl<'a> Sink<'a> for GreenBuilder<'a> { | ||
16 | type Tree = (GreenNode, Vec<SyntaxError>); | ||
17 | |||
18 | fn new(text: &'a str) -> Self { | ||
19 | GreenBuilder { | ||
20 | text, | ||
21 | parents: Vec::new(), | ||
22 | children: Vec::new(), | ||
23 | pos: 0.into(), | ||
24 | errors: Vec::new(), | ||
25 | } | ||
26 | } | ||
27 | |||
28 | fn leaf(&mut self, kind: SyntaxKind, len: TextUnit) { | ||
29 | let range = TextRange::offset_len(self.pos, len); | ||
30 | self.pos += len; | ||
31 | let text = &self.text[range]; | ||
32 | self.children.push( | ||
33 | GreenNode::new_leaf(kind, text) | ||
34 | ); | ||
35 | } | ||
36 | |||
37 | fn start_internal(&mut self, kind: SyntaxKind) { | ||
38 | let len = self.children.len(); | ||
39 | self.parents.push((kind, len)); | ||
40 | } | ||
41 | |||
42 | fn finish_internal(&mut self) { | ||
43 | let (kind, first_child) = self.parents.pop().unwrap(); | ||
44 | let children: Vec<_> = self.children | ||
45 | .drain(first_child..) | ||
46 | .collect(); | ||
47 | self.children.push( | ||
48 | GreenNode::new_branch(kind, children.into_boxed_slice()) | ||
49 | ); | ||
50 | } | ||
51 | |||
52 | fn error(&mut self, message: String) { | ||
53 | self.errors.push(SyntaxError { | ||
54 | msg: message, | ||
55 | offset: self.pos, | ||
56 | }) | ||
57 | } | ||
58 | |||
59 | fn finish(mut self) -> (GreenNode, Vec<SyntaxError>) { | ||
60 | assert_eq!(self.children.len(), 1); | ||
61 | let root = self.children.pop().unwrap(); | ||
62 | (root, self.errors) | ||
63 | } | ||
64 | } | ||
diff --git a/crates/ra_syntax/src/yellow/green.rs b/crates/ra_syntax/src/yellow/green.rs new file mode 100644 index 000000000..8fb691643 --- /dev/null +++ b/crates/ra_syntax/src/yellow/green.rs | |||
@@ -0,0 +1,90 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use smol_str::SmolStr; | ||
4 | |||
5 | use {SyntaxKind, TextUnit}; | ||
6 | |||
7 | #[derive(Clone, Debug)] | ||
8 | pub(crate) enum GreenNode { | ||
9 | Leaf { | ||
10 | kind: SyntaxKind, | ||
11 | text: SmolStr, | ||
12 | }, | ||
13 | Branch(Arc<GreenBranch>), | ||
14 | } | ||
15 | |||
16 | impl GreenNode { | ||
17 | pub(crate) fn new_leaf(kind: SyntaxKind, text: &str) -> GreenNode { | ||
18 | GreenNode::Leaf { kind, text: SmolStr::new(text) } | ||
19 | } | ||
20 | |||
21 | pub(crate) fn new_branch(kind: SyntaxKind, children: Box<[GreenNode]>) -> GreenNode { | ||
22 | GreenNode::Branch(Arc::new(GreenBranch::new(kind, children))) | ||
23 | } | ||
24 | |||
25 | pub fn kind(&self) -> SyntaxKind { | ||
26 | match self { | ||
27 | GreenNode::Leaf { kind, .. } => *kind, | ||
28 | GreenNode::Branch(b) => b.kind(), | ||
29 | } | ||
30 | } | ||
31 | |||
32 | pub fn text_len(&self) -> TextUnit { | ||
33 | match self { | ||
34 | GreenNode::Leaf { text, .. } => TextUnit::from(text.len() as u32), | ||
35 | GreenNode::Branch(b) => b.text_len(), | ||
36 | } | ||
37 | } | ||
38 | |||
39 | pub fn children(&self) -> &[GreenNode] { | ||
40 | match self { | ||
41 | GreenNode::Leaf { .. } => &[], | ||
42 | GreenNode::Branch(b) => b.children(), | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub fn leaf_text_ref(&self) -> Option<&SmolStr> { | ||
47 | match self { | ||
48 | GreenNode::Leaf { text, .. } => Some(text), | ||
49 | GreenNode::Branch(_) => None, | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | #[derive(Clone, Debug)] | ||
55 | pub(crate) struct GreenBranch { | ||
56 | text_len: TextUnit, | ||
57 | kind: SyntaxKind, | ||
58 | children: Box<[GreenNode]>, | ||
59 | } | ||
60 | |||
61 | impl GreenBranch { | ||
62 | fn new(kind: SyntaxKind, children: Box<[GreenNode]>) -> GreenBranch { | ||
63 | let text_len = children.iter().map(|x| x.text_len()).sum::<TextUnit>(); | ||
64 | GreenBranch { | ||
65 | text_len, | ||
66 | kind, | ||
67 | children, | ||
68 | } | ||
69 | } | ||
70 | |||
71 | pub fn kind(&self) -> SyntaxKind { | ||
72 | self.kind | ||
73 | } | ||
74 | |||
75 | pub fn text_len(&self) -> TextUnit { | ||
76 | self.text_len | ||
77 | } | ||
78 | |||
79 | pub fn children(&self) -> &[GreenNode] { | ||
80 | &*self.children | ||
81 | } | ||
82 | } | ||
83 | |||
84 | #[test] | ||
85 | fn test_sizes() { | ||
86 | use std::mem::size_of; | ||
87 | println!("GreenBranch = {}", size_of::<GreenBranch>()); | ||
88 | println!("GreenNode = {}", size_of::<GreenNode>()); | ||
89 | println!("SmolStr = {}", size_of::<SmolStr>()); | ||
90 | } | ||
diff --git a/crates/ra_syntax/src/yellow/mod.rs b/crates/ra_syntax/src/yellow/mod.rs new file mode 100644 index 000000000..0596e702f --- /dev/null +++ b/crates/ra_syntax/src/yellow/mod.rs | |||
@@ -0,0 +1,100 @@ | |||
1 | mod builder; | ||
2 | mod green; | ||
3 | mod red; | ||
4 | mod syntax; | ||
5 | mod syntax_text; | ||
6 | |||
7 | use std::{ | ||
8 | sync::Arc, | ||
9 | ptr, | ||
10 | }; | ||
11 | pub use self::syntax::{SyntaxNode, SyntaxNodeRef, SyntaxError, SyntaxNodeChildren}; | ||
12 | pub(crate) use self::{ | ||
13 | builder::GreenBuilder, | ||
14 | green::GreenNode, | ||
15 | red::RedNode, | ||
16 | syntax_text::SyntaxText, | ||
17 | }; | ||
18 | |||
19 | #[derive(Debug)] | ||
20 | pub struct SyntaxRoot { | ||
21 | red: RedNode, | ||
22 | pub(crate) errors: Vec<SyntaxError>, | ||
23 | } | ||
24 | |||
25 | pub trait TreeRoot: Clone + Send + Sync { | ||
26 | fn borrowed(&self) -> RefRoot; | ||
27 | fn owned(&self) -> OwnedRoot; | ||
28 | |||
29 | #[doc(hidden)] | ||
30 | fn syntax_root(&self) -> &SyntaxRoot; | ||
31 | } | ||
32 | #[derive(Clone, Debug)] | ||
33 | pub struct OwnedRoot(Arc<SyntaxRoot>); | ||
34 | #[derive(Clone, Copy, Debug)] | ||
35 | pub struct RefRoot<'a>(&'a OwnedRoot); // TODO: shared_from_this instead of double indirection | ||
36 | |||
37 | impl<'a> RefRoot<'a> { | ||
38 | fn syntax_root(&self) -> &'a SyntaxRoot { | ||
39 | self.0.syntax_root() | ||
40 | } | ||
41 | } | ||
42 | |||
43 | impl TreeRoot for OwnedRoot { | ||
44 | fn borrowed(&self) -> RefRoot { | ||
45 | RefRoot(&self) | ||
46 | } | ||
47 | fn owned(&self) -> OwnedRoot { | ||
48 | self.clone() | ||
49 | } | ||
50 | |||
51 | fn syntax_root(&self) -> &SyntaxRoot { | ||
52 | &*self.0 | ||
53 | } | ||
54 | } | ||
55 | |||
56 | impl<'a> TreeRoot for RefRoot<'a> { | ||
57 | fn borrowed(&self) -> RefRoot { | ||
58 | *self | ||
59 | } | ||
60 | fn owned(&self) -> OwnedRoot { | ||
61 | self.0.clone() | ||
62 | } | ||
63 | fn syntax_root(&self) -> &SyntaxRoot { | ||
64 | self.0.syntax_root() | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl SyntaxRoot { | ||
69 | pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SyntaxRoot { | ||
70 | SyntaxRoot { | ||
71 | red: RedNode::new_root(green), | ||
72 | errors, | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | |||
77 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
78 | pub(crate) struct RedPtr(ptr::NonNull<RedNode>); | ||
79 | |||
80 | unsafe impl Send for RedPtr {} | ||
81 | |||
82 | unsafe impl Sync for RedPtr {} | ||
83 | |||
84 | impl RedPtr { | ||
85 | fn new(red: &RedNode) -> RedPtr { | ||
86 | RedPtr(red.into()) | ||
87 | } | ||
88 | |||
89 | unsafe fn get<'a>(self, _root: &'a SyntaxRoot) -> &'a RedNode { | ||
90 | &*self.0.as_ptr() | ||
91 | } | ||
92 | } | ||
93 | |||
94 | #[test] | ||
95 | fn assert_send_sync() { | ||
96 | fn f<T: Send + Sync>() {} | ||
97 | f::<GreenNode>(); | ||
98 | f::<RedNode>(); | ||
99 | f::<SyntaxNode>(); | ||
100 | } | ||
diff --git a/crates/ra_syntax/src/yellow/red.rs b/crates/ra_syntax/src/yellow/red.rs new file mode 100644 index 000000000..84cfe4fba --- /dev/null +++ b/crates/ra_syntax/src/yellow/red.rs | |||
@@ -0,0 +1,113 @@ | |||
1 | use parking_lot::RwLock; | ||
2 | use {yellow::{GreenNode, RedPtr}, TextUnit}; | ||
3 | |||
4 | #[derive(Debug)] | ||
5 | pub(crate) struct RedNode { | ||
6 | green: GreenNode, | ||
7 | parent: Option<ParentData>, | ||
8 | children: RwLock<Box<[RedChild]>>, | ||
9 | } | ||
10 | |||
11 | #[derive(Debug)] | ||
12 | enum RedChild { | ||
13 | Zigot(TextUnit), | ||
14 | Child(RedNode) | ||
15 | } | ||
16 | |||
17 | impl RedChild { | ||
18 | fn set(&mut self, node: RedNode) -> &RedNode { | ||
19 | match self { | ||
20 | RedChild::Child(node) => return node, | ||
21 | RedChild::Zigot(_) => { | ||
22 | *self = RedChild::Child(node); | ||
23 | match self { | ||
24 | RedChild::Child(node) => return node, | ||
25 | RedChild::Zigot(_) => unreachable!() | ||
26 | } | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | |||
32 | #[derive(Debug)] | ||
33 | struct ParentData { | ||
34 | parent: RedPtr, | ||
35 | start_offset: TextUnit, | ||
36 | index_in_parent: usize, | ||
37 | } | ||
38 | |||
39 | impl RedNode { | ||
40 | pub fn new_root(green: GreenNode) -> RedNode { | ||
41 | RedNode::new(green, None) | ||
42 | } | ||
43 | |||
44 | fn new_child( | ||
45 | green: GreenNode, | ||
46 | parent: RedPtr, | ||
47 | start_offset: TextUnit, | ||
48 | index_in_parent: usize, | ||
49 | ) -> RedNode { | ||
50 | let parent_data = ParentData { | ||
51 | parent, | ||
52 | start_offset, | ||
53 | index_in_parent, | ||
54 | }; | ||
55 | RedNode::new(green, Some(parent_data)) | ||
56 | } | ||
57 | |||
58 | fn new(green: GreenNode, parent: Option<ParentData>) -> RedNode { | ||
59 | let mut start_offset = parent.as_ref().map(|it| it.start_offset).unwrap_or(0.into()); | ||
60 | let children = green.children() | ||
61 | .iter() | ||
62 | .map(|child| { | ||
63 | let off = start_offset; | ||
64 | start_offset += child.text_len(); | ||
65 | RedChild::Zigot(off) | ||
66 | }) | ||
67 | .collect::<Vec<_>>() | ||
68 | .into_boxed_slice(); | ||
69 | RedNode { | ||
70 | green, | ||
71 | parent, | ||
72 | children: RwLock::new(children), | ||
73 | } | ||
74 | } | ||
75 | |||
76 | pub(crate) fn green(&self) -> &GreenNode { | ||
77 | &self.green | ||
78 | } | ||
79 | |||
80 | pub(crate) fn start_offset(&self) -> TextUnit { | ||
81 | match &self.parent { | ||
82 | None => 0.into(), | ||
83 | Some(p) => p.start_offset, | ||
84 | } | ||
85 | } | ||
86 | |||
87 | pub(crate) fn n_children(&self) -> usize { | ||
88 | self.green.children().len() | ||
89 | } | ||
90 | |||
91 | pub(crate) fn get_child(&self, idx: usize) -> Option<RedPtr> { | ||
92 | if idx >= self.n_children() { | ||
93 | return None; | ||
94 | } | ||
95 | let start_offset = match &self.children.read()[idx] { | ||
96 | RedChild::Child(child) => return Some(RedPtr::new(child)), | ||
97 | RedChild::Zigot(start_offset) => *start_offset, | ||
98 | }; | ||
99 | let green_children = self.green.children(); | ||
100 | let child = | ||
101 | RedNode::new_child(green_children[idx].clone(), RedPtr::new(self), start_offset, idx); | ||
102 | let mut children = self.children.write(); | ||
103 | let child = children[idx].set(child); | ||
104 | Some(RedPtr::new(child)) | ||
105 | } | ||
106 | |||
107 | pub(crate) fn parent(&self) -> Option<RedPtr> { | ||
108 | Some(self.parent.as_ref()?.parent) | ||
109 | } | ||
110 | pub(crate) fn index_in_parent(&self) -> Option<usize> { | ||
111 | Some(self.parent.as_ref()?.index_in_parent) | ||
112 | } | ||
113 | } | ||
diff --git a/crates/ra_syntax/src/yellow/syntax.rs b/crates/ra_syntax/src/yellow/syntax.rs new file mode 100644 index 000000000..1d99cab4a --- /dev/null +++ b/crates/ra_syntax/src/yellow/syntax.rs | |||
@@ -0,0 +1,215 @@ | |||
1 | use std::{ | ||
2 | fmt, sync::Arc, | ||
3 | hash::{Hasher, Hash}, | ||
4 | ops::Range, | ||
5 | }; | ||
6 | |||
7 | use smol_str::SmolStr; | ||
8 | |||
9 | use { | ||
10 | yellow::{GreenNode, RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot, SyntaxText}, | ||
11 | SyntaxKind::{self, *}, | ||
12 | TextRange, TextUnit, | ||
13 | }; | ||
14 | |||
15 | |||
16 | #[derive(Clone, Copy)] | ||
17 | pub struct SyntaxNode<R: TreeRoot = OwnedRoot> { | ||
18 | pub(crate) root: R, | ||
19 | // Guaranteed to not dangle, because `root` holds a | ||
20 | // strong reference to red's ancestor | ||
21 | red: RedPtr, | ||
22 | } | ||
23 | |||
24 | unsafe impl<R: TreeRoot> Send for SyntaxNode<R> {} | ||
25 | unsafe impl<R: TreeRoot> Sync for SyntaxNode<R> {} | ||
26 | |||
27 | impl<R1: TreeRoot, R2: TreeRoot> PartialEq<SyntaxNode<R1>> for SyntaxNode<R2> { | ||
28 | fn eq(&self, other: &SyntaxNode<R1>) -> bool { | ||
29 | self.red == other.red | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl<R: TreeRoot> Eq for SyntaxNode<R> {} | ||
34 | impl<R: TreeRoot> Hash for SyntaxNode<R> { | ||
35 | fn hash<H: Hasher>(&self, state: &mut H) { | ||
36 | self.red.hash(state) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub type SyntaxNodeRef<'a> = SyntaxNode<RefRoot<'a>>; | ||
41 | |||
42 | #[test] | ||
43 | fn syntax_node_ref_is_copy() { | ||
44 | fn assert_copy<T: Copy>(){} | ||
45 | assert_copy::<SyntaxNodeRef>() | ||
46 | } | ||
47 | |||
48 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
49 | pub struct SyntaxError { | ||
50 | pub msg: String, | ||
51 | pub offset: TextUnit, | ||
52 | } | ||
53 | |||
54 | impl SyntaxNode<OwnedRoot> { | ||
55 | pub(crate) fn new_owned(root: SyntaxRoot) -> Self { | ||
56 | let root = OwnedRoot(Arc::new(root)); | ||
57 | let red = RedPtr::new(&root.syntax_root().red); | ||
58 | SyntaxNode { root, red } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | impl<'a> SyntaxNode<RefRoot<'a>> { | ||
63 | pub(crate) fn leaf_text_ref(self) -> Option<&'a SmolStr> { | ||
64 | let red = unsafe { self.red.get(self.root.syntax_root()) }; | ||
65 | red.green().leaf_text_ref() | ||
66 | } | ||
67 | } | ||
68 | |||
69 | impl<R: TreeRoot> SyntaxNode<R> { | ||
70 | pub fn borrowed<'a>(&'a self) -> SyntaxNodeRef<'a> { | ||
71 | SyntaxNode { | ||
72 | root: self.root.borrowed(), | ||
73 | red: self.red, | ||
74 | } | ||
75 | } | ||
76 | |||
77 | pub fn owned(&self) -> SyntaxNode { | ||
78 | SyntaxNode { | ||
79 | root: self.root.owned(), | ||
80 | red: self.red, | ||
81 | } | ||
82 | } | ||
83 | |||
84 | pub fn kind(&self) -> SyntaxKind { | ||
85 | self.red().green().kind() | ||
86 | } | ||
87 | |||
88 | pub fn range(&self) -> TextRange { | ||
89 | let red = self.red(); | ||
90 | TextRange::offset_len(red.start_offset(), red.green().text_len()) | ||
91 | } | ||
92 | |||
93 | pub fn text(&self) -> SyntaxText { | ||
94 | SyntaxText::new(self.borrowed()) | ||
95 | } | ||
96 | |||
97 | pub fn children(&self) -> SyntaxNodeChildren<R> { | ||
98 | SyntaxNodeChildren { | ||
99 | parent: self.clone(), | ||
100 | iter: (0..self.red().n_children()) | ||
101 | } | ||
102 | } | ||
103 | |||
104 | pub fn parent(&self) -> Option<SyntaxNode<R>> { | ||
105 | let parent = self.red().parent()?; | ||
106 | Some(SyntaxNode { | ||
107 | root: self.root.clone(), | ||
108 | red: parent, | ||
109 | }) | ||
110 | } | ||
111 | |||
112 | pub fn first_child(&self) -> Option<SyntaxNode<R>> { | ||
113 | let red = self.red().get_child(0)?; | ||
114 | Some(SyntaxNode { root: self.root.clone(), red }) | ||
115 | } | ||
116 | |||
117 | pub fn last_child(&self) -> Option<SyntaxNode<R>> { | ||
118 | let n = self.red().n_children(); | ||
119 | let n = n.checked_sub(1)?; | ||
120 | let red = self.red().get_child(n)?; | ||
121 | Some(SyntaxNode { root: self.root.clone(), red }) | ||
122 | } | ||
123 | |||
124 | pub fn next_sibling(&self) -> Option<SyntaxNode<R>> { | ||
125 | let red = self.red(); | ||
126 | let parent = self.parent()?; | ||
127 | let next_sibling_idx = red.index_in_parent()? + 1; | ||
128 | let sibling_red = parent.red().get_child(next_sibling_idx)?; | ||
129 | Some(SyntaxNode { | ||
130 | root: self.root.clone(), | ||
131 | red: sibling_red, | ||
132 | }) | ||
133 | } | ||
134 | |||
135 | pub fn prev_sibling(&self) -> Option<SyntaxNode<R>> { | ||
136 | let red = self.red(); | ||
137 | let parent = self.parent()?; | ||
138 | let prev_sibling_idx = red.index_in_parent()?.checked_sub(1)?; | ||
139 | let sibling_red = parent.red().get_child(prev_sibling_idx)?; | ||
140 | Some(SyntaxNode { | ||
141 | root: self.root.clone(), | ||
142 | red: sibling_red, | ||
143 | }) | ||
144 | } | ||
145 | |||
146 | pub fn is_leaf(&self) -> bool { | ||
147 | self.first_child().is_none() | ||
148 | } | ||
149 | |||
150 | pub fn leaf_text(&self) -> Option<SmolStr> { | ||
151 | self.borrowed().leaf_text_ref().map(|it| it.clone()) | ||
152 | } | ||
153 | |||
154 | pub(crate) fn replace_with(&self, green: GreenNode) -> GreenNode { | ||
155 | assert_eq!(self.kind(), green.kind()); | ||
156 | match self.parent() { | ||
157 | None => green, | ||
158 | Some(parent) => { | ||
159 | let children: Vec<_> = parent.children().map(|child| { | ||
160 | if child == *self { | ||
161 | green.clone() | ||
162 | } else { | ||
163 | child.red().green().clone() | ||
164 | } | ||
165 | }).collect(); | ||
166 | let new_parent = GreenNode::new_branch( | ||
167 | parent.kind(), | ||
168 | children.into_boxed_slice(), | ||
169 | ); | ||
170 | parent.replace_with(new_parent) | ||
171 | }, | ||
172 | } | ||
173 | } | ||
174 | |||
175 | fn red(&self) -> &RedNode { | ||
176 | unsafe { self.red.get(self.root.syntax_root()) } | ||
177 | } | ||
178 | } | ||
179 | |||
180 | impl<R: TreeRoot> fmt::Debug for SyntaxNode<R> { | ||
181 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
182 | write!(fmt, "{:?}@{:?}", self.kind(), self.range())?; | ||
183 | if has_short_text(self.kind()) { | ||
184 | write!(fmt, " \"{}\"", self.text())?; | ||
185 | } | ||
186 | Ok(()) | ||
187 | } | ||
188 | } | ||
189 | |||
190 | #[derive(Debug)] | ||
191 | pub struct SyntaxNodeChildren<R: TreeRoot> { | ||
192 | parent: SyntaxNode<R>, | ||
193 | iter: Range<usize>, | ||
194 | } | ||
195 | |||
196 | impl<R: TreeRoot> Iterator for SyntaxNodeChildren<R> { | ||
197 | type Item = SyntaxNode<R>; | ||
198 | |||
199 | fn next(&mut self) -> Option<SyntaxNode<R>> { | ||
200 | self.iter.next().map(|i| { | ||
201 | let red = self.parent.red(); | ||
202 | SyntaxNode { | ||
203 | root: self.parent.root.clone(), | ||
204 | red: red.get_child(i).unwrap(), | ||
205 | } | ||
206 | }) | ||
207 | } | ||
208 | } | ||
209 | |||
210 | fn has_short_text(kind: SyntaxKind) -> bool { | ||
211 | match kind { | ||
212 | IDENT | LIFETIME | INT_NUMBER | FLOAT_NUMBER => true, | ||
213 | _ => false, | ||
214 | } | ||
215 | } | ||
diff --git a/crates/ra_syntax/src/yellow/syntax_text.rs b/crates/ra_syntax/src/yellow/syntax_text.rs new file mode 100644 index 000000000..280bedd78 --- /dev/null +++ b/crates/ra_syntax/src/yellow/syntax_text.rs | |||
@@ -0,0 +1,122 @@ | |||
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 push_to(&self, buf: &mut String) { | ||
35 | self.chunks().for_each(|it| buf.push_str(it)); | ||
36 | } | ||
37 | pub fn to_string(&self) -> String { | ||
38 | self.chunks().collect() | ||
39 | } | ||
40 | pub fn contains(&self, c: char) -> bool { | ||
41 | self.chunks().any(|it| it.contains(c)) | ||
42 | } | ||
43 | pub fn find(&self, c: char) -> Option<TextUnit> { | ||
44 | let mut acc: TextUnit = 0.into(); | ||
45 | for chunk in self.chunks() { | ||
46 | if let Some(pos) = chunk.find(c) { | ||
47 | let pos: TextUnit = (pos as u32).into(); | ||
48 | return Some(acc + pos); | ||
49 | } | ||
50 | acc += TextUnit::of_str(chunk); | ||
51 | } | ||
52 | None | ||
53 | } | ||
54 | pub fn len(&self) -> TextUnit { | ||
55 | self.range.len() | ||
56 | } | ||
57 | pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> { | ||
58 | let range = range.restrict(self.range) | ||
59 | .unwrap_or_else(|| { | ||
60 | panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range) | ||
61 | }); | ||
62 | SyntaxText { node: self.node, range } | ||
63 | } | ||
64 | pub fn char_at(&self, offset: TextUnit) -> Option<char> { | ||
65 | let mut start: TextUnit = 0.into(); | ||
66 | for chunk in self.chunks() { | ||
67 | let end = start + TextUnit::of_str(chunk); | ||
68 | if start <= offset && offset < end { | ||
69 | let off: usize = u32::from(offset - start) as usize; | ||
70 | return Some(chunk[off..].chars().next().unwrap()); | ||
71 | } | ||
72 | start = end; | ||
73 | } | ||
74 | None | ||
75 | } | ||
76 | } | ||
77 | |||
78 | impl<'a> fmt::Debug for SyntaxText<'a> { | ||
79 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
80 | fmt::Debug::fmt(&self.to_string(), f) | ||
81 | } | ||
82 | } | ||
83 | |||
84 | impl<'a> fmt::Display for SyntaxText<'a> { | ||
85 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
86 | fmt::Display::fmt(&self.to_string(), f) | ||
87 | } | ||
88 | } | ||
89 | |||
90 | pub trait SyntaxTextSlice: fmt::Debug { | ||
91 | fn restrict(&self, range: TextRange) -> Option<TextRange>; | ||
92 | } | ||
93 | |||
94 | impl SyntaxTextSlice for TextRange { | ||
95 | fn restrict(&self, range: TextRange) -> Option<TextRange> { | ||
96 | intersect(*self, range) | ||
97 | } | ||
98 | } | ||
99 | |||
100 | impl SyntaxTextSlice for ops::RangeTo<TextUnit> { | ||
101 | fn restrict(&self, range: TextRange) -> Option<TextRange> { | ||
102 | if !contains_offset_nonstrict(range, self.end) { | ||
103 | return None; | ||
104 | } | ||
105 | Some(TextRange::from_to(range.start(), self.end)) | ||
106 | } | ||
107 | } | ||
108 | |||
109 | impl SyntaxTextSlice for ops::RangeFrom<TextUnit> { | ||
110 | fn restrict(&self, range: TextRange) -> Option<TextRange> { | ||
111 | if !contains_offset_nonstrict(range, self.start) { | ||
112 | return None; | ||
113 | } | ||
114 | Some(TextRange::from_to(self.start, range.end())) | ||
115 | } | ||
116 | } | ||
117 | |||
118 | impl SyntaxTextSlice for ops::Range<TextUnit> { | ||
119 | fn restrict(&self, range: TextRange) -> Option<TextRange> { | ||
120 | TextRange::from_to(self.start, self.end).restrict(range) | ||
121 | } | ||
122 | } | ||