diff options
author | Aleksey Kladov <[email protected]> | 2020-05-31 00:54:54 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-05-31 00:54:54 +0100 |
commit | c8f27a4a886413a15a2a6af4a87b39b901c873a8 (patch) | |
tree | ae4f7c31fa30581ef09b3c29d2673dee922e5845 /crates/ra_ide/src/goto_implementation.rs | |
parent | 383247a9ae8202f20ce6f01d1429c1cd2a11d516 (diff) |
Generate features docs from source
Diffstat (limited to 'crates/ra_ide/src/goto_implementation.rs')
-rw-r--r-- | crates/ra_ide/src/goto_implementation.rs | 210 |
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 | |||
3 | use hir::{Crate, ImplDef, Semantics}; | ||
4 | use ra_ide_db::RootDatabase; | ||
5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; | ||
6 | |||
7 | use 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 | // |=== | ||
18 | pub(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 | |||
43 | fn 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 | |||
65 | fn 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)] | ||
78 | mod 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 | } | ||