aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists')
-rw-r--r--crates/ra_assists/src/assists/auto_import.rs210
-rw-r--r--crates/ra_assists/src/assists/inline_local_variable.rs15
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 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 ast::{self, AstNode},
4 SmolStr,
5 SyntaxKind::USE_ITEM,
6 SyntaxNode,
7};
8
9use 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// ```
31pub(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
75fn 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)]
88mod 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 "
657fn foo() {
658 let <|>a = 0;
659}
660 ",
661 )
662 }
648} 663}