diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-02-06 16:50:01 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-02-06 16:50:01 +0000 |
commit | 895cdb588314f4e04529d1adf8cd232856497dad (patch) | |
tree | 6466cb45c30478eb0c413c25cc6d07cf23d39d3a /crates/ra_assists/src/lib.rs | |
parent | 8b957caf83b18b3fea153f770bfd0ed979227eff (diff) | |
parent | d1e8b8d134da802eecef5cbcd5486bd542ad75b5 (diff) |
Merge #3034
3034: Remove ImportLocator hack r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_assists/src/lib.rs')
-rw-r--r-- | crates/ra_assists/src/lib.rs | 215 |
1 files changed, 34 insertions, 181 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 625ebc4a2..1343043dd 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -9,13 +9,11 @@ mod assist_ctx; | |||
9 | mod marks; | 9 | mod marks; |
10 | #[cfg(test)] | 10 | #[cfg(test)] |
11 | mod doc_tests; | 11 | mod doc_tests; |
12 | #[cfg(test)] | ||
13 | mod test_db; | ||
14 | pub mod ast_transform; | 12 | pub mod ast_transform; |
15 | 13 | ||
16 | use either::Either; | 14 | use either::Either; |
17 | use hir::{db::HirDatabase, ModuleDef}; | ||
18 | use ra_db::FileRange; | 15 | use ra_db::FileRange; |
16 | use ra_ide_db::RootDatabase; | ||
19 | use ra_syntax::{TextRange, TextUnit}; | 17 | use ra_syntax::{TextRange, TextUnit}; |
20 | use ra_text_edit::TextEdit; | 18 | use ra_text_edit::TextEdit; |
21 | 19 | ||
@@ -61,10 +59,7 @@ impl ResolvedAssist { | |||
61 | /// | 59 | /// |
62 | /// Assists are returned in the "unresolved" state, that is only labels are | 60 | /// Assists are returned in the "unresolved" state, that is only labels are |
63 | /// returned, without actual edits. | 61 | /// returned, without actual edits. |
64 | pub fn applicable_assists<H>(db: &H, range: FileRange) -> Vec<AssistLabel> | 62 | pub fn applicable_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { |
65 | where | ||
66 | H: HirDatabase + 'static, | ||
67 | { | ||
68 | AssistCtx::with_ctx(db, range, false, |ctx| { | 63 | AssistCtx::with_ctx(db, range, false, |ctx| { |
69 | assists::all() | 64 | assists::all() |
70 | .iter() | 65 | .iter() |
@@ -77,59 +72,11 @@ where | |||
77 | }) | 72 | }) |
78 | } | 73 | } |
79 | 74 | ||
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. | ||
86 | pub trait ImportsLocator { | ||
87 | /// Finds all imports for the given name and the module that contains this name. | ||
88 | fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef>; | ||
89 | } | ||
90 | |||
91 | /// Return all the assists applicable at the given position | ||
92 | /// and additional assists that need the imports locator functionality to work. | ||
93 | /// | ||
94 | /// Assists are returned in the "resolved" state, that is with edit fully | ||
95 | /// computed. | ||
96 | pub fn assists_with_imports_locator<H, F>( | ||
97 | db: &H, | ||
98 | range: FileRange, | ||
99 | mut imports_locator: F, | ||
100 | ) -> Vec<ResolvedAssist> | ||
101 | where | ||
102 | H: HirDatabase + 'static, | ||
103 | F: ImportsLocator, | ||
104 | { | ||
105 | AssistCtx::with_ctx(db, range, true, |ctx| { | ||
106 | let mut assists = assists::all() | ||
107 | .iter() | ||
108 | .map(|f| f(ctx.clone())) | ||
109 | .chain( | ||
110 | assists::all_with_imports_locator() | ||
111 | .iter() | ||
112 | .map(|f| f(ctx.clone(), &mut imports_locator)), | ||
113 | ) | ||
114 | .filter_map(std::convert::identity) | ||
115 | .map(|a| match a { | ||
116 | Assist::Resolved { assist } => assist, | ||
117 | Assist::Unresolved { .. } => unreachable!(), | ||
118 | }) | ||
119 | .collect(); | ||
120 | sort_assists(&mut assists); | ||
121 | assists | ||
122 | }) | ||
123 | } | ||
124 | |||
125 | /// Return all the assists applicable at the given position. | 75 | /// Return all the assists applicable at the given position. |
126 | /// | 76 | /// |
127 | /// 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 |
128 | /// computed. | 78 | /// computed. |
129 | pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist> | 79 | pub fn assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { |
130 | where | ||
131 | H: HirDatabase + 'static, | ||
132 | { | ||
133 | AssistCtx::with_ctx(db, range, true, |ctx| { | 80 | AssistCtx::with_ctx(db, range, true, |ctx| { |
134 | let mut a = assists::all() | 81 | let mut a = assists::all() |
135 | .iter() | 82 | .iter() |
@@ -155,8 +102,7 @@ fn sort_assists(assists: &mut Vec<ResolvedAssist>) { | |||
155 | } | 102 | } |
156 | 103 | ||
157 | mod assists { | 104 | mod assists { |
158 | use crate::{Assist, AssistCtx, ImportsLocator}; | 105 | use crate::{Assist, AssistCtx}; |
159 | use hir::db::HirDatabase; | ||
160 | 106 | ||
161 | mod add_derive; | 107 | mod add_derive; |
162 | mod add_explicit_type; | 108 | mod add_explicit_type; |
@@ -184,7 +130,7 @@ mod assists { | |||
184 | mod move_bounds; | 130 | mod move_bounds; |
185 | mod early_return; | 131 | mod early_return; |
186 | 132 | ||
187 | pub(crate) fn all<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { | 133 | pub(crate) fn all() -> &'static [fn(AssistCtx) -> Option<Assist>] { |
188 | &[ | 134 | &[ |
189 | add_derive::add_derive, | 135 | add_derive::add_derive, |
190 | add_explicit_type::add_explicit_type, | 136 | add_explicit_type::add_explicit_type, |
@@ -215,79 +161,34 @@ mod assists { | |||
215 | raw_string::make_usual_string, | 161 | raw_string::make_usual_string, |
216 | raw_string::remove_hash, | 162 | raw_string::remove_hash, |
217 | early_return::convert_to_guarded_return, | 163 | early_return::convert_to_guarded_return, |
164 | auto_import::auto_import, | ||
218 | ] | 165 | ] |
219 | } | 166 | } |
220 | |||
221 | pub(crate) fn all_with_imports_locator<'a, DB: HirDatabase, F: ImportsLocator>( | ||
222 | ) -> &'a [fn(AssistCtx<DB>, &mut F) -> Option<Assist>] { | ||
223 | &[auto_import::auto_import] | ||
224 | } | ||
225 | } | 167 | } |
226 | 168 | ||
227 | #[cfg(test)] | 169 | #[cfg(test)] |
228 | mod helpers { | 170 | mod helpers { |
229 | use hir::db::DefDatabase; | ||
230 | use ra_db::{fixture::WithFixture, FileId, FileRange}; | ||
231 | use ra_syntax::TextRange; | ||
232 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; | ||
233 | |||
234 | use crate::{test_db::TestDB, Assist, AssistCtx, ImportsLocator}; | ||
235 | use std::sync::Arc; | 171 | use std::sync::Arc; |
236 | 172 | ||
237 | // FIXME remove the `ModuleDefId` reexport from `ra_hir` when this gets removed. | 173 | use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; |
238 | pub(crate) struct TestImportsLocator { | 174 | use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; |
239 | db: Arc<TestDB>, | 175 | use ra_syntax::TextRange; |
240 | test_file_id: FileId, | 176 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; |
241 | } | ||
242 | |||
243 | impl TestImportsLocator { | ||
244 | pub(crate) fn new(db: Arc<TestDB>, test_file_id: FileId) -> Self { | ||
245 | TestImportsLocator { db, test_file_id } | ||
246 | } | ||
247 | } | ||
248 | 177 | ||
249 | impl ImportsLocator for TestImportsLocator { | 178 | use crate::{Assist, AssistCtx}; |
250 | fn find_imports(&mut self, name_to_import: &str) -> Vec<hir::ModuleDef> { | ||
251 | let crate_def_map = self.db.crate_def_map(self.db.test_crate()); | ||
252 | let mut findings = Vec::new(); | ||
253 | |||
254 | let mut module_ids_to_process = | ||
255 | crate_def_map.modules_for_file(self.test_file_id).collect::<Vec<_>>(); | ||
256 | |||
257 | while !module_ids_to_process.is_empty() { | ||
258 | let mut more_ids_to_process = Vec::new(); | ||
259 | for local_module_id in module_ids_to_process.drain(..) { | ||
260 | for (name, namespace_data) in | ||
261 | crate_def_map[local_module_id].scope.entries_without_primitives() | ||
262 | { | ||
263 | let found_a_match = &name.to_string() == name_to_import; | ||
264 | vec![namespace_data.types, namespace_data.values] | ||
265 | .into_iter() | ||
266 | .filter_map(std::convert::identity) | ||
267 | .for_each(|(module_def_id, _)| { | ||
268 | if found_a_match { | ||
269 | findings.push(module_def_id.into()); | ||
270 | } | ||
271 | if let hir::ModuleDefId::ModuleId(module_id) = module_def_id { | ||
272 | more_ids_to_process.push(module_id.local_id); | ||
273 | } | ||
274 | }); | ||
275 | } | ||
276 | } | ||
277 | module_ids_to_process = more_ids_to_process; | ||
278 | } | ||
279 | 179 | ||
280 | findings | 180 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { |
281 | } | 181 | let (mut db, file_id) = RootDatabase::with_single_file(text); |
182 | // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`, | ||
183 | // but it looks like this might need specialization? :( | ||
184 | let local_roots = vec![db.file_source_root(file_id)]; | ||
185 | db.set_local_roots(Arc::new(local_roots)); | ||
186 | (db, file_id) | ||
282 | } | 187 | } |
283 | 188 | ||
284 | pub(crate) fn check_assist( | 189 | pub(crate) fn check_assist(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) { |
285 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, | ||
286 | before: &str, | ||
287 | after: &str, | ||
288 | ) { | ||
289 | let (before_cursor_pos, before) = extract_offset(before); | 190 | let (before_cursor_pos, before) = extract_offset(before); |
290 | let (db, file_id) = TestDB::with_single_file(&before); | 191 | let (db, file_id) = with_single_file(&before); |
291 | let frange = | 192 | let frange = |
292 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 193 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
293 | let assist = | 194 | let assist = |
@@ -309,45 +210,13 @@ mod helpers { | |||
309 | assert_eq_text!(after, &actual); | 210 | assert_eq_text!(after, &actual); |
310 | } | 211 | } |
311 | 212 | ||
312 | pub(crate) fn check_assist_with_imports_locator<F: ImportsLocator>( | ||
313 | assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>, | ||
314 | imports_locator_provider: fn(db: Arc<TestDB>, file_id: FileId) -> F, | ||
315 | before: &str, | ||
316 | after: &str, | ||
317 | ) { | ||
318 | let (before_cursor_pos, before) = extract_offset(before); | ||
319 | let (db, file_id) = TestDB::with_single_file(&before); | ||
320 | let db = Arc::new(db); | ||
321 | let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id); | ||
322 | let frange = | ||
323 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | ||
324 | let assist = | ||
325 | AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator)) | ||
326 | .expect("code action is not applicable"); | ||
327 | let action = match assist { | ||
328 | Assist::Unresolved { .. } => unreachable!(), | ||
329 | Assist::Resolved { assist } => assist.get_first_action(), | ||
330 | }; | ||
331 | |||
332 | let actual = action.edit.apply(&before); | ||
333 | let actual_cursor_pos = match action.cursor_position { | ||
334 | None => action | ||
335 | .edit | ||
336 | .apply_to_offset(before_cursor_pos) | ||
337 | .expect("cursor position is affected by the edit"), | ||
338 | Some(off) => off, | ||
339 | }; | ||
340 | let actual = add_cursor(&actual, actual_cursor_pos); | ||
341 | assert_eq_text!(after, &actual); | ||
342 | } | ||
343 | |||
344 | pub(crate) fn check_assist_range( | 213 | pub(crate) fn check_assist_range( |
345 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, | 214 | assist: fn(AssistCtx) -> Option<Assist>, |
346 | before: &str, | 215 | before: &str, |
347 | after: &str, | 216 | after: &str, |
348 | ) { | 217 | ) { |
349 | let (range, before) = extract_range(before); | 218 | let (range, before) = extract_range(before); |
350 | let (db, file_id) = TestDB::with_single_file(&before); | 219 | let (db, file_id) = with_single_file(&before); |
351 | let frange = FileRange { file_id, range }; | 220 | let frange = FileRange { file_id, range }; |
352 | let assist = | 221 | let assist = |
353 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 222 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
@@ -364,12 +233,12 @@ mod helpers { | |||
364 | } | 233 | } |
365 | 234 | ||
366 | pub(crate) fn check_assist_target( | 235 | pub(crate) fn check_assist_target( |
367 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, | 236 | assist: fn(AssistCtx) -> Option<Assist>, |
368 | before: &str, | 237 | before: &str, |
369 | target: &str, | 238 | target: &str, |
370 | ) { | 239 | ) { |
371 | let (before_cursor_pos, before) = extract_offset(before); | 240 | let (before_cursor_pos, before) = extract_offset(before); |
372 | let (db, file_id) = TestDB::with_single_file(&before); | 241 | let (db, file_id) = with_single_file(&before); |
373 | let frange = | 242 | let frange = |
374 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 243 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
375 | let assist = | 244 | let assist = |
@@ -384,12 +253,12 @@ mod helpers { | |||
384 | } | 253 | } |
385 | 254 | ||
386 | pub(crate) fn check_assist_range_target( | 255 | pub(crate) fn check_assist_range_target( |
387 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, | 256 | assist: fn(AssistCtx) -> Option<Assist>, |
388 | before: &str, | 257 | before: &str, |
389 | target: &str, | 258 | target: &str, |
390 | ) { | 259 | ) { |
391 | let (range, before) = extract_range(before); | 260 | let (range, before) = extract_range(before); |
392 | let (db, file_id) = TestDB::with_single_file(&before); | 261 | let (db, file_id) = with_single_file(&before); |
393 | let frange = FileRange { file_id, range }; | 262 | let frange = FileRange { file_id, range }; |
394 | let assist = | 263 | let assist = |
395 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 264 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
@@ -403,39 +272,23 @@ mod helpers { | |||
403 | } | 272 | } |
404 | 273 | ||
405 | pub(crate) fn check_assist_not_applicable( | 274 | pub(crate) fn check_assist_not_applicable( |
406 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, | 275 | assist: fn(AssistCtx) -> Option<Assist>, |
407 | before: &str, | 276 | before: &str, |
408 | ) { | 277 | ) { |
409 | let (before_cursor_pos, before) = extract_offset(before); | 278 | let (before_cursor_pos, before) = extract_offset(before); |
410 | let (db, file_id) = TestDB::with_single_file(&before); | 279 | let (db, file_id) = with_single_file(&before); |
411 | let frange = | 280 | let frange = |
412 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 281 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
413 | let assist = AssistCtx::with_ctx(&db, frange, true, assist); | 282 | let assist = AssistCtx::with_ctx(&db, frange, true, assist); |
414 | assert!(assist.is_none()); | 283 | assert!(assist.is_none()); |
415 | } | 284 | } |
416 | 285 | ||
417 | pub(crate) fn check_assist_with_imports_locator_not_applicable<F: ImportsLocator>( | ||
418 | assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>, | ||
419 | imports_locator_provider: fn(db: Arc<TestDB>, file_id: FileId) -> F, | ||
420 | before: &str, | ||
421 | ) { | ||
422 | let (before_cursor_pos, before) = extract_offset(before); | ||
423 | let (db, file_id) = TestDB::with_single_file(&before); | ||
424 | let db = Arc::new(db); | ||
425 | let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id); | ||
426 | let frange = | ||
427 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | ||
428 | let assist = | ||
429 | AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator)); | ||
430 | assert!(assist.is_none()); | ||
431 | } | ||
432 | |||
433 | pub(crate) fn check_assist_range_not_applicable( | 286 | pub(crate) fn check_assist_range_not_applicable( |
434 | assist: fn(AssistCtx<TestDB>) -> Option<Assist>, | 287 | assist: fn(AssistCtx) -> Option<Assist>, |
435 | before: &str, | 288 | before: &str, |
436 | ) { | 289 | ) { |
437 | let (range, before) = extract_range(before); | 290 | let (range, before) = extract_range(before); |
438 | let (db, file_id) = TestDB::with_single_file(&before); | 291 | let (db, file_id) = with_single_file(&before); |
439 | let frange = FileRange { file_id, range }; | 292 | let frange = FileRange { file_id, range }; |
440 | let assist = AssistCtx::with_ctx(&db, frange, true, assist); | 293 | let assist = AssistCtx::with_ctx(&db, frange, true, assist); |
441 | assert!(assist.is_none()); | 294 | assert!(assist.is_none()); |
@@ -444,17 +297,17 @@ mod helpers { | |||
444 | 297 | ||
445 | #[cfg(test)] | 298 | #[cfg(test)] |
446 | mod tests { | 299 | mod tests { |
447 | use ra_db::{fixture::WithFixture, FileRange}; | 300 | use ra_db::FileRange; |
448 | use ra_syntax::TextRange; | 301 | use ra_syntax::TextRange; |
449 | use test_utils::{extract_offset, extract_range}; | 302 | use test_utils::{extract_offset, extract_range}; |
450 | 303 | ||
451 | use crate::test_db::TestDB; | 304 | use crate::helpers; |
452 | 305 | ||
453 | #[test] | 306 | #[test] |
454 | fn assist_order_field_struct() { | 307 | fn assist_order_field_struct() { |
455 | let before = "struct Foo { <|>bar: u32 }"; | 308 | let before = "struct Foo { <|>bar: u32 }"; |
456 | let (before_cursor_pos, before) = extract_offset(before); | 309 | let (before_cursor_pos, before) = extract_offset(before); |
457 | let (db, file_id) = TestDB::with_single_file(&before); | 310 | let (db, file_id) = helpers::with_single_file(&before); |
458 | let frange = | 311 | let frange = |
459 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 312 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
460 | let assists = super::assists(&db, frange); | 313 | let assists = super::assists(&db, frange); |
@@ -478,7 +331,7 @@ mod tests { | |||
478 | } | 331 | } |
479 | }"; | 332 | }"; |
480 | let (range, before) = extract_range(before); | 333 | let (range, before) = extract_range(before); |
481 | let (db, file_id) = TestDB::with_single_file(&before); | 334 | let (db, file_id) = helpers::with_single_file(&before); |
482 | let frange = FileRange { file_id, range }; | 335 | let frange = FileRange { file_id, range }; |
483 | let assists = super::assists(&db, frange); | 336 | let assists = super::assists(&db, frange); |
484 | let mut assists = assists.iter(); | 337 | let mut assists = assists.iter(); |