aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src
diff options
context:
space:
mode:
authorJeremy Kolb <[email protected]>2019-01-28 14:26:32 +0000
committerJeremy Kolb <[email protected]>2019-01-30 00:13:02 +0000
commit3c17643b3085682a695f0e6d80483edc00d04cb3 (patch)
tree2e76d7be4f703a46608078228124285bc2c94e21 /crates/ra_ide_api/src
parent48d2acb297459fb06cbb49bdce2eccb4c2591714 (diff)
Go to Implementation for structs and enums
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r--crates/ra_ide_api/src/impls.rs121
-rw-r--r--crates/ra_ide_api/src/lib.rs8
-rw-r--r--crates/ra_ide_api/src/navigation_target.rs10
3 files changed, 139 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..16a05758a
--- /dev/null
+++ b/crates/ra_ide_api/src/impls.rs
@@ -0,0 +1,121 @@
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 krate_id = db.crate_for(position.file_id).pop()?;
18 let krate = hir::Crate { crate_id: krate_id };
19 let module = source_binder::module_from_position(db, position)?;
20
21 let node = find_node_at_offset::<ast::NominalDef>(syntax, position.offset)?;
22 let ty = match node.kind() {
23 ast::NominalDefKind::StructDef(def) => {
24 source_binder::struct_from_module(db, module, &def).ty(db)
25 }
26 ast::NominalDefKind::EnumDef(def) => {
27 source_binder::enum_from_module(db, module, &def).ty(db)
28 }
29 };
30
31 let impls = db.impls_in_crate(krate);
32
33 let navs = impls
34 .lookup_impl_blocks(db, &ty)
35 .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp));
36
37 Some(RangeInfo::new(node.syntax().range(), navs.collect()))
38}
39
40#[cfg(test)]
41mod tests {
42 use crate::mock_analysis::analysis_and_position;
43
44 fn check_goto(fixuture: &str, expected: &[&str]) {
45 let (analysis, pos) = analysis_and_position(fixuture);
46
47 let navs = analysis.goto_implementation(pos).unwrap().unwrap().info;
48 assert_eq!(navs.len(), expected.len());
49 navs.into_iter()
50 .enumerate()
51 .for_each(|(i, nav)| nav.assert_match(expected[i]));
52 }
53
54 #[test]
55 fn goto_implementation_works() {
56 check_goto(
57 "
58 //- /lib.rs
59 struct Foo<|>;
60 impl Foo {}
61 ",
62 &["impl IMPL_BLOCK FileId(1) [12; 23)"],
63 );
64 }
65
66 #[test]
67 fn goto_implementation_works_multiple_blocks() {
68 check_goto(
69 "
70 //- /lib.rs
71 struct Foo<|>;
72 impl Foo {}
73 impl Foo {}
74 ",
75 &[
76 "impl IMPL_BLOCK FileId(1) [12; 23)",
77 "impl IMPL_BLOCK FileId(1) [24; 35)",
78 ],
79 );
80 }
81
82 #[test]
83 fn goto_implementation_works_multiple_mods() {
84 check_goto(
85 "
86 //- /lib.rs
87 struct Foo<|>;
88 mod a {
89 impl super::Foo {}
90 }
91 mod b {
92 impl super::Foo {}
93 }
94 ",
95 &[
96 "impl IMPL_BLOCK FileId(1) [24; 42)",
97 "impl IMPL_BLOCK FileId(1) [57; 75)",
98 ],
99 );
100 }
101
102 #[test]
103 fn goto_implementation_works_multiple_files() {
104 check_goto(
105 "
106 //- /lib.rs
107 struct Foo<|>;
108 mod a;
109 mod b;
110 //- /a.rs
111 impl crate::Foo {}
112 //- /b.rs
113 impl crate::Foo {}
114 ",
115 &[
116 "impl IMPL_BLOCK FileId(2) [0; 18)",
117 "impl IMPL_BLOCK FileId(3) [0; 18)",
118 ],
119 );
120 }
121}
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;
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;
@@ -415,6 +416,13 @@ impl Analysis {
415 self.with_db(|db| goto_definition::goto_definition(db, position)) 416 self.with_db(|db| goto_definition::goto_definition(db, position))
416 } 417 }
417 418
419 pub fn goto_implementation(
420 &self,
421 position: FilePosition,
422 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
423 self.with_db(|db| impls::goto_implementation(db, position))
424 }
425
418 /// Finds all usages of the reference at point. 426 /// Finds all usages of the reference at point.
419 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 427 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
420 self.with_db(|db| db.find_all_refs(position)) 428 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();