aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/auto_import.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-02-07 16:28:33 +0000
committerGitHub <[email protected]>2020-02-07 16:28:33 +0000
commit5397f05bfe7f3b18229a65040c6685e762b2f9a3 (patch)
treea3c4aab400ffe1c84bd33e094a047798e7136d2d /crates/ra_assists/src/handlers/auto_import.rs
parent1996762b1f2b9cb196cc879f0ce26d28a3c450c8 (diff)
parentd00add1f1fec59494c3c1a99c27937ae3891458d (diff)
Merge #3049
3049: Introduce assists utils r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_assists/src/handlers/auto_import.rs')
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs258
1 files changed, 258 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
new file mode 100644
index 000000000..84b5474f9
--- /dev/null
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -0,0 +1,258 @@
1use hir::ModPath;
2use ra_ide_db::imports_locator::ImportsLocator;
3use ra_syntax::{
4 ast::{self, AstNode},
5 SyntaxNode,
6};
7
8use crate::{
9 assist_ctx::{ActionBuilder, Assist, AssistCtx},
10 auto_import_text_edit, AssistId,
11};
12use std::collections::BTreeSet;
13
14// Assist: auto_import
15//
16// If the name is unresolved, provides all possible imports for it.
17//
18// ```
19// fn main() {
20// let map = HashMap<|>::new();
21// }
22// # pub mod std { pub mod collections { pub struct HashMap { } } }
23// ```
24// ->
25// ```
26// use std::collections::HashMap;
27//
28// fn main() {
29// let map = HashMap::new();
30// }
31// # pub mod std { pub mod collections { pub struct HashMap { } } }
32// ```
33pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
34 let path_to_import: ast::Path = ctx.find_node_at_offset()?;
35 let path_to_import_syntax = path_to_import.syntax();
36 if path_to_import_syntax.ancestors().find_map(ast::UseItem::cast).is_some() {
37 return None;
38 }
39 let name_to_import =
40 path_to_import_syntax.descendants().find_map(ast::NameRef::cast)?.syntax().to_string();
41
42 let module = path_to_import_syntax.ancestors().find_map(ast::Module::cast);
43 let position = match module.and_then(|it| it.item_list()) {
44 Some(item_list) => item_list.syntax().clone(),
45 None => {
46 let current_file = path_to_import_syntax.ancestors().find_map(ast::SourceFile::cast)?;
47 current_file.syntax().clone()
48 }
49 };
50 let source_analyzer = ctx.source_analyzer(&position, None);
51 let module_with_name_to_import = source_analyzer.module()?;
52 if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() {
53 return None;
54 }
55
56 let mut imports_locator = ImportsLocator::new(ctx.db);
57
58 let proposed_imports = imports_locator
59 .find_imports(&name_to_import)
60 .into_iter()
61 .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def))
62 .filter(|use_path| !use_path.segments.is_empty())
63 .take(20)
64 .collect::<BTreeSet<_>>();
65
66 if proposed_imports.is_empty() {
67 return None;
68 }
69
70 ctx.add_assist_group(AssistId("auto_import"), format!("Import {}", name_to_import), || {
71 proposed_imports
72 .into_iter()
73 .map(|import| import_to_action(import, &position, &path_to_import_syntax))
74 .collect()
75 })
76}
77
78fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
79 let mut action_builder = ActionBuilder::default();
80 action_builder.label(format!("Import `{}`", &import));
81 auto_import_text_edit(position, anchor, &import, action_builder.text_edit_builder());
82 action_builder
83}
84
85#[cfg(test)]
86mod tests {
87 use crate::helpers::{check_assist, check_assist_not_applicable};
88
89 use super::*;
90
91 #[test]
92 fn applicable_when_found_an_import() {
93 check_assist(
94 auto_import,
95 r"
96 <|>PubStruct
97
98 pub mod PubMod {
99 pub struct PubStruct;
100 }
101 ",
102 r"
103 <|>use PubMod::PubStruct;
104
105 PubStruct
106
107 pub mod PubMod {
108 pub struct PubStruct;
109 }
110 ",
111 );
112 }
113
114 #[test]
115 fn auto_imports_are_merged() {
116 check_assist(
117 auto_import,
118 r"
119 use PubMod::PubStruct1;
120
121 struct Test {
122 test: Pub<|>Struct2<u8>,
123 }
124
125 pub mod PubMod {
126 pub struct PubStruct1;
127 pub struct PubStruct2<T> {
128 _t: T,
129 }
130 }
131 ",
132 r"
133 use PubMod::{PubStruct2, PubStruct1};
134
135 struct Test {
136 test: Pub<|>Struct2<u8>,
137 }
138
139 pub mod PubMod {
140 pub struct PubStruct1;
141 pub struct PubStruct2<T> {
142 _t: T,
143 }
144 }
145 ",
146 );
147 }
148
149 #[test]
150 fn applicable_when_found_multiple_imports() {
151 check_assist(
152 auto_import,
153 r"
154 PubSt<|>ruct
155
156 pub mod PubMod1 {
157 pub struct PubStruct;
158 }
159 pub mod PubMod2 {
160 pub struct PubStruct;
161 }
162 pub mod PubMod3 {
163 pub struct PubStruct;
164 }
165 ",
166 r"
167 use PubMod1::PubStruct;
168
169 PubSt<|>ruct
170
171 pub mod PubMod1 {
172 pub struct PubStruct;
173 }
174 pub mod PubMod2 {
175 pub struct PubStruct;
176 }
177 pub mod PubMod3 {
178 pub struct PubStruct;
179 }
180 ",
181 );
182 }
183
184 #[test]
185 fn not_applicable_for_already_imported_types() {
186 check_assist_not_applicable(
187 auto_import,
188 r"
189 use PubMod::PubStruct;
190
191 PubStruct<|>
192
193 pub mod PubMod {
194 pub struct PubStruct;
195 }
196 ",
197 );
198 }
199
200 #[test]
201 fn not_applicable_for_types_with_private_paths() {
202 check_assist_not_applicable(
203 auto_import,
204 r"
205 PrivateStruct<|>
206
207 pub mod PubMod {
208 struct PrivateStruct;
209 }
210 ",
211 );
212 }
213
214 #[test]
215 fn not_applicable_when_no_imports_found() {
216 check_assist_not_applicable(
217 auto_import,
218 "
219 PubStruct<|>",
220 );
221 }
222
223 #[test]
224 fn not_applicable_in_import_statements() {
225 check_assist_not_applicable(
226 auto_import,
227 r"
228 use PubStruct<|>;
229
230 pub mod PubMod {
231 pub struct PubStruct;
232 }",
233 );
234 }
235
236 #[test]
237 fn function_import() {
238 check_assist(
239 auto_import,
240 r"
241 test_function<|>
242
243 pub mod PubMod {
244 pub fn test_function() {};
245 }
246 ",
247 r"
248 use PubMod::test_function;
249
250 test_function<|>
251
252 pub mod PubMod {
253 pub fn test_function() {};
254 }
255 ",
256 );
257 }
258}