aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_editor/src')
-rw-r--r--crates/ra_editor/src/lib.rs4
-rw-r--r--crates/ra_editor/src/structure.rs129
-rw-r--r--crates/ra_editor/src/symbols.rs246
3 files changed, 131 insertions, 248 deletions
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index b03f9ea54..bfc745e58 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -3,7 +3,7 @@ mod extend_selection;
3mod folding_ranges; 3mod folding_ranges;
4mod line_index; 4mod line_index;
5mod line_index_utils; 5mod line_index_utils;
6mod symbols; 6mod structure;
7#[cfg(test)] 7#[cfg(test)]
8mod test_utils; 8mod test_utils;
9mod typing; 9mod typing;
@@ -15,7 +15,7 @@ pub use self::{
15 folding_ranges::{folding_ranges, Fold, FoldKind}, 15 folding_ranges::{folding_ranges, Fold, FoldKind},
16 line_index::{LineCol, LineIndex}, 16 line_index::{LineCol, LineIndex},
17 line_index_utils::translate_offset_with_edit, 17 line_index_utils::translate_offset_with_edit,
18 symbols::{file_structure, file_symbols, FileSymbol, StructureNode}, 18 structure::{file_structure, StructureNode},
19 typing::{join_lines, on_enter, on_eq_typed}, 19 typing::{join_lines, on_enter, on_eq_typed},
20 diagnostics::diagnostics 20 diagnostics::diagnostics
21}; 21};
diff --git a/crates/ra_editor/src/structure.rs b/crates/ra_editor/src/structure.rs
new file mode 100644
index 000000000..2292b1ddf
--- /dev/null
+++ b/crates/ra_editor/src/structure.rs
@@ -0,0 +1,129 @@
1use crate::TextRange;
2
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 ast::{self, NameOwner},
6 AstNode, SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent,
7};
8
9#[derive(Debug, Clone)]
10pub struct StructureNode {
11 pub parent: Option<usize>,
12 pub label: String,
13 pub navigation_range: TextRange,
14 pub node_range: TextRange,
15 pub kind: SyntaxKind,
16}
17
18pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> {
19 let mut res = Vec::new();
20 let mut stack = Vec::new();
21
22 for event in file.syntax().preorder() {
23 match event {
24 WalkEvent::Enter(node) => {
25 if let Some(mut symbol) = structure_node(node) {
26 symbol.parent = stack.last().map(|&n| n);
27 stack.push(res.len());
28 res.push(symbol);
29 }
30 }
31 WalkEvent::Leave(node) => {
32 if structure_node(node).is_some() {
33 stack.pop().unwrap();
34 }
35 }
36 }
37 }
38 res
39}
40
41fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
42 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> {
43 let name = node.name()?;
44 Some(StructureNode {
45 parent: None,
46 label: name.text().to_string(),
47 navigation_range: name.syntax().range(),
48 node_range: node.syntax().range(),
49 kind: node.syntax().kind(),
50 })
51 }
52
53 visitor()
54 .visit(decl::<ast::FnDef>)
55 .visit(decl::<ast::StructDef>)
56 .visit(decl::<ast::NamedFieldDef>)
57 .visit(decl::<ast::EnumDef>)
58 .visit(decl::<ast::TraitDef>)
59 .visit(decl::<ast::Module>)
60 .visit(decl::<ast::TypeDef>)
61 .visit(decl::<ast::ConstDef>)
62 .visit(decl::<ast::StaticDef>)
63 .visit(|im: ast::ImplItem| {
64 let target_type = im.target_type()?;
65 let target_trait = im.target_trait();
66 let label = match target_trait {
67 None => format!("impl {}", target_type.syntax().text()),
68 Some(t) => format!(
69 "impl {} for {}",
70 t.syntax().text(),
71 target_type.syntax().text(),
72 ),
73 };
74
75 let node = StructureNode {
76 parent: None,
77 label,
78 navigation_range: target_type.syntax().range(),
79 node_range: im.syntax().range(),
80 kind: im.syntax().kind(),
81 };
82 Some(node)
83 })
84 .accept(node)?
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use test_utils::assert_eq_dbg;
91
92 #[test]
93 fn test_file_structure() {
94 let file = SourceFileNode::parse(
95 r#"
96struct Foo {
97 x: i32
98}
99
100mod m {
101 fn bar() {}
102}
103
104enum E { X, Y(i32) }
105type T = ();
106static S: i32 = 92;
107const C: i32 = 92;
108
109impl E {}
110
111impl fmt::Debug for E {}
112"#,
113 );
114 let structure = file_structure(&file);
115 assert_eq_dbg(
116 r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF },
117 StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF },
118 StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE },
119 StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF },
120 StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF },
121 StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF },
122 StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF },
123 StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF },
124 StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM },
125 StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#,
126 &structure,
127 )
128 }
129}
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs
deleted file mode 100644
index 9e25decfb..000000000
--- a/crates/ra_editor/src/symbols.rs
+++ /dev/null
@@ -1,246 +0,0 @@
1use crate::TextRange;
2
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 ast::{self, DocCommentsOwner, NameOwner},
6 AstNode, SourceFileNode, SmolStr, SyntaxKind, SyntaxNodeRef, WalkEvent,
7};
8
9#[derive(Debug, Clone)]
10pub struct StructureNode {
11 pub parent: Option<usize>,
12 pub label: String,
13 pub navigation_range: TextRange,
14 pub node_range: TextRange,
15 pub kind: SyntaxKind,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub struct FileSymbol {
20 pub name: SmolStr,
21 pub node_range: TextRange,
22 pub kind: SyntaxKind,
23}
24
25impl FileSymbol {
26 pub fn docs(&self, file: &SourceFileNode) -> Option<String> {
27 file.syntax()
28 .descendants()
29 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
30 .filter_map(|node: SyntaxNodeRef| {
31 fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> {
32 let comments = node.doc_comment_text();
33 if comments.is_empty() {
34 None
35 } else {
36 Some(comments)
37 }
38 }
39
40 visitor()
41 .visit(doc_comments::<ast::FnDef>)
42 .visit(doc_comments::<ast::StructDef>)
43 .visit(doc_comments::<ast::EnumDef>)
44 .visit(doc_comments::<ast::TraitDef>)
45 .visit(doc_comments::<ast::Module>)
46 .visit(doc_comments::<ast::TypeDef>)
47 .visit(doc_comments::<ast::ConstDef>)
48 .visit(doc_comments::<ast::StaticDef>)
49 .accept(node)?
50 })
51 .nth(0)
52 }
53 /// Get a description of this node.
54 ///
55 /// e.g. `struct Name`, `enum Name`, `fn Name`
56 pub fn description(&self, file: &SourceFileNode) -> Option<String> {
57 // TODO: After type inference is done, add type information to improve the output
58 file.syntax()
59 .descendants()
60 .filter(|node| node.kind() == self.kind && node.range() == self.node_range)
61 .filter_map(|node: SyntaxNodeRef| {
62 // TODO: Refactor to be have less repetition
63 visitor()
64 .visit(|node: ast::FnDef| {
65 let mut string = "fn ".to_string();
66 node.name()?.syntax().text().push_to(&mut string);
67 Some(string)
68 })
69 .visit(|node: ast::StructDef| {
70 let mut string = "struct ".to_string();
71 node.name()?.syntax().text().push_to(&mut string);
72 Some(string)
73 })
74 .visit(|node: ast::EnumDef| {
75 let mut string = "enum ".to_string();
76 node.name()?.syntax().text().push_to(&mut string);
77 Some(string)
78 })
79 .visit(|node: ast::TraitDef| {
80 let mut string = "trait ".to_string();
81 node.name()?.syntax().text().push_to(&mut string);
82 Some(string)
83 })
84 .visit(|node: ast::Module| {
85 let mut string = "mod ".to_string();
86 node.name()?.syntax().text().push_to(&mut string);
87 Some(string)
88 })
89 .visit(|node: ast::TypeDef| {
90 let mut string = "type ".to_string();
91 node.name()?.syntax().text().push_to(&mut string);
92 Some(string)
93 })
94 .visit(|node: ast::ConstDef| {
95 let mut string = "const ".to_string();
96 node.name()?.syntax().text().push_to(&mut string);
97 Some(string)
98 })
99 .visit(|node: ast::StaticDef| {
100 let mut string = "static ".to_string();
101 node.name()?.syntax().text().push_to(&mut string);
102 Some(string)
103 })
104 .accept(node)?
105 })
106 .nth(0)
107 }
108}
109
110pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> {
111 file.syntax().descendants().filter_map(to_symbol).collect()
112}
113
114fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
115 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> {
116 let name = node.name()?;
117 Some(FileSymbol {
118 name: name.text(),
119 node_range: node.syntax().range(),
120 kind: node.syntax().kind(),
121 })
122 }
123 visitor()
124 .visit(decl::<ast::FnDef>)
125 .visit(decl::<ast::StructDef>)
126 .visit(decl::<ast::EnumDef>)
127 .visit(decl::<ast::TraitDef>)
128 .visit(decl::<ast::Module>)
129 .visit(decl::<ast::TypeDef>)
130 .visit(decl::<ast::ConstDef>)
131 .visit(decl::<ast::StaticDef>)
132 .accept(node)?
133}
134
135pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> {
136 let mut res = Vec::new();
137 let mut stack = Vec::new();
138
139 for event in file.syntax().preorder() {
140 match event {
141 WalkEvent::Enter(node) => {
142 if let Some(mut symbol) = structure_node(node) {
143 symbol.parent = stack.last().map(|&n| n);
144 stack.push(res.len());
145 res.push(symbol);
146 }
147 }
148 WalkEvent::Leave(node) => {
149 if structure_node(node).is_some() {
150 stack.pop().unwrap();
151 }
152 }
153 }
154 }
155 res
156}
157
158fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
159 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> {
160 let name = node.name()?;
161 Some(StructureNode {
162 parent: None,
163 label: name.text().to_string(),
164 navigation_range: name.syntax().range(),
165 node_range: node.syntax().range(),
166 kind: node.syntax().kind(),
167 })
168 }
169
170 visitor()
171 .visit(decl::<ast::FnDef>)
172 .visit(decl::<ast::StructDef>)
173 .visit(decl::<ast::NamedFieldDef>)
174 .visit(decl::<ast::EnumDef>)
175 .visit(decl::<ast::TraitDef>)
176 .visit(decl::<ast::Module>)
177 .visit(decl::<ast::TypeDef>)
178 .visit(decl::<ast::ConstDef>)
179 .visit(decl::<ast::StaticDef>)
180 .visit(|im: ast::ImplItem| {
181 let target_type = im.target_type()?;
182 let target_trait = im.target_trait();
183 let label = match target_trait {
184 None => format!("impl {}", target_type.syntax().text()),
185 Some(t) => format!(
186 "impl {} for {}",
187 t.syntax().text(),
188 target_type.syntax().text(),
189 ),
190 };
191
192 let node = StructureNode {
193 parent: None,
194 label,
195 navigation_range: target_type.syntax().range(),
196 node_range: im.syntax().range(),
197 kind: im.syntax().kind(),
198 };
199 Some(node)
200 })
201 .accept(node)?
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use test_utils::assert_eq_dbg;
208
209 #[test]
210 fn test_file_structure() {
211 let file = SourceFileNode::parse(
212 r#"
213struct Foo {
214 x: i32
215}
216
217mod m {
218 fn bar() {}
219}
220
221enum E { X, Y(i32) }
222type T = ();
223static S: i32 = 92;
224const C: i32 = 92;
225
226impl E {}
227
228impl fmt::Debug for E {}
229"#,
230 );
231 let symbols = file_structure(&file);
232 assert_eq_dbg(
233 r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF },
234 StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF },
235 StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE },
236 StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF },
237 StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF },
238 StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF },
239 StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF },
240 StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF },
241 StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM },
242 StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#,
243 &symbols,
244 )
245 }
246}