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