aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/goto_implementation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/goto_implementation.rs')
-rw-r--r--crates/ra_ide/src/goto_implementation.rs208
1 files changed, 208 insertions, 0 deletions
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs
new file mode 100644
index 000000000..0cec0657e
--- /dev/null
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -0,0 +1,208 @@
1use hir::{Crate, ImplDef, Semantics};
2use ra_ide_db::RootDatabase;
3use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
4
5use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
6
7// Feature: Go to Implementation
8//
9// Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
10//
11// |===
12// | Editor | Shortcut
13//
14// | VS Code | kbd:[Ctrl+F12]
15// |===
16pub(crate) fn goto_implementation(
17 db: &RootDatabase,
18 position: FilePosition,
19) -> Option<RangeInfo<Vec<NavigationTarget>>> {
20 let sema = Semantics::new(db);
21 let source_file = sema.parse(position.file_id);
22 let syntax = source_file.syntax().clone();
23
24 let krate = sema.to_module_def(position.file_id)?.krate();
25
26 if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) {
27 return Some(RangeInfo::new(
28 nominal_def.syntax().text_range(),
29 impls_for_def(&sema, &nominal_def, krate)?,
30 ));
31 } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) {
32 return Some(RangeInfo::new(
33 trait_def.syntax().text_range(),
34 impls_for_trait(&sema, &trait_def, krate)?,
35 ));
36 }
37
38 None
39}
40
41fn impls_for_def(
42 sema: &Semantics<RootDatabase>,
43 node: &ast::NominalDef,
44 krate: Crate,
45) -> Option<Vec<NavigationTarget>> {
46 let ty = match node {
47 ast::NominalDef::StructDef(def) => sema.to_def(def)?.ty(sema.db),
48 ast::NominalDef::EnumDef(def) => sema.to_def(def)?.ty(sema.db),
49 ast::NominalDef::UnionDef(def) => sema.to_def(def)?.ty(sema.db),
50 };
51
52 let impls = ImplDef::all_in_crate(sema.db, krate);
53
54 Some(
55 impls
56 .into_iter()
57 .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db)))
58 .map(|imp| imp.to_nav(sema.db))
59 .collect(),
60 )
61}
62
63fn impls_for_trait(
64 sema: &Semantics<RootDatabase>,
65 node: &ast::TraitDef,
66 krate: Crate,
67) -> Option<Vec<NavigationTarget>> {
68 let tr = sema.to_def(node)?;
69
70 let impls = ImplDef::for_trait(sema.db, krate, tr);
71
72 Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect())
73}
74
75#[cfg(test)]
76mod tests {
77 use crate::mock_analysis::analysis_and_position;
78
79 fn check_goto(fixture: &str, expected: &[&str]) {
80 let (analysis, pos) = analysis_and_position(fixture);
81
82 let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info;
83 assert_eq!(navs.len(), expected.len());
84 navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start()));
85 navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i]));
86 }
87
88 #[test]
89 fn goto_implementation_works() {
90 check_goto(
91 "
92 //- /lib.rs
93 struct Foo<|>;
94 impl Foo {}
95 ",
96 &["impl IMPL_DEF FileId(1) 12..23"],
97 );
98 }
99
100 #[test]
101 fn goto_implementation_works_multiple_blocks() {
102 check_goto(
103 "
104 //- /lib.rs
105 struct Foo<|>;
106 impl Foo {}
107 impl Foo {}
108 ",
109 &["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"],
110 );
111 }
112
113 #[test]
114 fn goto_implementation_works_multiple_mods() {
115 check_goto(
116 "
117 //- /lib.rs
118 struct Foo<|>;
119 mod a {
120 impl super::Foo {}
121 }
122 mod b {
123 impl super::Foo {}
124 }
125 ",
126 &["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"],
127 );
128 }
129
130 #[test]
131 fn goto_implementation_works_multiple_files() {
132 check_goto(
133 "
134 //- /lib.rs
135 struct Foo<|>;
136 mod a;
137 mod b;
138 //- /a.rs
139 impl crate::Foo {}
140 //- /b.rs
141 impl crate::Foo {}
142 ",
143 &["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"],
144 );
145 }
146
147 #[test]
148 fn goto_implementation_for_trait() {
149 check_goto(
150 "
151 //- /lib.rs
152 trait T<|> {}
153 struct Foo;
154 impl T for Foo {}
155 ",
156 &["impl IMPL_DEF FileId(1) 23..40"],
157 );
158 }
159
160 #[test]
161 fn goto_implementation_for_trait_multiple_files() {
162 check_goto(
163 "
164 //- /lib.rs
165 trait T<|> {};
166 struct Foo;
167 mod a;
168 mod b;
169 //- /a.rs
170 impl crate::T for crate::Foo {}
171 //- /b.rs
172 impl crate::T for crate::Foo {}
173 ",
174 &["impl IMPL_DEF FileId(2) 0..31", "impl IMPL_DEF FileId(3) 0..31"],
175 );
176 }
177
178 #[test]
179 fn goto_implementation_all_impls() {
180 check_goto(
181 "
182 //- /lib.rs
183 trait T {}
184 struct Foo<|>;
185 impl Foo {}
186 impl T for Foo {}
187 impl T for &Foo {}
188 ",
189 &[
190 "impl IMPL_DEF FileId(1) 23..34",
191 "impl IMPL_DEF FileId(1) 35..52",
192 "impl IMPL_DEF FileId(1) 53..71",
193 ],
194 );
195 }
196
197 #[test]
198 fn goto_implementation_to_builtin_derive() {
199 check_goto(
200 "
201 //- /lib.rs
202 #[derive(Copy)]
203 struct Foo<|>;
204 ",
205 &["impl IMPL_DEF FileId(1) 0..15"],
206 );
207 }
208}