diff options
Diffstat (limited to 'crates/ra_ide_api/src/impls.rs')
-rw-r--r-- | crates/ra_ide_api/src/impls.rs | 121 |
1 files changed, 121 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 @@ | |||
1 | use ra_db::{SourceDatabase}; | ||
2 | use ra_syntax::{ | ||
3 | AstNode, ast, | ||
4 | algo::find_node_at_offset, | ||
5 | }; | ||
6 | use hir::{db::HirDatabase, source_binder}; | ||
7 | |||
8 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; | ||
9 | |||
10 | pub(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)] | ||
41 | mod 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 | } | ||