aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r--crates/ra_ide_api/src/impls.rs120
-rw-r--r--crates/ra_ide_api/src/lib.rs8
-rw-r--r--crates/ra_ide_api/src/navigation_target.rs10
3 files changed, 138 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs
new file mode 100644
index 000000000..469d56d63
--- /dev/null
+++ b/crates/ra_ide_api/src/impls.rs
@@ -0,0 +1,120 @@
1use ra_db::{SourceDatabase};
2use ra_syntax::{
3 AstNode, ast,
4 algo::find_node_at_offset,
5};
6use hir::{db::HirDatabase, source_binder};
7
8use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
9
10pub(crate) fn goto_implementation(
11 db: &RootDatabase,
12 position: FilePosition,
13) -> Option<RangeInfo<Vec<NavigationTarget>>> {
14 let file = db.parse(position.file_id);
15 let syntax = file.syntax();
16
17 let module = source_binder::module_from_position(db, position)?;
18 let krate = module.krate(db)?;
19
20 let node = find_node_at_offset::<ast::NominalDef>(syntax, position.offset)?;
21 let ty = match node.kind() {
22 ast::NominalDefKind::StructDef(def) => {
23 source_binder::struct_from_module(db, module, &def).ty(db)
24 }
25 ast::NominalDefKind::EnumDef(def) => {
26 source_binder::enum_from_module(db, module, &def).ty(db)
27 }
28 };
29
30 let impls = db.impls_in_crate(krate);
31
32 let navs = impls
33 .lookup_impl_blocks(db, &ty)
34 .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp));
35
36 Some(RangeInfo::new(node.syntax().range(), navs.collect()))
37}
38
39#[cfg(test)]
40mod tests {
41 use crate::mock_analysis::analysis_and_position;
42
43 fn check_goto(fixuture: &str, expected: &[&str]) {
44 let (analysis, pos) = analysis_and_position(fixuture);
45
46 let navs = analysis.goto_implementation(pos).unwrap().unwrap().info;
47 assert_eq!(navs.len(), expected.len());
48 navs.into_iter()
49 .enumerate()
50 .for_each(|(i, nav)| nav.assert_match(expected[i]));
51 }
52
53 #[test]
54 fn goto_implementation_works() {
55 check_goto(
56 "
57 //- /lib.rs
58 struct Foo<|>;
59 impl Foo {}
60 ",
61 &["impl IMPL_BLOCK FileId(1) [12; 23)"],
62 );
63 }
64
65 #[test]
66 fn goto_implementation_works_multiple_blocks() {
67 check_goto(
68 "
69 //- /lib.rs
70 struct Foo<|>;
71 impl Foo {}
72 impl Foo {}
73 ",
74 &[
75 "impl IMPL_BLOCK FileId(1) [12; 23)",
76 "impl IMPL_BLOCK FileId(1) [24; 35)",
77 ],
78 );
79 }
80
81 #[test]
82 fn goto_implementation_works_multiple_mods() {
83 check_goto(
84 "
85 //- /lib.rs
86 struct Foo<|>;
87 mod a {
88 impl super::Foo {}
89 }
90 mod b {
91 impl super::Foo {}
92 }
93 ",
94 &[
95 "impl IMPL_BLOCK FileId(1) [24; 42)",
96 "impl IMPL_BLOCK FileId(1) [57; 75)",
97 ],
98 );
99 }
100
101 #[test]
102 fn goto_implementation_works_multiple_files() {
103 check_goto(
104 "
105 //- /lib.rs
106 struct Foo<|>;
107 mod a;
108 mod b;
109 //- /a.rs
110 impl crate::Foo {}
111 //- /b.rs
112 impl crate::Foo {}
113 ",
114 &[
115 "impl IMPL_BLOCK FileId(2) [0; 18)",
116 "impl IMPL_BLOCK FileId(3) [0; 18)",
117 ],
118 );
119 }
120}
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 09cf0216d..5d8acf9df 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -25,6 +25,7 @@ mod call_info;
25mod syntax_highlighting; 25mod syntax_highlighting;
26mod parent_module; 26mod parent_module;
27mod rename; 27mod rename;
28mod impls;
28 29
29#[cfg(test)] 30#[cfg(test)]
30mod marks; 31mod marks;
@@ -416,6 +417,13 @@ impl Analysis {
416 self.with_db(|db| goto_definition::goto_definition(db, position)) 417 self.with_db(|db| goto_definition::goto_definition(db, position))
417 } 418 }
418 419
420 pub fn goto_implementation(
421 &self,
422 position: FilePosition,
423 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
424 self.with_db(|db| impls::goto_implementation(db, position))
425 }
426
419 /// Finds all usages of the reference at point. 427 /// Finds all usages of the reference at point.
420 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 428 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
421 self.with_db(|db| db.find_all_refs(position)) 429 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 {
147 } 147 }
148 } 148 }
149 149
150 pub(crate) fn from_impl_block(
151 db: &RootDatabase,
152 module: hir::Module,
153 impl_block: &hir::ImplBlock,
154 ) -> NavigationTarget {
155 let (file_id, _) = module.definition_source(db);
156 let node = module.impl_source(db, impl_block.id());
157 NavigationTarget::from_syntax(file_id, "impl".into(), None, node.syntax())
158 }
159
150 #[cfg(test)] 160 #[cfg(test)]
151 pub(crate) fn assert_match(&self, expected: &str) { 161 pub(crate) fn assert_match(&self, expected: &str) {
152 let actual = self.debug_render(); 162 let actual = self.debug_render();