]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide/src/file_structure.rs
Rollup merge of #100228 - luqmana:suggestion-ice, r=estebank
[rust.git] / src / tools / rust-analyzer / crates / ide / src / file_structure.rs
1 use ide_db::SymbolKind;
2 use syntax::{
3     ast::{self, HasAttrs, HasGenericParams, HasName},
4     match_ast, AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, TextRange,
5     WalkEvent,
6 };
7
8 #[derive(Debug, Clone)]
9 pub struct StructureNode {
10     pub parent: Option<usize>,
11     pub label: String,
12     pub navigation_range: TextRange,
13     pub node_range: TextRange,
14     pub kind: StructureNodeKind,
15     pub detail: Option<String>,
16     pub deprecated: bool,
17 }
18
19 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
20 pub enum StructureNodeKind {
21     SymbolKind(SymbolKind),
22     Region,
23 }
24
25 // Feature: File Structure
26 //
27 // Provides a tree of the symbols defined in the file. Can be used to
28 //
29 // * fuzzy search symbol in a file (super useful)
30 // * draw breadcrumbs to describe the context around the cursor
31 // * draw outline of the file
32 //
33 // |===
34 // | Editor  | Shortcut
35 //
36 // | VS Code | kbd:[Ctrl+Shift+O]
37 // |===
38 //
39 // image::https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif[]
40
41 pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
42     let mut res = Vec::new();
43     let mut stack = Vec::new();
44
45     for event in file.syntax().preorder_with_tokens() {
46         match event {
47             WalkEvent::Enter(NodeOrToken::Node(node)) => {
48                 if let Some(mut symbol) = structure_node(&node) {
49                     symbol.parent = stack.last().copied();
50                     stack.push(res.len());
51                     res.push(symbol);
52                 }
53             }
54             WalkEvent::Leave(NodeOrToken::Node(node)) => {
55                 if structure_node(&node).is_some() {
56                     stack.pop().unwrap();
57                 }
58             }
59             WalkEvent::Enter(NodeOrToken::Token(token)) => {
60                 if let Some(mut symbol) = structure_token(token) {
61                     symbol.parent = stack.last().copied();
62                     stack.push(res.len());
63                     res.push(symbol);
64                 }
65             }
66             WalkEvent::Leave(NodeOrToken::Token(token)) => {
67                 if structure_token(token).is_some() {
68                     stack.pop().unwrap();
69                 }
70             }
71         }
72     }
73     res
74 }
75
76 fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
77     fn decl<N: HasName + HasAttrs>(node: N, kind: StructureNodeKind) -> Option<StructureNode> {
78         decl_with_detail(&node, None, kind)
79     }
80
81     fn decl_with_type_ref<N: HasName + HasAttrs>(
82         node: &N,
83         type_ref: Option<ast::Type>,
84         kind: StructureNodeKind,
85     ) -> Option<StructureNode> {
86         let detail = type_ref.map(|type_ref| {
87             let mut detail = String::new();
88             collapse_ws(type_ref.syntax(), &mut detail);
89             detail
90         });
91         decl_with_detail(node, detail, kind)
92     }
93
94     fn decl_with_detail<N: HasName + HasAttrs>(
95         node: &N,
96         detail: Option<String>,
97         kind: StructureNodeKind,
98     ) -> Option<StructureNode> {
99         let name = node.name()?;
100
101         Some(StructureNode {
102             parent: None,
103             label: name.text().to_string(),
104             navigation_range: name.syntax().text_range(),
105             node_range: node.syntax().text_range(),
106             kind,
107             detail,
108             deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"),
109         })
110     }
111
112     fn collapse_ws(node: &SyntaxNode, output: &mut String) {
113         let mut can_insert_ws = false;
114         node.text().for_each_chunk(|chunk| {
115             for line in chunk.lines() {
116                 let line = line.trim();
117                 if line.is_empty() {
118                     if can_insert_ws {
119                         output.push(' ');
120                         can_insert_ws = false;
121                     }
122                 } else {
123                     output.push_str(line);
124                     can_insert_ws = true;
125                 }
126             }
127         })
128     }
129
130     match_ast! {
131         match node {
132             ast::Fn(it) => {
133                 let mut detail = String::from("fn");
134                 if let Some(type_param_list) = it.generic_param_list() {
135                     collapse_ws(type_param_list.syntax(), &mut detail);
136                 }
137                 if let Some(param_list) = it.param_list() {
138                     collapse_ws(param_list.syntax(), &mut detail);
139                 }
140                 if let Some(ret_type) = it.ret_type() {
141                     detail.push(' ');
142                     collapse_ws(ret_type.syntax(), &mut detail);
143                 }
144
145                 decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(SymbolKind::Function))
146             },
147             ast::Struct(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Struct)),
148             ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)),
149             ast::Enum(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)),
150             ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)),
151             ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)),
152             ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)),
153             ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)),
154             ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)),
155             ast::Const(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Const)),
156             ast::Static(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Static)),
157             ast::Impl(it) => {
158                 let target_type = it.self_ty()?;
159                 let target_trait = it.trait_();
160                 let label = match target_trait {
161                     None => format!("impl {}", target_type.syntax().text()),
162                     Some(t) => {
163                         format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),)
164                     }
165                 };
166
167                 let node = StructureNode {
168                     parent: None,
169                     label,
170                     navigation_range: target_type.syntax().text_range(),
171                     node_range: it.syntax().text_range(),
172                     kind: StructureNodeKind::SymbolKind(SymbolKind::Impl),
173                     detail: None,
174                     deprecated: false,
175                 };
176                 Some(node)
177             },
178             ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
179             _ => None,
180         }
181     }
182 }
183
184 fn structure_token(token: SyntaxToken) -> Option<StructureNode> {
185     if let Some(comment) = ast::Comment::cast(token) {
186         let text = comment.text().trim();
187
188         if let Some(region_name) = text.strip_prefix("// region:").map(str::trim) {
189             return Some(StructureNode {
190                 parent: None,
191                 label: region_name.to_string(),
192                 navigation_range: comment.syntax().text_range(),
193                 node_range: comment.syntax().text_range(),
194                 kind: StructureNodeKind::Region,
195                 detail: None,
196                 deprecated: false,
197             });
198         }
199     }
200
201     None
202 }
203
204 #[cfg(test)]
205 mod tests {
206     use expect_test::{expect, Expect};
207
208     use super::*;
209
210     fn check(ra_fixture: &str, expect: Expect) {
211         let file = SourceFile::parse(ra_fixture).ok().unwrap();
212         let structure = file_structure(&file);
213         expect.assert_debug_eq(&structure)
214     }
215
216     #[test]
217     fn test_file_structure() {
218         check(
219             r#"
220 struct Foo {
221     x: i32
222 }
223
224 mod m {
225     fn bar1() {}
226     fn bar2<T>(t: T) -> T {}
227     fn bar3<A,
228         B>(a: A,
229         b: B) -> Vec<
230         u32
231     > {}
232 }
233
234 enum E { X, Y(i32) }
235 type T = ();
236 static S: i32 = 92;
237 const C: i32 = 92;
238
239 impl E {}
240
241 impl fmt::Debug for E {}
242
243 macro_rules! mc {
244     () => {}
245 }
246
247 #[macro_export]
248 macro_rules! mcexp {
249     () => {}
250 }
251
252 /// Doc comment
253 macro_rules! mcexp {
254     () => {}
255 }
256
257 #[deprecated]
258 fn obsolete() {}
259
260 #[deprecated(note = "for awhile")]
261 fn very_obsolete() {}
262
263 // region: Some region name
264 // endregion
265
266 // region: dontpanic
267 mod m {
268 fn f() {}
269 // endregion
270 fn g() {}
271 }
272 "#,
273             expect![[r#"
274                 [
275                     StructureNode {
276                         parent: None,
277                         label: "Foo",
278                         navigation_range: 8..11,
279                         node_range: 1..26,
280                         kind: SymbolKind(
281                             Struct,
282                         ),
283                         detail: None,
284                         deprecated: false,
285                     },
286                     StructureNode {
287                         parent: Some(
288                             0,
289                         ),
290                         label: "x",
291                         navigation_range: 18..19,
292                         node_range: 18..24,
293                         kind: SymbolKind(
294                             Field,
295                         ),
296                         detail: Some(
297                             "i32",
298                         ),
299                         deprecated: false,
300                     },
301                     StructureNode {
302                         parent: None,
303                         label: "m",
304                         navigation_range: 32..33,
305                         node_range: 28..158,
306                         kind: SymbolKind(
307                             Module,
308                         ),
309                         detail: None,
310                         deprecated: false,
311                     },
312                     StructureNode {
313                         parent: Some(
314                             2,
315                         ),
316                         label: "bar1",
317                         navigation_range: 43..47,
318                         node_range: 40..52,
319                         kind: SymbolKind(
320                             Function,
321                         ),
322                         detail: Some(
323                             "fn()",
324                         ),
325                         deprecated: false,
326                     },
327                     StructureNode {
328                         parent: Some(
329                             2,
330                         ),
331                         label: "bar2",
332                         navigation_range: 60..64,
333                         node_range: 57..81,
334                         kind: SymbolKind(
335                             Function,
336                         ),
337                         detail: Some(
338                             "fn<T>(t: T) -> T",
339                         ),
340                         deprecated: false,
341                     },
342                     StructureNode {
343                         parent: Some(
344                             2,
345                         ),
346                         label: "bar3",
347                         navigation_range: 89..93,
348                         node_range: 86..156,
349                         kind: SymbolKind(
350                             Function,
351                         ),
352                         detail: Some(
353                             "fn<A, B>(a: A, b: B) -> Vec< u32 >",
354                         ),
355                         deprecated: false,
356                     },
357                     StructureNode {
358                         parent: None,
359                         label: "E",
360                         navigation_range: 165..166,
361                         node_range: 160..180,
362                         kind: SymbolKind(
363                             Enum,
364                         ),
365                         detail: None,
366                         deprecated: false,
367                     },
368                     StructureNode {
369                         parent: Some(
370                             6,
371                         ),
372                         label: "X",
373                         navigation_range: 169..170,
374                         node_range: 169..170,
375                         kind: SymbolKind(
376                             Variant,
377                         ),
378                         detail: None,
379                         deprecated: false,
380                     },
381                     StructureNode {
382                         parent: Some(
383                             6,
384                         ),
385                         label: "Y",
386                         navigation_range: 172..173,
387                         node_range: 172..178,
388                         kind: SymbolKind(
389                             Variant,
390                         ),
391                         detail: None,
392                         deprecated: false,
393                     },
394                     StructureNode {
395                         parent: None,
396                         label: "T",
397                         navigation_range: 186..187,
398                         node_range: 181..193,
399                         kind: SymbolKind(
400                             TypeAlias,
401                         ),
402                         detail: Some(
403                             "()",
404                         ),
405                         deprecated: false,
406                     },
407                     StructureNode {
408                         parent: None,
409                         label: "S",
410                         navigation_range: 201..202,
411                         node_range: 194..213,
412                         kind: SymbolKind(
413                             Static,
414                         ),
415                         detail: Some(
416                             "i32",
417                         ),
418                         deprecated: false,
419                     },
420                     StructureNode {
421                         parent: None,
422                         label: "C",
423                         navigation_range: 220..221,
424                         node_range: 214..232,
425                         kind: SymbolKind(
426                             Const,
427                         ),
428                         detail: Some(
429                             "i32",
430                         ),
431                         deprecated: false,
432                     },
433                     StructureNode {
434                         parent: None,
435                         label: "impl E",
436                         navigation_range: 239..240,
437                         node_range: 234..243,
438                         kind: SymbolKind(
439                             Impl,
440                         ),
441                         detail: None,
442                         deprecated: false,
443                     },
444                     StructureNode {
445                         parent: None,
446                         label: "impl fmt::Debug for E",
447                         navigation_range: 265..266,
448                         node_range: 245..269,
449                         kind: SymbolKind(
450                             Impl,
451                         ),
452                         detail: None,
453                         deprecated: false,
454                     },
455                     StructureNode {
456                         parent: None,
457                         label: "mc",
458                         navigation_range: 284..286,
459                         node_range: 271..303,
460                         kind: SymbolKind(
461                             Macro,
462                         ),
463                         detail: None,
464                         deprecated: false,
465                     },
466                     StructureNode {
467                         parent: None,
468                         label: "mcexp",
469                         navigation_range: 334..339,
470                         node_range: 305..356,
471                         kind: SymbolKind(
472                             Macro,
473                         ),
474                         detail: None,
475                         deprecated: false,
476                     },
477                     StructureNode {
478                         parent: None,
479                         label: "mcexp",
480                         navigation_range: 387..392,
481                         node_range: 358..409,
482                         kind: SymbolKind(
483                             Macro,
484                         ),
485                         detail: None,
486                         deprecated: false,
487                     },
488                     StructureNode {
489                         parent: None,
490                         label: "obsolete",
491                         navigation_range: 428..436,
492                         node_range: 411..441,
493                         kind: SymbolKind(
494                             Function,
495                         ),
496                         detail: Some(
497                             "fn()",
498                         ),
499                         deprecated: true,
500                     },
501                     StructureNode {
502                         parent: None,
503                         label: "very_obsolete",
504                         navigation_range: 481..494,
505                         node_range: 443..499,
506                         kind: SymbolKind(
507                             Function,
508                         ),
509                         detail: Some(
510                             "fn()",
511                         ),
512                         deprecated: true,
513                     },
514                     StructureNode {
515                         parent: None,
516                         label: "Some region name",
517                         navigation_range: 501..528,
518                         node_range: 501..528,
519                         kind: Region,
520                         detail: None,
521                         deprecated: false,
522                     },
523                     StructureNode {
524                         parent: None,
525                         label: "m",
526                         navigation_range: 568..569,
527                         node_range: 543..606,
528                         kind: SymbolKind(
529                             Module,
530                         ),
531                         detail: None,
532                         deprecated: false,
533                     },
534                     StructureNode {
535                         parent: Some(
536                             20,
537                         ),
538                         label: "dontpanic",
539                         navigation_range: 543..563,
540                         node_range: 543..563,
541                         kind: Region,
542                         detail: None,
543                         deprecated: false,
544                     },
545                     StructureNode {
546                         parent: Some(
547                             20,
548                         ),
549                         label: "f",
550                         navigation_range: 575..576,
551                         node_range: 572..581,
552                         kind: SymbolKind(
553                             Function,
554                         ),
555                         detail: Some(
556                             "fn()",
557                         ),
558                         deprecated: false,
559                     },
560                     StructureNode {
561                         parent: Some(
562                             20,
563                         ),
564                         label: "g",
565                         navigation_range: 598..599,
566                         node_range: 582..604,
567                         kind: SymbolKind(
568                             Function,
569                         ),
570                         detail: Some(
571                             "fn()",
572                         ),
573                         deprecated: false,
574                     },
575                 ]
576             "#]],
577         );
578     }
579 }