aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assists/auto_import.rs39
-rw-r--r--crates/ra_assists/src/lib.rs158
-rw-r--r--crates/ra_ide/src/assists.rs2
3 files changed, 21 insertions, 178 deletions
diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/assists/auto_import.rs
index 48ab336b1..219051063 100644
--- a/crates/ra_assists/src/assists/auto_import.rs
+++ b/crates/ra_assists/src/assists/auto_import.rs
@@ -6,8 +6,9 @@ use ra_syntax::{
6 6
7use crate::{ 7use crate::{
8 assist_ctx::{ActionBuilder, Assist, AssistCtx}, 8 assist_ctx::{ActionBuilder, Assist, AssistCtx},
9 auto_import_text_edit, AssistId, ImportsLocator, 9 auto_import_text_edit, AssistId,
10}; 10};
11use ra_ide_db::imports_locator::ImportsLocatorIde;
11 12
12// Assist: auto_import 13// Assist: auto_import
13// 14//
@@ -26,10 +27,7 @@ use crate::{
26// let map = HashMap<|>::new(); 27// let map = HashMap<|>::new();
27// } 28// }
28// ``` 29// ```
29pub(crate) fn auto_import<F: ImportsLocator>( 30pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
30 ctx: AssistCtx,
31 imports_locator: &mut F,
32) -> Option<Assist> {
33 let path_to_import: ast::Path = ctx.find_node_at_offset()?; 31 let path_to_import: ast::Path = ctx.find_node_at_offset()?;
34 let path_to_import_syntax = path_to_import.syntax(); 32 let path_to_import_syntax = path_to_import.syntax();
35 if path_to_import_syntax.ancestors().find_map(ast::UseItem::cast).is_some() { 33 if path_to_import_syntax.ancestors().find_map(ast::UseItem::cast).is_some() {
@@ -52,6 +50,8 @@ pub(crate) fn auto_import<F: ImportsLocator>(
52 return None; 50 return None;
53 } 51 }
54 52
53 let mut imports_locator = ImportsLocatorIde::new(ctx.db);
54
55 let proposed_imports = imports_locator 55 let proposed_imports = imports_locator
56 .find_imports(&name_to_import) 56 .find_imports(&name_to_import)
57 .into_iter() 57 .into_iter()
@@ -81,16 +81,12 @@ fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode)
81#[cfg(test)] 81#[cfg(test)]
82mod tests { 82mod tests {
83 use super::*; 83 use super::*;
84 use crate::helpers::{ 84 use crate::helpers::{check_assist, check_assist_not_applicable};
85 check_assist_with_imports_locator, check_assist_with_imports_locator_not_applicable,
86 TestImportsLocator,
87 };
88 85
89 #[test] 86 #[test]
90 fn applicable_when_found_an_import() { 87 fn applicable_when_found_an_import() {
91 check_assist_with_imports_locator( 88 check_assist(
92 auto_import, 89 auto_import,
93 TestImportsLocator::new,
94 r" 90 r"
95 <|>PubStruct 91 <|>PubStruct
96 92
@@ -112,9 +108,8 @@ mod tests {
112 108
113 #[test] 109 #[test]
114 fn auto_imports_are_merged() { 110 fn auto_imports_are_merged() {
115 check_assist_with_imports_locator( 111 check_assist(
116 auto_import, 112 auto_import,
117 TestImportsLocator::new,
118 r" 113 r"
119 use PubMod::PubStruct1; 114 use PubMod::PubStruct1;
120 115
@@ -148,9 +143,8 @@ mod tests {
148 143
149 #[test] 144 #[test]
150 fn applicable_when_found_multiple_imports() { 145 fn applicable_when_found_multiple_imports() {
151 check_assist_with_imports_locator( 146 check_assist(
152 auto_import, 147 auto_import,
153 TestImportsLocator::new,
154 r" 148 r"
155 PubSt<|>ruct 149 PubSt<|>ruct
156 150
@@ -184,9 +178,8 @@ mod tests {
184 178
185 #[test] 179 #[test]
186 fn not_applicable_for_already_imported_types() { 180 fn not_applicable_for_already_imported_types() {
187 check_assist_with_imports_locator_not_applicable( 181 check_assist_not_applicable(
188 auto_import, 182 auto_import,
189 TestImportsLocator::new,
190 r" 183 r"
191 use PubMod::PubStruct; 184 use PubMod::PubStruct;
192 185
@@ -201,9 +194,8 @@ mod tests {
201 194
202 #[test] 195 #[test]
203 fn not_applicable_for_types_with_private_paths() { 196 fn not_applicable_for_types_with_private_paths() {
204 check_assist_with_imports_locator_not_applicable( 197 check_assist_not_applicable(
205 auto_import, 198 auto_import,
206 TestImportsLocator::new,
207 r" 199 r"
208 PrivateStruct<|> 200 PrivateStruct<|>
209 201
@@ -216,9 +208,8 @@ mod tests {
216 208
217 #[test] 209 #[test]
218 fn not_applicable_when_no_imports_found() { 210 fn not_applicable_when_no_imports_found() {
219 check_assist_with_imports_locator_not_applicable( 211 check_assist_not_applicable(
220 auto_import, 212 auto_import,
221 TestImportsLocator::new,
222 " 213 "
223 PubStruct<|>", 214 PubStruct<|>",
224 ); 215 );
@@ -226,9 +217,8 @@ mod tests {
226 217
227 #[test] 218 #[test]
228 fn not_applicable_in_import_statements() { 219 fn not_applicable_in_import_statements() {
229 check_assist_with_imports_locator_not_applicable( 220 check_assist_not_applicable(
230 auto_import, 221 auto_import,
231 TestImportsLocator::new,
232 r" 222 r"
233 use PubStruct<|>; 223 use PubStruct<|>;
234 224
@@ -240,9 +230,8 @@ mod tests {
240 230
241 #[test] 231 #[test]
242 fn function_import() { 232 fn function_import() {
243 check_assist_with_imports_locator( 233 check_assist(
244 auto_import, 234 auto_import,
245 TestImportsLocator::new,
246 r" 235 r"
247 test_function<|> 236 test_function<|>
248 237
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index ad8438b6c..8285e93a4 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -12,9 +12,8 @@ mod doc_tests;
12pub mod ast_transform; 12pub mod ast_transform;
13 13
14use either::Either; 14use either::Either;
15use hir::ModuleDef;
16use ra_db::FileRange; 15use ra_db::FileRange;
17use ra_ide_db::{imports_locator::ImportsLocatorIde, RootDatabase}; 16use ra_ide_db::RootDatabase;
18use ra_syntax::{TextRange, TextUnit}; 17use ra_syntax::{TextRange, TextUnit};
19use ra_text_edit::TextEdit; 18use ra_text_edit::TextEdit;
20 19
@@ -73,50 +72,6 @@ pub fn applicable_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe
73 }) 72 })
74} 73}
75 74
76/// A functionality for locating imports for the given name.
77///
78/// Currently has to be a trait with the real implementation provided by the ra_ide_api crate,
79/// due to the search functionality located there.
80/// Later, this trait should be removed completely and the search functionality moved to a separate crate,
81/// accessible from the ra_assists crate.
82pub trait ImportsLocator {
83 /// Finds all imports for the given name and the module that contains this name.
84 fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef>;
85}
86
87impl ImportsLocator for ImportsLocatorIde<'_> {
88 fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> {
89 self.find_imports(name_to_import)
90 }
91}
92
93/// Return all the assists applicable at the given position
94/// and additional assists that need the imports locator functionality to work.
95///
96/// Assists are returned in the "resolved" state, that is with edit fully
97/// computed.
98pub fn assists_with_imports_locator(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
99 let mut imports_locator = ImportsLocatorIde::new(db);
100 AssistCtx::with_ctx(db, range, true, |ctx| {
101 let mut assists = assists::all()
102 .iter()
103 .map(|f| f(ctx.clone()))
104 .chain(
105 assists::all_with_imports_locator()
106 .iter()
107 .map(|f| f(ctx.clone(), &mut imports_locator)),
108 )
109 .filter_map(std::convert::identity)
110 .map(|a| match a {
111 Assist::Resolved { assist } => assist,
112 Assist::Unresolved { .. } => unreachable!(),
113 })
114 .collect();
115 sort_assists(&mut assists);
116 assists
117 })
118}
119
120/// Return all the assists applicable at the given position. 75/// Return all the assists applicable at the given position.
121/// 76///
122/// Assists are returned in the "resolved" state, that is with edit fully 77/// Assists are returned in the "resolved" state, that is with edit fully
@@ -147,7 +102,7 @@ fn sort_assists(assists: &mut Vec<ResolvedAssist>) {
147} 102}
148 103
149mod assists { 104mod assists {
150 use crate::{Assist, AssistCtx, ImportsLocator}; 105 use crate::{Assist, AssistCtx};
151 106
152 mod add_derive; 107 mod add_derive;
153 mod add_explicit_type; 108 mod add_explicit_type;
@@ -206,72 +161,19 @@ mod assists {
206 raw_string::make_usual_string, 161 raw_string::make_usual_string,
207 raw_string::remove_hash, 162 raw_string::remove_hash,
208 early_return::convert_to_guarded_return, 163 early_return::convert_to_guarded_return,
164 auto_import::auto_import,
209 ] 165 ]
210 } 166 }
211
212 pub(crate) fn all_with_imports_locator<'a, F: ImportsLocator>(
213 ) -> &'a [fn(AssistCtx, &mut F) -> Option<Assist>] {
214 &[auto_import::auto_import]
215 }
216} 167}
217 168
218#[cfg(test)] 169#[cfg(test)]
219mod helpers { 170mod helpers {
220 use hir::db::DefDatabase; 171 use ra_db::{fixture::WithFixture, FileRange};
221 use ra_db::{fixture::WithFixture, FileId, FileRange}; 172 use ra_ide_db::RootDatabase;
222 use ra_syntax::TextRange; 173 use ra_syntax::TextRange;
223 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; 174 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
224 175
225 use crate::{Assist, AssistCtx, ImportsLocator}; 176 use crate::{Assist, AssistCtx};
226 use ra_ide_db::RootDatabase;
227 use std::sync::Arc;
228
229 // FIXME remove the `ModuleDefId` reexport from `ra_hir` when this gets removed.
230 pub(crate) struct TestImportsLocator {
231 db: Arc<RootDatabase>,
232 test_file_id: FileId,
233 }
234
235 impl TestImportsLocator {
236 pub(crate) fn new(db: Arc<RootDatabase>, test_file_id: FileId) -> Self {
237 TestImportsLocator { db, test_file_id }
238 }
239 }
240
241 impl ImportsLocator for TestImportsLocator {
242 fn find_imports(&mut self, name_to_import: &str) -> Vec<hir::ModuleDef> {
243 let crate_def_map = self.db.crate_def_map(self.db.test_crate());
244 let mut findings = Vec::new();
245
246 let mut module_ids_to_process =
247 crate_def_map.modules_for_file(self.test_file_id).collect::<Vec<_>>();
248
249 while !module_ids_to_process.is_empty() {
250 let mut more_ids_to_process = Vec::new();
251 for local_module_id in module_ids_to_process.drain(..) {
252 for (name, namespace_data) in
253 crate_def_map[local_module_id].scope.entries_without_primitives()
254 {
255 let found_a_match = &name.to_string() == name_to_import;
256 vec![namespace_data.types, namespace_data.values]
257 .into_iter()
258 .filter_map(std::convert::identity)
259 .for_each(|(module_def_id, _)| {
260 if found_a_match {
261 findings.push(module_def_id.into());
262 }
263 if let hir::ModuleDefId::ModuleId(module_id) = module_def_id {
264 more_ids_to_process.push(module_id.local_id);
265 }
266 });
267 }
268 }
269 module_ids_to_process = more_ids_to_process;
270 }
271
272 findings
273 }
274 }
275 177
276 pub(crate) fn check_assist(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) { 178 pub(crate) fn check_assist(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) {
277 let (before_cursor_pos, before) = extract_offset(before); 179 let (before_cursor_pos, before) = extract_offset(before);
@@ -297,38 +199,6 @@ mod helpers {
297 assert_eq_text!(after, &actual); 199 assert_eq_text!(after, &actual);
298 } 200 }
299 201
300 pub(crate) fn check_assist_with_imports_locator<F: ImportsLocator>(
301 assist: fn(AssistCtx, &mut F) -> Option<Assist>,
302 imports_locator_provider: fn(db: Arc<RootDatabase>, file_id: FileId) -> F,
303 before: &str,
304 after: &str,
305 ) {
306 let (before_cursor_pos, before) = extract_offset(before);
307 let (db, file_id) = RootDatabase::with_single_file(&before);
308 let db = Arc::new(db);
309 let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id);
310 let frange =
311 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
312 let assist =
313 AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator))
314 .expect("code action is not applicable");
315 let action = match assist {
316 Assist::Unresolved { .. } => unreachable!(),
317 Assist::Resolved { assist } => assist.get_first_action(),
318 };
319
320 let actual = action.edit.apply(&before);
321 let actual_cursor_pos = match action.cursor_position {
322 None => action
323 .edit
324 .apply_to_offset(before_cursor_pos)
325 .expect("cursor position is affected by the edit"),
326 Some(off) => off,
327 };
328 let actual = add_cursor(&actual, actual_cursor_pos);
329 assert_eq_text!(after, &actual);
330 }
331
332 pub(crate) fn check_assist_range( 202 pub(crate) fn check_assist_range(
333 assist: fn(AssistCtx) -> Option<Assist>, 203 assist: fn(AssistCtx) -> Option<Assist>,
334 before: &str, 204 before: &str,
@@ -402,22 +272,6 @@ mod helpers {
402 assert!(assist.is_none()); 272 assert!(assist.is_none());
403 } 273 }
404 274
405 pub(crate) fn check_assist_with_imports_locator_not_applicable<F: ImportsLocator>(
406 assist: fn(AssistCtx, &mut F) -> Option<Assist>,
407 imports_locator_provider: fn(db: Arc<RootDatabase>, file_id: FileId) -> F,
408 before: &str,
409 ) {
410 let (before_cursor_pos, before) = extract_offset(before);
411 let (db, file_id) = RootDatabase::with_single_file(&before);
412 let db = Arc::new(db);
413 let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id);
414 let frange =
415 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
416 let assist =
417 AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator));
418 assert!(assist.is_none());
419 }
420
421 pub(crate) fn check_assist_range_not_applicable( 275 pub(crate) fn check_assist_range_not_applicable(
422 assist: fn(AssistCtx) -> Option<Assist>, 276 assist: fn(AssistCtx) -> Option<Assist>,
423 before: &str, 277 before: &str,
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs
index 6ee617e79..c3b2c638b 100644
--- a/crates/ra_ide/src/assists.rs
+++ b/crates/ra_ide/src/assists.rs
@@ -17,7 +17,7 @@ pub struct Assist {
17} 17}
18 18
19pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { 19pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
20 ra_assists::assists_with_imports_locator(db, frange) 20 ra_assists::assists(db, frange)
21 .into_iter() 21 .into_iter()
22 .map(|assist| { 22 .map(|assist| {
23 let file_id = frange.file_id; 23 let file_id = frange.file_id;