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