diff options
Diffstat (limited to 'crates/ra_assists/src/assists')
-rw-r--r-- | crates/ra_assists/src/assists/auto_import.rs | 210 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/inline_local_variable.rs | 15 |
2 files changed, 225 insertions, 0 deletions
diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/assists/auto_import.rs new file mode 100644 index 000000000..69126a1c9 --- /dev/null +++ b/crates/ra_assists/src/assists/auto_import.rs | |||
@@ -0,0 +1,210 @@ | |||
1 | use hir::db::HirDatabase; | ||
2 | use ra_syntax::{ | ||
3 | ast::{self, AstNode}, | ||
4 | SmolStr, | ||
5 | SyntaxKind::USE_ITEM, | ||
6 | SyntaxNode, | ||
7 | }; | ||
8 | |||
9 | use crate::{ | ||
10 | assist_ctx::{ActionBuilder, Assist, AssistCtx}, | ||
11 | auto_import_text_edit, AssistId, ImportsLocator, | ||
12 | }; | ||
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 | // ``` | ||
23 | // -> | ||
24 | // ``` | ||
25 | // use std::collections::HashMap; | ||
26 | // | ||
27 | // fn main() { | ||
28 | // let map = HashMap<|>::new(); | ||
29 | // } | ||
30 | // ``` | ||
31 | pub(crate) fn auto_import<F: ImportsLocator>( | ||
32 | ctx: AssistCtx<impl HirDatabase>, | ||
33 | imports_locator: &mut F, | ||
34 | ) -> Option<Assist> { | ||
35 | let path_to_import: ast::Path = ctx.find_node_at_offset()?; | ||
36 | let path_to_import_syntax = path_to_import.syntax(); | ||
37 | if path_to_import_syntax.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() { | ||
38 | return None; | ||
39 | } | ||
40 | |||
41 | let module = path_to_import_syntax.ancestors().find_map(ast::Module::cast); | ||
42 | let position = match module.and_then(|it| it.item_list()) { | ||
43 | Some(item_list) => item_list.syntax().clone(), | ||
44 | None => { | ||
45 | let current_file = path_to_import_syntax.ancestors().find_map(ast::SourceFile::cast)?; | ||
46 | current_file.syntax().clone() | ||
47 | } | ||
48 | }; | ||
49 | let source_analyzer = ctx.source_analyzer(&position, None); | ||
50 | let module_with_name_to_import = source_analyzer.module()?; | ||
51 | if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() { | ||
52 | return None; | ||
53 | } | ||
54 | |||
55 | let proposed_imports = imports_locator | ||
56 | .find_imports(&path_to_import_syntax.to_string()) | ||
57 | .into_iter() | ||
58 | .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) | ||
59 | .filter(|use_path| !use_path.segments.is_empty()) | ||
60 | .take(20) | ||
61 | .map(|import| import.to_string()) | ||
62 | .collect::<std::collections::BTreeSet<_>>(); | ||
63 | if proposed_imports.is_empty() { | ||
64 | return None; | ||
65 | } | ||
66 | |||
67 | ctx.add_assist_group(AssistId("auto_import"), "auto import", || { | ||
68 | proposed_imports | ||
69 | .into_iter() | ||
70 | .map(|import| import_to_action(import, &position, &path_to_import_syntax)) | ||
71 | .collect() | ||
72 | }) | ||
73 | } | ||
74 | |||
75 | fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { | ||
76 | let mut action_builder = ActionBuilder::default(); | ||
77 | action_builder.label(format!("Import `{}`", &import)); | ||
78 | auto_import_text_edit( | ||
79 | position, | ||
80 | anchor, | ||
81 | &[SmolStr::new(import)], | ||
82 | action_builder.text_edit_builder(), | ||
83 | ); | ||
84 | action_builder | ||
85 | } | ||
86 | |||
87 | #[cfg(test)] | ||
88 | mod tests { | ||
89 | use super::*; | ||
90 | use crate::helpers::{ | ||
91 | check_assist_with_imports_locator, check_assist_with_imports_locator_not_applicable, | ||
92 | TestImportsLocator, | ||
93 | }; | ||
94 | |||
95 | #[test] | ||
96 | fn applicable_when_found_an_import() { | ||
97 | check_assist_with_imports_locator( | ||
98 | auto_import, | ||
99 | TestImportsLocator::new, | ||
100 | r" | ||
101 | <|>PubStruct | ||
102 | |||
103 | pub mod PubMod { | ||
104 | pub struct PubStruct; | ||
105 | } | ||
106 | ", | ||
107 | r" | ||
108 | <|>use PubMod::PubStruct; | ||
109 | |||
110 | PubStruct | ||
111 | |||
112 | pub mod PubMod { | ||
113 | pub struct PubStruct; | ||
114 | } | ||
115 | ", | ||
116 | ); | ||
117 | } | ||
118 | |||
119 | #[test] | ||
120 | fn applicable_when_found_multiple_imports() { | ||
121 | check_assist_with_imports_locator( | ||
122 | auto_import, | ||
123 | TestImportsLocator::new, | ||
124 | r" | ||
125 | PubSt<|>ruct | ||
126 | |||
127 | pub mod PubMod1 { | ||
128 | pub struct PubStruct; | ||
129 | } | ||
130 | pub mod PubMod2 { | ||
131 | pub struct PubStruct; | ||
132 | } | ||
133 | pub mod PubMod3 { | ||
134 | pub struct PubStruct; | ||
135 | } | ||
136 | ", | ||
137 | r" | ||
138 | use PubMod1::PubStruct; | ||
139 | |||
140 | PubSt<|>ruct | ||
141 | |||
142 | pub mod PubMod1 { | ||
143 | pub struct PubStruct; | ||
144 | } | ||
145 | pub mod PubMod2 { | ||
146 | pub struct PubStruct; | ||
147 | } | ||
148 | pub mod PubMod3 { | ||
149 | pub struct PubStruct; | ||
150 | } | ||
151 | ", | ||
152 | ); | ||
153 | } | ||
154 | |||
155 | #[test] | ||
156 | fn not_applicable_for_already_imported_types() { | ||
157 | check_assist_with_imports_locator_not_applicable( | ||
158 | auto_import, | ||
159 | TestImportsLocator::new, | ||
160 | r" | ||
161 | use PubMod::PubStruct; | ||
162 | |||
163 | PubStruct<|> | ||
164 | |||
165 | pub mod PubMod { | ||
166 | pub struct PubStruct; | ||
167 | } | ||
168 | ", | ||
169 | ); | ||
170 | } | ||
171 | |||
172 | #[test] | ||
173 | fn not_applicable_for_types_with_private_paths() { | ||
174 | check_assist_with_imports_locator_not_applicable( | ||
175 | auto_import, | ||
176 | TestImportsLocator::new, | ||
177 | r" | ||
178 | PrivateStruct<|> | ||
179 | |||
180 | pub mod PubMod { | ||
181 | struct PrivateStruct; | ||
182 | } | ||
183 | ", | ||
184 | ); | ||
185 | } | ||
186 | |||
187 | #[test] | ||
188 | fn not_applicable_when_no_imports_found() { | ||
189 | check_assist_with_imports_locator_not_applicable( | ||
190 | auto_import, | ||
191 | TestImportsLocator::new, | ||
192 | " | ||
193 | PubStruct<|>", | ||
194 | ); | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn not_applicable_in_import_statements() { | ||
199 | check_assist_with_imports_locator_not_applicable( | ||
200 | auto_import, | ||
201 | TestImportsLocator::new, | ||
202 | r" | ||
203 | use PubStruct<|>; | ||
204 | |||
205 | pub mod PubMod { | ||
206 | pub struct PubStruct; | ||
207 | }", | ||
208 | ); | ||
209 | } | ||
210 | } | ||
diff --git a/crates/ra_assists/src/assists/inline_local_variable.rs b/crates/ra_assists/src/assists/inline_local_variable.rs index d0c5c3b8c..83527d904 100644 --- a/crates/ra_assists/src/assists/inline_local_variable.rs +++ b/crates/ra_assists/src/assists/inline_local_variable.rs | |||
@@ -47,6 +47,9 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx<impl HirDatabase>) -> Option< | |||
47 | }; | 47 | }; |
48 | let analyzer = ctx.source_analyzer(bind_pat.syntax(), None); | 48 | let analyzer = ctx.source_analyzer(bind_pat.syntax(), None); |
49 | let refs = analyzer.find_all_refs(&bind_pat); | 49 | let refs = analyzer.find_all_refs(&bind_pat); |
50 | if refs.is_empty() { | ||
51 | return None; | ||
52 | }; | ||
50 | 53 | ||
51 | let mut wrap_in_parens = vec![true; refs.len()]; | 54 | let mut wrap_in_parens = vec![true; refs.len()]; |
52 | 55 | ||
@@ -645,4 +648,16 @@ fn foo() { | |||
645 | }", | 648 | }", |
646 | ); | 649 | ); |
647 | } | 650 | } |
651 | |||
652 | #[test] | ||
653 | fn test_not_applicable_if_variable_unused() { | ||
654 | check_assist_not_applicable( | ||
655 | inline_local_variable, | ||
656 | " | ||
657 | fn foo() { | ||
658 | let <|>a = 0; | ||
659 | } | ||
660 | ", | ||
661 | ) | ||
662 | } | ||
648 | } | 663 | } |