From 3c17643b3085682a695f0e6d80483edc00d04cb3 Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Mon, 28 Jan 2019 09:26:32 -0500 Subject: Go to Implementation for structs and enums --- crates/ra_ide_api/src/impls.rs | 121 +++++++++++++++++++++++++++++ crates/ra_ide_api/src/lib.rs | 8 ++ crates/ra_ide_api/src/navigation_target.rs | 10 +++ 3 files changed, 139 insertions(+) create mode 100644 crates/ra_ide_api/src/impls.rs (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs new file mode 100644 index 000000000..16a05758a --- /dev/null +++ b/crates/ra_ide_api/src/impls.rs @@ -0,0 +1,121 @@ +use ra_db::{SourceDatabase}; +use ra_syntax::{ + AstNode, ast, + algo::find_node_at_offset, +}; +use hir::{db::HirDatabase, source_binder}; + +use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; + +pub(crate) fn goto_implementation( + db: &RootDatabase, + position: FilePosition, +) -> Option>> { + let file = db.parse(position.file_id); + let syntax = file.syntax(); + + let krate_id = db.crate_for(position.file_id).pop()?; + let krate = hir::Crate { crate_id: krate_id }; + let module = source_binder::module_from_position(db, position)?; + + let node = find_node_at_offset::(syntax, position.offset)?; + let ty = match node.kind() { + ast::NominalDefKind::StructDef(def) => { + source_binder::struct_from_module(db, module, &def).ty(db) + } + ast::NominalDefKind::EnumDef(def) => { + source_binder::enum_from_module(db, module, &def).ty(db) + } + }; + + let impls = db.impls_in_crate(krate); + + let navs = impls + .lookup_impl_blocks(db, &ty) + .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp)); + + Some(RangeInfo::new(node.syntax().range(), navs.collect())) +} + +#[cfg(test)] +mod tests { + use crate::mock_analysis::analysis_and_position; + + fn check_goto(fixuture: &str, expected: &[&str]) { + let (analysis, pos) = analysis_and_position(fixuture); + + let navs = analysis.goto_implementation(pos).unwrap().unwrap().info; + assert_eq!(navs.len(), expected.len()); + navs.into_iter() + .enumerate() + .for_each(|(i, nav)| nav.assert_match(expected[i])); + } + + #[test] + fn goto_implementation_works() { + check_goto( + " + //- /lib.rs + struct Foo<|>; + impl Foo {} + ", + &["impl IMPL_BLOCK FileId(1) [12; 23)"], + ); + } + + #[test] + fn goto_implementation_works_multiple_blocks() { + check_goto( + " + //- /lib.rs + struct Foo<|>; + impl Foo {} + impl Foo {} + ", + &[ + "impl IMPL_BLOCK FileId(1) [12; 23)", + "impl IMPL_BLOCK FileId(1) [24; 35)", + ], + ); + } + + #[test] + fn goto_implementation_works_multiple_mods() { + check_goto( + " + //- /lib.rs + struct Foo<|>; + mod a { + impl super::Foo {} + } + mod b { + impl super::Foo {} + } + ", + &[ + "impl IMPL_BLOCK FileId(1) [24; 42)", + "impl IMPL_BLOCK FileId(1) [57; 75)", + ], + ); + } + + #[test] + fn goto_implementation_works_multiple_files() { + check_goto( + " + //- /lib.rs + struct Foo<|>; + mod a; + mod b; + //- /a.rs + impl crate::Foo {} + //- /b.rs + impl crate::Foo {} + ", + &[ + "impl IMPL_BLOCK FileId(2) [0; 18)", + "impl IMPL_BLOCK FileId(3) [0; 18)", + ], + ); + } +} diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 51947e4cc..9ec4fdd95 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -25,6 +25,7 @@ mod call_info; mod syntax_highlighting; mod parent_module; mod rename; +mod impls; #[cfg(test)] mod marks; @@ -415,6 +416,13 @@ impl Analysis { self.with_db(|db| goto_definition::goto_definition(db, position)) } + pub fn goto_implementation( + &self, + position: FilePosition, + ) -> Cancelable>>> { + self.with_db(|db| impls::goto_implementation(db, position)) + } + /// Finds all usages of the reference at point. pub fn find_all_refs(&self, position: FilePosition) -> Cancelable> { self.with_db(|db| db.find_all_refs(position)) diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index d73d4afa7..5ccb5cc2e 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs @@ -147,6 +147,16 @@ impl NavigationTarget { } } + pub(crate) fn from_impl_block( + db: &RootDatabase, + module: hir::Module, + impl_block: &hir::ImplBlock, + ) -> NavigationTarget { + let (file_id, _) = module.definition_source(db); + let node = module.impl_source(db, impl_block.id()); + NavigationTarget::from_syntax(file_id, "impl".into(), None, node.syntax()) + } + #[cfg(test)] pub(crate) fn assert_match(&self, expected: &str) { let actual = self.debug_render(); -- cgit v1.2.3