aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/display/structure.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/display/structure.rs')
-rw-r--r--crates/ra_ide/src/display/structure.rs401
1 files changed, 401 insertions, 0 deletions
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
new file mode 100644
index 000000000..a80d65ac7
--- /dev/null
+++ b/crates/ra_ide/src/display/structure.rs
@@ -0,0 +1,401 @@
1//! FIXME: write short doc here
2
3use crate::TextRange;
4
5use ra_syntax::{
6 ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
7 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent,
8};
9
10#[derive(Debug, Clone)]
11pub struct StructureNode {
12 pub parent: Option<usize>,
13 pub label: String,
14 pub navigation_range: TextRange,
15 pub node_range: TextRange,
16 pub kind: SyntaxKind,
17 pub detail: Option<String>,
18 pub deprecated: bool,
19}
20
21pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
22 let mut res = Vec::new();
23 let mut stack = Vec::new();
24
25 for event in file.syntax().preorder() {
26 match event {
27 WalkEvent::Enter(node) => {
28 if let Some(mut symbol) = structure_node(&node) {
29 symbol.parent = stack.last().copied();
30 stack.push(res.len());
31 res.push(symbol);
32 }
33 }
34 WalkEvent::Leave(node) => {
35 if structure_node(&node).is_some() {
36 stack.pop().unwrap();
37 }
38 }
39 }
40 }
41 res
42}
43
44fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
45 fn decl<N: NameOwner + AttrsOwner>(node: N) -> Option<StructureNode> {
46 decl_with_detail(node, None)
47 }
48
49 fn decl_with_ascription<N: NameOwner + AttrsOwner + TypeAscriptionOwner>(
50 node: N,
51 ) -> Option<StructureNode> {
52 let ty = node.ascribed_type();
53 decl_with_type_ref(node, ty)
54 }
55
56 fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
57 node: N,
58 type_ref: Option<ast::TypeRef>,
59 ) -> Option<StructureNode> {
60 let detail = type_ref.map(|type_ref| {
61 let mut detail = String::new();
62 collapse_ws(type_ref.syntax(), &mut detail);
63 detail
64 });
65 decl_with_detail(node, detail)
66 }
67
68 fn decl_with_detail<N: NameOwner + AttrsOwner>(
69 node: N,
70 detail: Option<String>,
71 ) -> Option<StructureNode> {
72 let name = node.name()?;
73
74 Some(StructureNode {
75 parent: None,
76 label: name.text().to_string(),
77 navigation_range: name.syntax().text_range(),
78 node_range: node.syntax().text_range(),
79 kind: node.syntax().kind(),
80 detail,
81 deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"),
82 })
83 }
84
85 fn collapse_ws(node: &SyntaxNode, output: &mut String) {
86 let mut can_insert_ws = false;
87 node.text().for_each_chunk(|chunk| {
88 for line in chunk.lines() {
89 let line = line.trim();
90 if line.is_empty() {
91 if can_insert_ws {
92 output.push(' ');
93 can_insert_ws = false;
94 }
95 } else {
96 output.push_str(line);
97 can_insert_ws = true;
98 }
99 }
100 })
101 }
102
103 match_ast! {
104 match node {
105 ast::FnDef(it) => {
106 let mut detail = String::from("fn");
107 if let Some(type_param_list) = it.type_param_list() {
108 collapse_ws(type_param_list.syntax(), &mut detail);
109 }
110 if let Some(param_list) = it.param_list() {
111 collapse_ws(param_list.syntax(), &mut detail);
112 }
113 if let Some(ret_type) = it.ret_type() {
114 detail.push_str(" ");
115 collapse_ws(ret_type.syntax(), &mut detail);
116 }
117
118 decl_with_detail(it, Some(detail))
119 },
120 ast::StructDef(it) => { decl(it) },
121 ast::EnumDef(it) => { decl(it) },
122 ast::EnumVariant(it) => { decl(it) },
123 ast::TraitDef(it) => { decl(it) },
124 ast::Module(it) => { decl(it) },
125 ast::TypeAliasDef(it) => {
126 let ty = it.type_ref();
127 decl_with_type_ref(it, ty)
128 },
129 ast::RecordFieldDef(it) => { decl_with_ascription(it) },
130 ast::ConstDef(it) => { decl_with_ascription(it) },
131 ast::StaticDef(it) => { decl_with_ascription(it) },
132 ast::ImplBlock(it) => {
133 let target_type = it.target_type()?;
134 let target_trait = it.target_trait();
135 let label = match target_trait {
136 None => format!("impl {}", target_type.syntax().text()),
137 Some(t) => {
138 format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),)
139 }
140 };
141
142 let node = StructureNode {
143 parent: None,
144 label,
145 navigation_range: target_type.syntax().text_range(),
146 node_range: it.syntax().text_range(),
147 kind: it.syntax().kind(),
148 detail: None,
149 deprecated: false,
150 };
151 Some(node)
152 },
153 ast::MacroCall(it) => {
154 let first_token = it.syntax().first_token().unwrap();
155 if first_token.text().as_str() != "macro_rules" {
156 return None;
157 }
158 decl(it)
159 },
160 _ => None,
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use insta::assert_debug_snapshot;
169
170 #[test]
171 fn test_file_structure() {
172 let file = SourceFile::parse(
173 r#"
174struct Foo {
175 x: i32
176}
177
178mod m {
179 fn bar1() {}
180 fn bar2<T>(t: T) -> T {}
181 fn bar3<A,
182 B>(a: A,
183 b: B) -> Vec<
184 u32
185 > {}
186}
187
188enum E { X, Y(i32) }
189type T = ();
190static S: i32 = 92;
191const C: i32 = 92;
192
193impl E {}
194
195impl fmt::Debug for E {}
196
197macro_rules! mc {
198 () => {}
199}
200
201#[deprecated]
202fn obsolete() {}
203
204#[deprecated(note = "for awhile")]
205fn very_obsolete() {}
206"#,
207 )
208 .ok()
209 .unwrap();
210 let structure = file_structure(&file);
211 assert_debug_snapshot!(structure,
212 @r###"
213 [
214 StructureNode {
215 parent: None,
216 label: "Foo",
217 navigation_range: [8; 11),
218 node_range: [1; 26),
219 kind: STRUCT_DEF,
220 detail: None,
221 deprecated: false,
222 },
223 StructureNode {
224 parent: Some(
225 0,
226 ),
227 label: "x",
228 navigation_range: [18; 19),
229 node_range: [18; 24),
230 kind: RECORD_FIELD_DEF,
231 detail: Some(
232 "i32",
233 ),
234 deprecated: false,
235 },
236 StructureNode {
237 parent: None,
238 label: "m",
239 navigation_range: [32; 33),
240 node_range: [28; 158),
241 kind: MODULE,
242 detail: None,
243 deprecated: false,
244 },
245 StructureNode {
246 parent: Some(
247 2,
248 ),
249 label: "bar1",
250 navigation_range: [43; 47),
251 node_range: [40; 52),
252 kind: FN_DEF,
253 detail: Some(
254 "fn()",
255 ),
256 deprecated: false,
257 },
258 StructureNode {
259 parent: Some(
260 2,
261 ),
262 label: "bar2",
263 navigation_range: [60; 64),
264 node_range: [57; 81),
265 kind: FN_DEF,
266 detail: Some(
267 "fn<T>(t: T) -> T",
268 ),
269 deprecated: false,
270 },
271 StructureNode {
272 parent: Some(
273 2,
274 ),
275 label: "bar3",
276 navigation_range: [89; 93),
277 node_range: [86; 156),
278 kind: FN_DEF,
279 detail: Some(
280 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
281 ),
282 deprecated: false,
283 },
284 StructureNode {
285 parent: None,
286 label: "E",
287 navigation_range: [165; 166),
288 node_range: [160; 180),
289 kind: ENUM_DEF,
290 detail: None,
291 deprecated: false,
292 },
293 StructureNode {
294 parent: Some(
295 6,
296 ),
297 label: "X",
298 navigation_range: [169; 170),
299 node_range: [169; 170),
300 kind: ENUM_VARIANT,
301 detail: None,
302 deprecated: false,
303 },
304 StructureNode {
305 parent: Some(
306 6,
307 ),
308 label: "Y",
309 navigation_range: [172; 173),
310 node_range: [172; 178),
311 kind: ENUM_VARIANT,
312 detail: None,
313 deprecated: false,
314 },
315 StructureNode {
316 parent: None,
317 label: "T",
318 navigation_range: [186; 187),
319 node_range: [181; 193),
320 kind: TYPE_ALIAS_DEF,
321 detail: Some(
322 "()",
323 ),
324 deprecated: false,
325 },
326 StructureNode {
327 parent: None,
328 label: "S",
329 navigation_range: [201; 202),
330 node_range: [194; 213),
331 kind: STATIC_DEF,
332 detail: Some(
333 "i32",
334 ),
335 deprecated: false,
336 },
337 StructureNode {
338 parent: None,
339 label: "C",
340 navigation_range: [220; 221),
341 node_range: [214; 232),
342 kind: CONST_DEF,
343 detail: Some(
344 "i32",
345 ),
346 deprecated: false,
347 },
348 StructureNode {
349 parent: None,
350 label: "impl E",
351 navigation_range: [239; 240),
352 node_range: [234; 243),
353 kind: IMPL_BLOCK,
354 detail: None,
355 deprecated: false,
356 },
357 StructureNode {
358 parent: None,
359 label: "impl fmt::Debug for E",
360 navigation_range: [265; 266),
361 node_range: [245; 269),
362 kind: IMPL_BLOCK,
363 detail: None,
364 deprecated: false,
365 },
366 StructureNode {
367 parent: None,
368 label: "mc",
369 navigation_range: [284; 286),
370 node_range: [271; 303),
371 kind: MACRO_CALL,
372 detail: None,
373 deprecated: false,
374 },
375 StructureNode {
376 parent: None,
377 label: "obsolete",
378 navigation_range: [322; 330),
379 node_range: [305; 335),
380 kind: FN_DEF,
381 detail: Some(
382 "fn()",
383 ),
384 deprecated: true,
385 },
386 StructureNode {
387 parent: None,
388 label: "very_obsolete",
389 navigation_range: [375; 388),
390 node_range: [337; 393),
391 kind: FN_DEF,
392 detail: Some(
393 "fn()",
394 ),
395 deprecated: true,
396 },
397 ]
398 "###
399 );
400 }
401}