aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/assist_ctx.rs2
-rw-r--r--crates/ra_assists/src/assists/auto_import.rs181
-rw-r--r--crates/ra_assists/src/doc_tests.rs4
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs19
-rw-r--r--crates/ra_assists/src/lib.rs126
-rw-r--r--crates/ra_hir/src/lib.rs2
-rw-r--r--crates/ra_ide/src/assists.rs7
-rw-r--r--crates/ra_ide/src/imports_locator.rs97
-rw-r--r--crates/ra_ide/src/lib.rs1
-rw-r--r--docs/user/assists.md18
10 files changed, 438 insertions, 19 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 43f0d664b..2ab65ab99 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -101,7 +101,6 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
101 Some(assist) 101 Some(assist)
102 } 102 }
103 103
104 #[allow(dead_code)] // will be used for auto import assist with multiple actions
105 pub(crate) fn add_assist_group( 104 pub(crate) fn add_assist_group(
106 self, 105 self,
107 id: AssistId, 106 id: AssistId,
@@ -168,7 +167,6 @@ pub(crate) struct ActionBuilder {
168} 167}
169 168
170impl ActionBuilder { 169impl ActionBuilder {
171 #[allow(dead_code)]
172 /// Adds a custom label to the action, if it needs to be different from the assist label 170 /// Adds a custom label to the action, if it needs to be different from the assist label
173 pub(crate) fn label(&mut self, label: impl Into<String>) { 171 pub(crate) fn label(&mut self, label: impl Into<String>) {
174 self.label = Some(label.into()) 172 self.label = Some(label.into())
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..fe226521e
--- /dev/null
+++ b/crates/ra_assists/src/assists/auto_import.rs
@@ -0,0 +1,181 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 ast::{self, AstNode},
4 SmolStr, SyntaxElement,
5 SyntaxKind::{NAME_REF, 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<'a, F: ImportsLocator<'a>>(
32 ctx: AssistCtx<impl HirDatabase>,
33 imports_locator: &mut F,
34) -> Option<Assist> {
35 let path: ast::Path = ctx.find_node_at_offset()?;
36 let module = path.syntax().ancestors().find_map(ast::Module::cast);
37 let position = match module.and_then(|it| it.item_list()) {
38 Some(item_list) => item_list.syntax().clone(),
39 None => {
40 let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?;
41 current_file.syntax().clone()
42 }
43 };
44
45 let module_with_name_to_import = ctx.source_analyzer(&position, None).module()?;
46 let name_to_import = hir::InFile {
47 file_id: ctx.frange.file_id.into(),
48 value: &find_applicable_name_ref(ctx.covering_element())?,
49 };
50
51 let proposed_imports =
52 imports_locator.find_imports(name_to_import, module_with_name_to_import)?;
53 if proposed_imports.is_empty() {
54 return None;
55 }
56
57 ctx.add_assist_group(AssistId("auto_import"), "auto import", || {
58 proposed_imports
59 .into_iter()
60 .map(|import| import_to_action(import.to_string(), &position, &path))
61 .collect()
62 })
63}
64
65fn find_applicable_name_ref(element: SyntaxElement) -> Option<ast::NameRef> {
66 if element.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() {
67 None
68 } else if element.kind() == NAME_REF {
69 Some(element.as_node().cloned().and_then(ast::NameRef::cast)?)
70 } else {
71 let parent = element.parent()?;
72 if parent.kind() == NAME_REF {
73 Some(ast::NameRef::cast(parent)?)
74 } else {
75 None
76 }
77 }
78}
79
80fn import_to_action(import: String, position: &SyntaxNode, path: &ast::Path) -> ActionBuilder {
81 let mut action_builder = ActionBuilder::default();
82 action_builder.label(format!("Import `{}`", &import));
83 auto_import_text_edit(
84 position,
85 &path.syntax().clone(),
86 &[SmolStr::new(import)],
87 action_builder.text_edit_builder(),
88 );
89 action_builder
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use crate::helpers::{
96 check_assist_with_imports_locator, check_assist_with_imports_locator_not_applicable,
97 };
98 use hir::Name;
99
100 #[derive(Clone)]
101 struct TestImportsLocator<'a> {
102 import_path: &'a [Name],
103 }
104
105 impl<'a> TestImportsLocator<'a> {
106 fn new(import_path: &'a [Name]) -> Self {
107 TestImportsLocator { import_path }
108 }
109 }
110
111 impl<'a> ImportsLocator<'_> for TestImportsLocator<'_> {
112 fn find_imports(
113 &mut self,
114 _: hir::InFile<&ast::NameRef>,
115 _: hir::Module,
116 ) -> Option<Vec<hir::ModPath>> {
117 if self.import_path.is_empty() {
118 None
119 } else {
120 Some(vec![hir::ModPath {
121 kind: hir::PathKind::Plain,
122 segments: self.import_path.to_owned(),
123 }])
124 }
125 }
126 }
127
128 #[test]
129 fn applicable_when_found_an_import() {
130 let import_path = &[hir::name::known::std, hir::name::known::ops, hir::name::known::Debug];
131 let mut imports_locator = TestImportsLocator::new(import_path);
132 check_assist_with_imports_locator(
133 auto_import,
134 &mut imports_locator,
135 "
136 fn main() {
137 }
138
139 Debug<|>",
140 &format!(
141 "
142 use {};
143
144 fn main() {{
145 }}
146
147 Debug<|>",
148 import_path
149 .into_iter()
150 .map(|name| name.to_string())
151 .collect::<Vec<String>>()
152 .join("::")
153 ),
154 );
155 }
156
157 #[test]
158 fn not_applicable_when_no_imports_found() {
159 let mut imports_locator = TestImportsLocator::new(&[]);
160 check_assist_with_imports_locator_not_applicable(
161 auto_import,
162 &mut imports_locator,
163 "
164 fn main() {
165 }
166
167 Debug<|>",
168 );
169 }
170
171 #[test]
172 fn not_applicable_in_import_statements() {
173 let import_path = &[hir::name::known::std, hir::name::known::ops, hir::name::known::Debug];
174 let mut imports_locator = TestImportsLocator::new(import_path);
175 check_assist_with_imports_locator_not_applicable(
176 auto_import,
177 &mut imports_locator,
178 "use Debug<|>;",
179 );
180 }
181}
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs
index 5dc1ee233..65d51428b 100644
--- a/crates/ra_assists/src/doc_tests.rs
+++ b/crates/ra_assists/src/doc_tests.rs
@@ -11,6 +11,10 @@ use test_utils::{assert_eq_text, extract_range_or_offset};
11use crate::test_db::TestDB; 11use crate::test_db::TestDB;
12 12
13fn check(assist_id: &str, before: &str, after: &str) { 13fn check(assist_id: &str, before: &str, after: &str) {
14 // FIXME we cannot get the imports search functionality here yet, but still need to generate a test and a doc for an assist
15 if assist_id == "auto_import" {
16 return;
17 }
14 let (selection, before) = extract_range_or_offset(before); 18 let (selection, before) = extract_range_or_offset(before);
15 let (db, file_id) = TestDB::with_single_file(&before); 19 let (db, file_id) = TestDB::with_single_file(&before);
16 let frange = FileRange { file_id, range: selection.into() }; 20 let frange = FileRange { file_id, range: selection.into() };
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index 7d84dc8fb..ec4587ce7 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -215,6 +215,25 @@ fn main() {
215} 215}
216 216
217#[test] 217#[test]
218fn doctest_auto_import() {
219 check(
220 "auto_import",
221 r#####"
222fn main() {
223 let map = HashMap<|>::new();
224}
225"#####,
226 r#####"
227use std::collections::HashMap;
228
229fn main() {
230 let map = HashMap<|>::new();
231}
232"#####,
233 )
234}
235
236#[test]
218fn doctest_change_visibility() { 237fn doctest_change_visibility() {
219 check( 238 check(
220 "change_visibility", 239 "change_visibility",
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 3337805a5..4029962f7 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -14,9 +14,9 @@ mod test_db;
14pub mod ast_transform; 14pub mod ast_transform;
15 15
16use either::Either; 16use either::Either;
17use hir::db::HirDatabase; 17use hir::{db::HirDatabase, InFile, ModPath, Module};
18use ra_db::FileRange; 18use ra_db::FileRange;
19use ra_syntax::{TextRange, TextUnit}; 19use ra_syntax::{ast::NameRef, TextRange, TextUnit};
20use ra_text_edit::TextEdit; 20use ra_text_edit::TextEdit;
21 21
22pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; 22pub(crate) use crate::assist_ctx::{Assist, AssistCtx};
@@ -77,6 +77,55 @@ where
77 }) 77 })
78} 78}
79 79
80/// A functionality for locating imports for the given name.
81///
82/// Currently has to be a trait with the real implementation provided by the ra_ide_api crate,
83/// due to the search functionality located there.
84/// Later, this trait should be removed completely and the search functionality moved to a separate crate,
85/// accessible from the ra_assists crate.
86pub trait ImportsLocator<'a> {
87 /// Finds all imports for the given name and the module that contains this name.
88 fn find_imports(
89 &mut self,
90 name_to_import: InFile<&NameRef>,
91 module_with_name_to_import: Module,
92 ) -> Option<Vec<ModPath>>;
93}
94
95/// Return all the assists applicable at the given position
96/// and additional assists that need the imports locator functionality to work.
97///
98/// Assists are returned in the "resolved" state, that is with edit fully
99/// computed.
100pub fn assists_with_imports_locator<'a, H, F: 'a>(
101 db: &H,
102 range: FileRange,
103 mut imports_locator: F,
104) -> Vec<ResolvedAssist>
105where
106 H: HirDatabase + 'static,
107 F: ImportsLocator<'a>,
108{
109 AssistCtx::with_ctx(db, range, true, |ctx| {
110 let mut assists = assists::all()
111 .iter()
112 .map(|f| f(ctx.clone()))
113 .chain(
114 assists::all_with_imports_locator()
115 .iter()
116 .map(|f| f(ctx.clone(), &mut imports_locator)),
117 )
118 .filter_map(std::convert::identity)
119 .map(|a| match a {
120 Assist::Resolved { assist } => assist,
121 Assist::Unresolved { .. } => unreachable!(),
122 })
123 .collect();
124 sort_assists(&mut assists);
125 assists
126 })
127}
128
80/// Return all the assists applicable at the given position. 129/// Return all the assists applicable at the given position.
81/// 130///
82/// Assists are returned in the "resolved" state, that is with edit fully 131/// Assists are returned in the "resolved" state, that is with edit fully
@@ -85,8 +134,6 @@ pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist>
85where 134where
86 H: HirDatabase + 'static, 135 H: HirDatabase + 'static,
87{ 136{
88 use std::cmp::Ordering;
89
90 AssistCtx::with_ctx(db, range, true, |ctx| { 137 AssistCtx::with_ctx(db, range, true, |ctx| {
91 let mut a = assists::all() 138 let mut a = assists::all()
92 .iter() 139 .iter()
@@ -95,19 +142,24 @@ where
95 Assist::Resolved { assist } => assist, 142 Assist::Resolved { assist } => assist,
96 Assist::Unresolved { .. } => unreachable!(), 143 Assist::Unresolved { .. } => unreachable!(),
97 }) 144 })
98 .collect::<Vec<_>>(); 145 .collect();
99 a.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { 146 sort_assists(&mut a);
100 (Some(a), Some(b)) => a.len().cmp(&b.len()),
101 (Some(_), None) => Ordering::Less,
102 (None, Some(_)) => Ordering::Greater,
103 (None, None) => Ordering::Equal,
104 });
105 a 147 a
106 }) 148 })
107} 149}
108 150
151fn sort_assists(assists: &mut Vec<ResolvedAssist>) {
152 use std::cmp::Ordering;
153 assists.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) {
154 (Some(a), Some(b)) => a.len().cmp(&b.len()),
155 (Some(_), None) => Ordering::Less,
156 (None, Some(_)) => Ordering::Greater,
157 (None, None) => Ordering::Equal,
158 });
159}
160
109mod assists { 161mod assists {
110 use crate::{Assist, AssistCtx}; 162 use crate::{Assist, AssistCtx, ImportsLocator};
111 use hir::db::HirDatabase; 163 use hir::db::HirDatabase;
112 164
113 mod add_derive; 165 mod add_derive;
@@ -116,6 +168,7 @@ mod assists {
116 mod add_custom_impl; 168 mod add_custom_impl;
117 mod add_new; 169 mod add_new;
118 mod apply_demorgan; 170 mod apply_demorgan;
171 mod auto_import;
119 mod invert_if; 172 mod invert_if;
120 mod flip_comma; 173 mod flip_comma;
121 mod flip_binexpr; 174 mod flip_binexpr;
@@ -168,6 +221,11 @@ mod assists {
168 early_return::convert_to_guarded_return, 221 early_return::convert_to_guarded_return,
169 ] 222 ]
170 } 223 }
224
225 pub(crate) fn all_with_imports_locator<'a, DB: HirDatabase, F: ImportsLocator<'a>>(
226 ) -> &'a [fn(AssistCtx<DB>, &mut F) -> Option<Assist>] {
227 &[auto_import::auto_import]
228 }
171} 229}
172 230
173#[cfg(test)] 231#[cfg(test)]
@@ -176,7 +234,7 @@ mod helpers {
176 use ra_syntax::TextRange; 234 use ra_syntax::TextRange;
177 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; 235 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
178 236
179 use crate::{test_db::TestDB, Assist, AssistCtx}; 237 use crate::{test_db::TestDB, Assist, AssistCtx, ImportsLocator};
180 238
181 pub(crate) fn check_assist( 239 pub(crate) fn check_assist(
182 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 240 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
@@ -206,6 +264,35 @@ mod helpers {
206 assert_eq_text!(after, &actual); 264 assert_eq_text!(after, &actual);
207 } 265 }
208 266
267 pub(crate) fn check_assist_with_imports_locator<'a, F: ImportsLocator<'a>>(
268 assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>,
269 imports_locator: &mut F,
270 before: &str,
271 after: &str,
272 ) {
273 let (before_cursor_pos, before) = extract_offset(before);
274 let (db, file_id) = TestDB::with_single_file(&before);
275 let frange =
276 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
277 let assist = AssistCtx::with_ctx(&db, frange, true, |ctx| assist(ctx, imports_locator))
278 .expect("code action is not applicable");
279 let action = match assist {
280 Assist::Unresolved { .. } => unreachable!(),
281 Assist::Resolved { assist } => assist.get_first_action(),
282 };
283
284 let actual = action.edit.apply(&before);
285 let actual_cursor_pos = match action.cursor_position {
286 None => action
287 .edit
288 .apply_to_offset(before_cursor_pos)
289 .expect("cursor position is affected by the edit"),
290 Some(off) => off,
291 };
292 let actual = add_cursor(&actual, actual_cursor_pos);
293 assert_eq_text!(after, &actual);
294 }
295
209 pub(crate) fn check_assist_range( 296 pub(crate) fn check_assist_range(
210 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 297 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
211 before: &str, 298 before: &str,
@@ -279,6 +366,19 @@ mod helpers {
279 assert!(assist.is_none()); 366 assert!(assist.is_none());
280 } 367 }
281 368
369 pub(crate) fn check_assist_with_imports_locator_not_applicable<'a, F: ImportsLocator<'a>>(
370 assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>,
371 imports_locator: &mut F,
372 before: &str,
373 ) {
374 let (before_cursor_pos, before) = extract_offset(before);
375 let (db, file_id) = TestDB::with_single_file(&before);
376 let frange =
377 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
378 let assist = AssistCtx::with_ctx(&db, frange, true, |ctx| assist(ctx, imports_locator));
379 assert!(assist.is_none());
380 }
381
282 pub(crate) fn check_assist_range_not_applicable( 382 pub(crate) fn check_assist_range_not_applicable(
283 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 383 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
284 before: &str, 384 before: &str,
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index e1c7b7a20..be6dced53 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -58,6 +58,6 @@ pub use hir_def::{
58 type_ref::Mutability, 58 type_ref::Mutability,
59}; 59};
60pub use hir_expand::{ 60pub use hir_expand::{
61 name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, 61 name, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin,
62}; 62};
63pub use hir_ty::{display::HirDisplay, CallableDef}; 63pub use hir_ty::{display::HirDisplay, CallableDef};
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs
index a936900da..c43c45c65 100644
--- a/crates/ra_ide/src/assists.rs
+++ b/crates/ra_ide/src/assists.rs
@@ -2,8 +2,9 @@
2 2
3use ra_db::{FilePosition, FileRange}; 3use ra_db::{FilePosition, FileRange};
4 4
5use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; 5use crate::{
6 6 db::RootDatabase, imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit,
7};
7use either::Either; 8use either::Either;
8pub use ra_assists::AssistId; 9pub use ra_assists::AssistId;
9use ra_assists::{AssistAction, AssistLabel}; 10use ra_assists::{AssistAction, AssistLabel};
@@ -16,7 +17,7 @@ pub struct Assist {
16} 17}
17 18
18pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { 19pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
19 ra_assists::assists(db, frange) 20 ra_assists::assists_with_imports_locator(db, frange, ImportsLocatorIde::new(db))
20 .into_iter() 21 .into_iter()
21 .map(|assist| { 22 .map(|assist| {
22 let file_id = frange.file_id; 23 let file_id = frange.file_id;
diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs
new file mode 100644
index 000000000..23391ac3b
--- /dev/null
+++ b/crates/ra_ide/src/imports_locator.rs
@@ -0,0 +1,97 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3
4use crate::{
5 db::RootDatabase,
6 references::{classify_name, classify_name_ref, NameDefinition, NameKind},
7 symbol_index::{self, FileSymbol},
8 Query,
9};
10use ast::NameRef;
11use hir::{db::HirDatabase, InFile, ModPath, Module, SourceBinder};
12use itertools::Itertools;
13use ra_assists::ImportsLocator;
14use ra_prof::profile;
15use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
16
17pub(crate) struct ImportsLocatorIde<'a> {
18 source_binder: SourceBinder<'a, RootDatabase>,
19}
20
21impl<'a> ImportsLocatorIde<'a> {
22 pub(crate) fn new(db: &'a RootDatabase) -> Self {
23 Self { source_binder: SourceBinder::new(db) }
24 }
25
26 fn search_for_imports(
27 &mut self,
28 name_to_import: &ast::NameRef,
29 module_with_name_to_import: Module,
30 ) -> Vec<ModPath> {
31 let _p = profile("search_for_imports");
32 let db = self.source_binder.db;
33 let name_to_import = name_to_import.text();
34
35 let project_results = {
36 let mut query = Query::new(name_to_import.to_string());
37 query.exact();
38 query.limit(10);
39 symbol_index::world_symbols(db, query)
40 };
41 let lib_results = {
42 let mut query = Query::new(name_to_import.to_string());
43 query.libs();
44 query.exact();
45 query.limit(10);
46 symbol_index::world_symbols(db, query)
47 };
48
49 project_results
50 .into_iter()
51 .chain(lib_results.into_iter())
52 .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate))
53 .filter_map(|name_definition_to_import| {
54 if let NameKind::Def(module_def) = name_definition_to_import.kind {
55 module_with_name_to_import.find_use_path(db, module_def)
56 } else {
57 None
58 }
59 })
60 .filter(|use_path| !use_path.segments.is_empty())
61 .unique()
62 .collect()
63 }
64
65 fn get_name_definition(
66 &mut self,
67 db: &impl HirDatabase,
68 import_candidate: &FileSymbol,
69 ) -> Option<NameDefinition> {
70 let _p = profile("get_name_definition");
71 let file_id = import_candidate.file_id.into();
72 let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?);
73 let candidate_name_node = if candidate_node.kind() != NAME {
74 candidate_node.children().find(|it| it.kind() == NAME)?
75 } else {
76 candidate_node
77 };
78 classify_name(
79 &mut self.source_binder,
80 hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? },
81 )
82 }
83}
84
85impl<'a> ImportsLocator<'a> for ImportsLocatorIde<'a> {
86 fn find_imports(
87 &mut self,
88 name_to_import: InFile<&NameRef>,
89 module_with_name_to_import: Module,
90 ) -> Option<Vec<ModPath>> {
91 if classify_name_ref(&mut self.source_binder, name_to_import).is_none() {
92 Some(self.search_for_imports(name_to_import.value, module_with_name_to_import))
93 } else {
94 None
95 }
96 }
97}
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 62fe6d2a9..03ad6b2c1 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -30,6 +30,7 @@ mod syntax_highlighting;
30mod parent_module; 30mod parent_module;
31mod references; 31mod references;
32mod impls; 32mod impls;
33mod imports_locator;
33mod assists; 34mod assists;
34mod diagnostics; 35mod diagnostics;
35mod syntax_tree; 36mod syntax_tree;
diff --git a/docs/user/assists.md b/docs/user/assists.md
index ecf206f71..c36c5df6a 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -209,6 +209,24 @@ fn main() {
209} 209}
210``` 210```
211 211
212## `auto_import`
213
214If the name is unresolved, provides all possible imports for it.
215
216```rust
217// BEFORE
218fn main() {
219 let map = HashMap┃::new();
220}
221
222// AFTER
223use std::collections::HashMap;
224
225fn main() {
226 let map = HashMap┃::new();
227}
228```
229
212## `change_visibility` 230## `change_visibility`
213 231
214Adds or changes existing visibility specifier. 232Adds or changes existing visibility specifier.