• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
file_structure(file: &SourceFile) -> Vec<StructureNode>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 
structure_node(node: &SyntaxNode) -> Option<StructureNode>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::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)),
153             ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)),
154             ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)),
155             ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)),
156             ast::Const(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Const)),
157             ast::Static(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Static)),
158             ast::Impl(it) => {
159                 let target_type = it.self_ty()?;
160                 let target_trait = it.trait_();
161                 let label = match target_trait {
162                     None => format!("impl {}", target_type.syntax().text()),
163                     Some(t) => {
164                         format!("impl {}{} for {}",
165                             it.excl_token().map(|x| x.to_string()).unwrap_or_default(),
166                             t.syntax().text(),
167                             target_type.syntax().text(),
168                         )
169                     }
170                 };
171 
172                 let node = StructureNode {
173                     parent: None,
174                     label,
175                     navigation_range: target_type.syntax().text_range(),
176                     node_range: it.syntax().text_range(),
177                     kind: StructureNodeKind::SymbolKind(SymbolKind::Impl),
178                     detail: None,
179                     deprecated: false,
180                 };
181                 Some(node)
182             },
183             ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
184             _ => None,
185         }
186     }
187 }
188 
structure_token(token: SyntaxToken) -> Option<StructureNode>189 fn structure_token(token: SyntaxToken) -> Option<StructureNode> {
190     if let Some(comment) = ast::Comment::cast(token) {
191         let text = comment.text().trim();
192 
193         if let Some(region_name) = text.strip_prefix("// region:").map(str::trim) {
194             return Some(StructureNode {
195                 parent: None,
196                 label: region_name.to_string(),
197                 navigation_range: comment.syntax().text_range(),
198                 node_range: comment.syntax().text_range(),
199                 kind: StructureNodeKind::Region,
200                 detail: None,
201                 deprecated: false,
202             });
203         }
204     }
205 
206     None
207 }
208 
209 #[cfg(test)]
210 mod tests {
211     use expect_test::{expect, Expect};
212 
213     use super::*;
214 
check(ra_fixture: &str, expect: Expect)215     fn check(ra_fixture: &str, expect: Expect) {
216         let file = SourceFile::parse(ra_fixture).ok().unwrap();
217         let structure = file_structure(&file);
218         expect.assert_debug_eq(&structure)
219     }
220 
221     #[test]
test_negative_trait_bound()222     fn test_negative_trait_bound() {
223         let txt = r#"impl !Unpin for Test {}"#;
224         check(
225             txt,
226             expect![[r#"
227         [
228             StructureNode {
229                 parent: None,
230                 label: "impl !Unpin for Test",
231                 navigation_range: 16..20,
232                 node_range: 0..23,
233                 kind: SymbolKind(
234                     Impl,
235                 ),
236                 detail: None,
237                 deprecated: false,
238             },
239         ]
240         "#]],
241         );
242     }
243 
244     #[test]
test_file_structure()245     fn test_file_structure() {
246         check(
247             r#"
248 struct Foo {
249     x: i32
250 }
251 
252 mod m {
253     fn bar1() {}
254     fn bar2<T>(t: T) -> T {}
255     fn bar3<A,
256         B>(a: A,
257         b: B) -> Vec<
258         u32
259     > {}
260 }
261 
262 enum E { X, Y(i32) }
263 type T = ();
264 static S: i32 = 92;
265 const C: i32 = 92;
266 trait Tr {}
267 trait Alias = Tr;
268 
269 impl E {}
270 
271 impl fmt::Debug for E {}
272 
273 macro_rules! mc {
274     () => {}
275 }
276 
277 #[macro_export]
278 macro_rules! mcexp {
279     () => {}
280 }
281 
282 /// Doc comment
283 macro_rules! mcexp {
284     () => {}
285 }
286 
287 #[deprecated]
288 fn obsolete() {}
289 
290 #[deprecated(note = "for awhile")]
291 fn very_obsolete() {}
292 
293 // region: Some region name
294 // endregion
295 
296 // region: dontpanic
297 mod m {
298 fn f() {}
299 // endregion
300 fn g() {}
301 }
302 "#,
303             expect![[r#"
304                 [
305                     StructureNode {
306                         parent: None,
307                         label: "Foo",
308                         navigation_range: 8..11,
309                         node_range: 1..26,
310                         kind: SymbolKind(
311                             Struct,
312                         ),
313                         detail: None,
314                         deprecated: false,
315                     },
316                     StructureNode {
317                         parent: Some(
318                             0,
319                         ),
320                         label: "x",
321                         navigation_range: 18..19,
322                         node_range: 18..24,
323                         kind: SymbolKind(
324                             Field,
325                         ),
326                         detail: Some(
327                             "i32",
328                         ),
329                         deprecated: false,
330                     },
331                     StructureNode {
332                         parent: None,
333                         label: "m",
334                         navigation_range: 32..33,
335                         node_range: 28..158,
336                         kind: SymbolKind(
337                             Module,
338                         ),
339                         detail: None,
340                         deprecated: false,
341                     },
342                     StructureNode {
343                         parent: Some(
344                             2,
345                         ),
346                         label: "bar1",
347                         navigation_range: 43..47,
348                         node_range: 40..52,
349                         kind: SymbolKind(
350                             Function,
351                         ),
352                         detail: Some(
353                             "fn()",
354                         ),
355                         deprecated: false,
356                     },
357                     StructureNode {
358                         parent: Some(
359                             2,
360                         ),
361                         label: "bar2",
362                         navigation_range: 60..64,
363                         node_range: 57..81,
364                         kind: SymbolKind(
365                             Function,
366                         ),
367                         detail: Some(
368                             "fn<T>(t: T) -> T",
369                         ),
370                         deprecated: false,
371                     },
372                     StructureNode {
373                         parent: Some(
374                             2,
375                         ),
376                         label: "bar3",
377                         navigation_range: 89..93,
378                         node_range: 86..156,
379                         kind: SymbolKind(
380                             Function,
381                         ),
382                         detail: Some(
383                             "fn<A, B>(a: A, b: B) -> Vec< u32 >",
384                         ),
385                         deprecated: false,
386                     },
387                     StructureNode {
388                         parent: None,
389                         label: "E",
390                         navigation_range: 165..166,
391                         node_range: 160..180,
392                         kind: SymbolKind(
393                             Enum,
394                         ),
395                         detail: None,
396                         deprecated: false,
397                     },
398                     StructureNode {
399                         parent: Some(
400                             6,
401                         ),
402                         label: "X",
403                         navigation_range: 169..170,
404                         node_range: 169..170,
405                         kind: SymbolKind(
406                             Variant,
407                         ),
408                         detail: None,
409                         deprecated: false,
410                     },
411                     StructureNode {
412                         parent: Some(
413                             6,
414                         ),
415                         label: "Y",
416                         navigation_range: 172..173,
417                         node_range: 172..178,
418                         kind: SymbolKind(
419                             Variant,
420                         ),
421                         detail: None,
422                         deprecated: false,
423                     },
424                     StructureNode {
425                         parent: None,
426                         label: "T",
427                         navigation_range: 186..187,
428                         node_range: 181..193,
429                         kind: SymbolKind(
430                             TypeAlias,
431                         ),
432                         detail: Some(
433                             "()",
434                         ),
435                         deprecated: false,
436                     },
437                     StructureNode {
438                         parent: None,
439                         label: "S",
440                         navigation_range: 201..202,
441                         node_range: 194..213,
442                         kind: SymbolKind(
443                             Static,
444                         ),
445                         detail: Some(
446                             "i32",
447                         ),
448                         deprecated: false,
449                     },
450                     StructureNode {
451                         parent: None,
452                         label: "C",
453                         navigation_range: 220..221,
454                         node_range: 214..232,
455                         kind: SymbolKind(
456                             Const,
457                         ),
458                         detail: Some(
459                             "i32",
460                         ),
461                         deprecated: false,
462                     },
463                     StructureNode {
464                         parent: None,
465                         label: "Tr",
466                         navigation_range: 239..241,
467                         node_range: 233..244,
468                         kind: SymbolKind(
469                             Trait,
470                         ),
471                         detail: None,
472                         deprecated: false,
473                     },
474                     StructureNode {
475                         parent: None,
476                         label: "Alias",
477                         navigation_range: 251..256,
478                         node_range: 245..262,
479                         kind: SymbolKind(
480                             TraitAlias,
481                         ),
482                         detail: None,
483                         deprecated: false,
484                     },
485                     StructureNode {
486                         parent: None,
487                         label: "impl E",
488                         navigation_range: 269..270,
489                         node_range: 264..273,
490                         kind: SymbolKind(
491                             Impl,
492                         ),
493                         detail: None,
494                         deprecated: false,
495                     },
496                     StructureNode {
497                         parent: None,
498                         label: "impl fmt::Debug for E",
499                         navigation_range: 295..296,
500                         node_range: 275..299,
501                         kind: SymbolKind(
502                             Impl,
503                         ),
504                         detail: None,
505                         deprecated: false,
506                     },
507                     StructureNode {
508                         parent: None,
509                         label: "mc",
510                         navigation_range: 314..316,
511                         node_range: 301..333,
512                         kind: SymbolKind(
513                             Macro,
514                         ),
515                         detail: None,
516                         deprecated: false,
517                     },
518                     StructureNode {
519                         parent: None,
520                         label: "mcexp",
521                         navigation_range: 364..369,
522                         node_range: 335..386,
523                         kind: SymbolKind(
524                             Macro,
525                         ),
526                         detail: None,
527                         deprecated: false,
528                     },
529                     StructureNode {
530                         parent: None,
531                         label: "mcexp",
532                         navigation_range: 417..422,
533                         node_range: 388..439,
534                         kind: SymbolKind(
535                             Macro,
536                         ),
537                         detail: None,
538                         deprecated: false,
539                     },
540                     StructureNode {
541                         parent: None,
542                         label: "obsolete",
543                         navigation_range: 458..466,
544                         node_range: 441..471,
545                         kind: SymbolKind(
546                             Function,
547                         ),
548                         detail: Some(
549                             "fn()",
550                         ),
551                         deprecated: true,
552                     },
553                     StructureNode {
554                         parent: None,
555                         label: "very_obsolete",
556                         navigation_range: 511..524,
557                         node_range: 473..529,
558                         kind: SymbolKind(
559                             Function,
560                         ),
561                         detail: Some(
562                             "fn()",
563                         ),
564                         deprecated: true,
565                     },
566                     StructureNode {
567                         parent: None,
568                         label: "Some region name",
569                         navigation_range: 531..558,
570                         node_range: 531..558,
571                         kind: Region,
572                         detail: None,
573                         deprecated: false,
574                     },
575                     StructureNode {
576                         parent: None,
577                         label: "m",
578                         navigation_range: 598..599,
579                         node_range: 573..636,
580                         kind: SymbolKind(
581                             Module,
582                         ),
583                         detail: None,
584                         deprecated: false,
585                     },
586                     StructureNode {
587                         parent: Some(
588                             22,
589                         ),
590                         label: "dontpanic",
591                         navigation_range: 573..593,
592                         node_range: 573..593,
593                         kind: Region,
594                         detail: None,
595                         deprecated: false,
596                     },
597                     StructureNode {
598                         parent: Some(
599                             22,
600                         ),
601                         label: "f",
602                         navigation_range: 605..606,
603                         node_range: 602..611,
604                         kind: SymbolKind(
605                             Function,
606                         ),
607                         detail: Some(
608                             "fn()",
609                         ),
610                         deprecated: false,
611                     },
612                     StructureNode {
613                         parent: Some(
614                             22,
615                         ),
616                         label: "g",
617                         navigation_range: 628..629,
618                         node_range: 612..634,
619                         kind: SymbolKind(
620                             Function,
621                         ),
622                         detail: Some(
623                             "fn()",
624                         ),
625                         deprecated: false,
626                     },
627                 ]
628             "#]],
629         );
630     }
631 }
632