ide/
child_modules.rs

1use hir::Semantics;
2use ide_db::{FilePosition, RootDatabase};
3use syntax::{
4    algo::find_node_at_offset,
5    ast::{self, AstNode},
6};
7
8use crate::NavigationTarget;
9
10// Feature: Child Modules
11//
12// Navigates to the child modules of the current module.
13//
14// | Editor  | Action Name |
15// |---------|-------------|
16// | VS Code | **rust-analyzer: Locate child modules** |
17
18/// This returns `Vec` because a module may be included from several places.
19pub(crate) fn child_modules(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
20    let sema = Semantics::new(db);
21    let source_file = sema.parse_guess_edition(position.file_id);
22    // First go to the parent module which contains the cursor
23    let module = find_node_at_offset::<ast::Module>(source_file.syntax(), position.offset);
24
25    match module {
26        Some(module) => {
27            // Return all child modules inside the ItemList of the parent module
28            sema.to_def(&module)
29                .into_iter()
30                .flat_map(|module| module.children(db))
31                .map(|module| NavigationTarget::from_module_to_decl(db, module).call_site())
32                .collect()
33        }
34        None => {
35            // Return all the child modules inside the source file
36            sema.file_to_module_defs(position.file_id)
37                .flat_map(|module| module.children(db))
38                .map(|module| NavigationTarget::from_module_to_decl(db, module).call_site())
39                .collect()
40        }
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use ide_db::FileRange;
47
48    use crate::fixture;
49
50    fn check_child_module(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
51        let (analysis, position, expected) = fixture::annotations(ra_fixture);
52        let navs = analysis.child_modules(position).unwrap();
53        let navs = navs
54            .iter()
55            .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
56            .collect::<Vec<_>>();
57        assert_eq!(expected.into_iter().map(|(fr, _)| fr).collect::<Vec<_>>(), navs);
58    }
59
60    #[test]
61    fn test_resolve_child_module() {
62        check_child_module(
63            r#"
64//- /lib.rs
65$0
66mod foo;
67  //^^^
68
69//- /foo.rs
70// empty
71"#,
72        );
73    }
74
75    #[test]
76    fn test_resolve_child_module_on_module_decl() {
77        check_child_module(
78            r#"
79//- /lib.rs
80mod $0foo;
81//- /foo.rs
82mod bar;
83  //^^^
84
85//- /foo/bar.rs
86// empty
87"#,
88        );
89    }
90
91    #[test]
92    fn test_resolve_child_module_for_inline() {
93        check_child_module(
94            r#"
95//- /lib.rs
96mod foo {
97    mod $0bar {
98        mod baz {}
99    }     //^^^
100}
101"#,
102        );
103    }
104
105    #[test]
106    fn test_resolve_multi_child_module() {
107        check_child_module(
108            r#"
109//- /main.rs
110$0
111mod foo;
112  //^^^
113mod bar;
114  //^^^
115//- /foo.rs
116// empty
117
118//- /bar.rs
119// empty
120"#,
121        );
122    }
123}