Merge pull request #19255 from geetanshjuneja/master

Add children modules feature
This commit is contained in:
Lukas Wirth 2025-04-09 09:53:25 +00:00 committed by GitHub
commit f4747f2617
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 204 additions and 1 deletions

View file

@ -0,0 +1,123 @@
use hir::Semantics;
use ide_db::{FilePosition, RootDatabase};
use syntax::{
algo::find_node_at_offset,
ast::{self, AstNode},
};
use crate::NavigationTarget;
// Feature: Children Modules
//
// Navigates to the children modules of the current module.
//
// | Editor | Action Name |
// |---------|-------------|
// | VS Code | **rust-analyzer: Locate children modules** |
/// This returns `Vec` because a module may be included from several places.
pub(crate) fn children_modules(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
let sema = Semantics::new(db);
let source_file = sema.parse_guess_edition(position.file_id);
// First go to the parent module which contains the cursor
let module = find_node_at_offset::<ast::Module>(source_file.syntax(), position.offset);
match module {
Some(module) => {
// Return all the children module inside the ItemList of the parent module
sema.to_def(&module)
.into_iter()
.flat_map(|module| module.children(db))
.map(|module| NavigationTarget::from_module_to_decl(db, module).call_site())
.collect()
}
None => {
// Return all the children module inside the source file
sema.file_to_module_defs(position.file_id)
.flat_map(|module| module.children(db))
.map(|module| NavigationTarget::from_module_to_decl(db, module).call_site())
.collect()
}
}
}
#[cfg(test)]
mod tests {
use ide_db::FileRange;
use crate::fixture;
fn check_children_module(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis.children_modules(position).unwrap();
let navs = navs
.iter()
.map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
.collect::<Vec<_>>();
assert_eq!(expected.into_iter().map(|(fr, _)| fr).collect::<Vec<_>>(), navs);
}
#[test]
fn test_resolve_children_module() {
check_children_module(
r#"
//- /lib.rs
$0
mod foo;
//^^^
//- /foo.rs
// empty
"#,
);
}
#[test]
fn test_resolve_children_module_on_module_decl() {
check_children_module(
r#"
//- /lib.rs
mod $0foo;
//- /foo.rs
mod bar;
//^^^
//- /foo/bar.rs
// empty
"#,
);
}
#[test]
fn test_resolve_children_module_for_inline() {
check_children_module(
r#"
//- /lib.rs
mod foo {
mod $0bar {
mod baz {}
} //^^^
}
"#,
);
}
#[test]
fn test_resolve_multi_child_module() {
check_children_module(
r#"
//- /main.rs
$0
mod foo;
//^^^
mod bar;
//^^^
//- /foo.rs
// empty
//- /bar.rs
// empty
"#,
);
}
}

View file

@ -20,6 +20,7 @@ mod navigation_target;
mod annotations;
mod call_hierarchy;
mod children_modules;
mod doc_links;
mod expand_macro;
mod extend_selection;
@ -605,6 +606,11 @@ impl Analysis {
self.with_db(|db| parent_module::parent_module(db, position))
}
/// Returns vec of `mod name;` declaration which are created by the current module.
pub fn children_modules(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
self.with_db(|db| children_modules::children_modules(db, position))
}
/// Returns crates that this file belongs to.
pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> {
self.with_db(|db| parent_module::crates_for(db, file_id))