diff options
-rw-r--r-- | crates/ide_completion/src/completions/flyimport.rs | 34 | ||||
-rw-r--r-- | crates/ide_completion/src/item.rs | 41 | ||||
-rw-r--r-- | crates/ide_completion/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 99 |
4 files changed, 116 insertions, 65 deletions
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index c1e3f091f..c5b3c9e27 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -144,7 +144,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
144 | .filter_map(|import| { | 144 | .filter_map(|import| { |
145 | render_resolution_with_import( | 145 | render_resolution_with_import( |
146 | RenderContext::new(ctx), | 146 | RenderContext::new(ctx), |
147 | ImportEdit { import, import_scope: import_scope.clone() }, | 147 | ImportEdit { import, scope: import_scope.clone() }, |
148 | ) | 148 | ) |
149 | }), | 149 | }), |
150 | ); | 150 | ); |
@@ -690,8 +690,8 @@ fn main() { | |||
690 | } | 690 | } |
691 | "#, | 691 | "#, |
692 | expect![[r#" | 692 | expect![[r#" |
693 | fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED | ||
694 | ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED | 693 | ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED |
694 | fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED | ||
695 | "#]], | 695 | "#]], |
696 | ); | 696 | ); |
697 | } | 697 | } |
@@ -807,7 +807,12 @@ fn main() { | |||
807 | bar::baz::Ite$0 | 807 | bar::baz::Ite$0 |
808 | }"#; | 808 | }"#; |
809 | 809 | ||
810 | check(fixture, expect![["st Item (foo::bar::baz::Item)"]]); | 810 | check( |
811 | fixture, | ||
812 | expect![[r#" | ||
813 | st foo::bar::baz::Item | ||
814 | "#]], | ||
815 | ); | ||
811 | 816 | ||
812 | check_edit( | 817 | check_edit( |
813 | "Item", | 818 | "Item", |
@@ -825,8 +830,7 @@ fn main() { | |||
825 | 830 | ||
826 | fn main() { | 831 | fn main() { |
827 | bar::baz::Item | 832 | bar::baz::Item |
828 | } | 833 | }"#, |
829 | "#, | ||
830 | ); | 834 | ); |
831 | } | 835 | } |
832 | 836 | ||
@@ -845,7 +849,12 @@ fn main() { | |||
845 | Item::TEST_A$0 | 849 | Item::TEST_A$0 |
846 | }"#; | 850 | }"#; |
847 | 851 | ||
848 | check(fixture, expect![["ct TEST_ASSOC (foo::bar::baz::Item)"]]); | 852 | check( |
853 | fixture, | ||
854 | expect![[r#" | ||
855 | ct TEST_ASSOC (foo::Item) | ||
856 | "#]], | ||
857 | ); | ||
849 | 858 | ||
850 | check_edit( | 859 | check_edit( |
851 | "TEST_ASSOC", | 860 | "TEST_ASSOC", |
@@ -863,8 +872,7 @@ mod foo { | |||
863 | 872 | ||
864 | fn main() { | 873 | fn main() { |
865 | Item::TEST_ASSOC | 874 | Item::TEST_ASSOC |
866 | } | 875 | }"#, |
867 | "#, | ||
868 | ); | 876 | ); |
869 | } | 877 | } |
870 | 878 | ||
@@ -885,7 +893,12 @@ fn main() { | |||
885 | bar::Item::TEST_A$0 | 893 | bar::Item::TEST_A$0 |
886 | }"#; | 894 | }"#; |
887 | 895 | ||
888 | check(fixture, expect![["ct TEST_ASSOC (foo::bar::baz::Item)"]]); | 896 | check( |
897 | fixture, | ||
898 | expect![[r#" | ||
899 | ct TEST_ASSOC (foo::bar::Item) | ||
900 | "#]], | ||
901 | ); | ||
889 | 902 | ||
890 | check_edit( | 903 | check_edit( |
891 | "TEST_ASSOC", | 904 | "TEST_ASSOC", |
@@ -905,8 +918,7 @@ mod foo { | |||
905 | 918 | ||
906 | fn main() { | 919 | fn main() { |
907 | bar::Item::TEST_ASSOC | 920 | bar::Item::TEST_ASSOC |
908 | } | 921 | }"#, |
909 | "#, | ||
910 | ); | 922 | ); |
911 | } | 923 | } |
912 | } | 924 | } |
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index d01620500..44e4a6dfd 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs | |||
@@ -11,7 +11,7 @@ use ide_db::{ | |||
11 | }, | 11 | }, |
12 | SymbolKind, | 12 | SymbolKind, |
13 | }; | 13 | }; |
14 | use stdx::{impl_from, never}; | 14 | use stdx::{format_to, impl_from, never}; |
15 | use syntax::{algo, TextRange}; | 15 | use syntax::{algo, TextRange}; |
16 | use text_edit::TextEdit; | 16 | use text_edit::TextEdit; |
17 | 17 | ||
@@ -274,7 +274,7 @@ impl CompletionItem { | |||
274 | #[derive(Debug, Clone)] | 274 | #[derive(Debug, Clone)] |
275 | pub struct ImportEdit { | 275 | pub struct ImportEdit { |
276 | pub import: LocatedImport, | 276 | pub import: LocatedImport, |
277 | pub import_scope: ImportScope, | 277 | pub scope: ImportScope, |
278 | } | 278 | } |
279 | 279 | ||
280 | impl ImportEdit { | 280 | impl ImportEdit { |
@@ -284,7 +284,7 @@ impl ImportEdit { | |||
284 | let _p = profile::span("ImportEdit::to_text_edit"); | 284 | let _p = profile::span("ImportEdit::to_text_edit"); |
285 | 285 | ||
286 | let rewriter = insert_use::insert_use( | 286 | let rewriter = insert_use::insert_use( |
287 | &self.import_scope, | 287 | &self.scope, |
288 | mod_path_to_ast(&self.import.import_path), | 288 | mod_path_to_ast(&self.import.import_path), |
289 | cfg, | 289 | cfg, |
290 | ); | 290 | ); |
@@ -302,7 +302,6 @@ impl ImportEdit { | |||
302 | pub(crate) struct Builder { | 302 | pub(crate) struct Builder { |
303 | source_range: TextRange, | 303 | source_range: TextRange, |
304 | completion_kind: CompletionKind, | 304 | completion_kind: CompletionKind, |
305 | // TODO kb also add a db here, to resolve the completion label? | ||
306 | import_to_add: Option<ImportEdit>, | 305 | import_to_add: Option<ImportEdit>, |
307 | label: String, | 306 | label: String, |
308 | insert_text: Option<String>, | 307 | insert_text: Option<String>, |
@@ -322,22 +321,24 @@ impl Builder { | |||
322 | pub(crate) fn build(self) -> CompletionItem { | 321 | pub(crate) fn build(self) -> CompletionItem { |
323 | let _p = profile::span("item::Builder::build"); | 322 | let _p = profile::span("item::Builder::build"); |
324 | 323 | ||
325 | let label = self.label; | 324 | let mut label = self.label; |
326 | let lookup = self.lookup; | 325 | let mut lookup = self.lookup; |
327 | let insert_text = self.insert_text; | 326 | let mut insert_text = self.insert_text; |
328 | 327 | ||
329 | if let Some(_import_to_add) = self.import_to_add.as_ref() { | 328 | if let Some(original_path) = self |
330 | todo!("todo kb") | 329 | .import_to_add |
331 | // let import = &import_to_add.import; | 330 | .as_ref() |
332 | // let item_to_import = import.item_to_import(); | 331 | .and_then(|import_edit| import_edit.import.original_path.as_ref()) |
333 | // lookup = lookup.or_else(|| Some(label.clone())); | 332 | { |
334 | // insert_text = insert_text.or_else(|| Some(label.clone())); | 333 | lookup = lookup.or_else(|| Some(label.clone())); |
335 | // let display_path = import_to_add.import.display_path(); | 334 | insert_text = insert_text.or_else(|| Some(label.clone())); |
336 | // if import_to_add.import { | 335 | |
337 | // label = format!("{} ({})", label, display_path); | 336 | let original_path_label = original_path.to_string(); |
338 | // } else { | 337 | if original_path_label.ends_with(&label) { |
339 | // label = display_path.to_string(); | 338 | label = original_path_label; |
340 | // } | 339 | } else { |
340 | format_to!(label, " ({})", original_path) | ||
341 | } | ||
341 | } | 342 | } |
342 | 343 | ||
343 | let text_edit = match self.text_edit { | 344 | let text_edit = match self.text_edit { |
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index d19368de0..5470914fb 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -144,7 +144,7 @@ pub fn resolve_completion_edits( | |||
144 | ) -> Option<Vec<TextEdit>> { | 144 | ) -> Option<Vec<TextEdit>> { |
145 | let ctx = CompletionContext::new(db, position, config)?; | 145 | let ctx = CompletionContext::new(db, position, config)?; |
146 | let position_for_import = position_for_import(&ctx, None)?; | 146 | let position_for_import = position_for_import(&ctx, None)?; |
147 | let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; | 147 | let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; |
148 | 148 | ||
149 | let current_module = ctx.sema.scope(position_for_import).module()?; | 149 | let current_module = ctx.sema.scope(position_for_import).module()?; |
150 | let current_crate = current_module.krate(); | 150 | let current_crate = current_module.krate(); |
@@ -158,9 +158,10 @@ pub fn resolve_completion_edits( | |||
158 | .zip(Some(candidate)) | 158 | .zip(Some(candidate)) |
159 | }) | 159 | }) |
160 | .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?; | 160 | .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?; |
161 | let import = LocatedImport::new(import_path, item_to_import, item_to_import); | 161 | let import = |
162 | LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path)); | ||
162 | 163 | ||
163 | ImportEdit { import, import_scope }.to_text_edit(config.insert_use).map(|edit| vec![edit]) | 164 | ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit]) |
164 | } | 165 | } |
165 | 166 | ||
166 | #[cfg(test)] | 167 | #[cfg(test)] |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 8d16c011e..b3e90717a 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -132,11 +132,17 @@ pub struct LocatedImport { | |||
132 | pub import_path: ModPath, | 132 | pub import_path: ModPath, |
133 | pub item_to_import: ItemInNs, | 133 | pub item_to_import: ItemInNs, |
134 | pub original_item: ItemInNs, | 134 | pub original_item: ItemInNs, |
135 | pub original_path: Option<ModPath>, | ||
135 | } | 136 | } |
136 | 137 | ||
137 | impl LocatedImport { | 138 | impl LocatedImport { |
138 | pub fn new(import_path: ModPath, item_to_import: ItemInNs, original_item: ItemInNs) -> Self { | 139 | pub fn new( |
139 | Self { import_path, item_to_import, original_item } | 140 | import_path: ModPath, |
141 | item_to_import: ItemInNs, | ||
142 | original_item: ItemInNs, | ||
143 | original_path: Option<ModPath>, | ||
144 | ) -> Self { | ||
145 | Self { import_path, item_to_import, original_item, original_path } | ||
140 | } | 146 | } |
141 | 147 | ||
142 | pub fn original_item_name(&self, db: &RootDatabase) -> Option<Name> { | 148 | pub fn original_item_name(&self, db: &RootDatabase) -> Option<Name> { |
@@ -238,7 +244,9 @@ impl<'a> ImportAssets<'a> { | |||
238 | let _p = profile::span("import_assets::applicable_defs"); | 244 | let _p = profile::span("import_assets::applicable_defs"); |
239 | let current_crate = self.module_with_candidate.krate(); | 245 | let current_crate = self.module_with_candidate.krate(); |
240 | 246 | ||
241 | let mod_path = |item| get_mod_path(db, item, &self.module_with_candidate, prefixed); | 247 | let mod_path = |item| { |
248 | get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed) | ||
249 | }; | ||
242 | 250 | ||
243 | match &self.import_candidate { | 251 | match &self.import_candidate { |
244 | ImportCandidate::Path(path_candidate) => { | 252 | ImportCandidate::Path(path_candidate) => { |
@@ -276,7 +284,9 @@ fn path_applicable_imports( | |||
276 | Qualifier::Absent => { | 284 | Qualifier::Absent => { |
277 | return items_with_candidate_name | 285 | return items_with_candidate_name |
278 | .into_iter() | 286 | .into_iter() |
279 | .filter_map(|item| Some(LocatedImport::new(mod_path(item)?, item, item))) | 287 | .filter_map(|item| { |
288 | Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item))) | ||
289 | }) | ||
280 | .collect(); | 290 | .collect(); |
281 | } | 291 | } |
282 | Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => { | 292 | Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => { |
@@ -300,46 +310,69 @@ fn import_for_item( | |||
300 | original_item: ItemInNs, | 310 | original_item: ItemInNs, |
301 | ) -> Option<LocatedImport> { | 311 | ) -> Option<LocatedImport> { |
302 | let _p = profile::span("import_assets::import_for_item"); | 312 | let _p = profile::span("import_assets::import_for_item"); |
303 | let (item_candidate, trait_to_import) = match original_item.as_module_def_id() { | ||
304 | Some(module_def_id) => { | ||
305 | match ModuleDef::from(module_def_id).as_assoc_item(db).map(|assoc| assoc.container(db)) | ||
306 | { | ||
307 | Some(AssocItemContainer::Trait(trait_)) => { | ||
308 | let trait_item = ItemInNs::from(ModuleDef::from(trait_)); | ||
309 | (trait_item, Some(trait_item)) | ||
310 | } | ||
311 | Some(AssocItemContainer::Impl(impl_)) => { | ||
312 | (ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)), None) | ||
313 | } | ||
314 | None => (original_item, None), | ||
315 | } | ||
316 | } | ||
317 | None => (original_item, None), | ||
318 | }; | ||
319 | let import_path_candidate = mod_path(item_candidate)?; | ||
320 | 313 | ||
314 | let original_item_candidate = item_for_path_search(db, original_item)?; | ||
315 | let import_path_candidate = mod_path(original_item_candidate)?; | ||
321 | let import_path_string = import_path_candidate.to_string(); | 316 | let import_path_string = import_path_candidate.to_string(); |
317 | |||
322 | if !import_path_string.contains(unresolved_first_segment) | 318 | if !import_path_string.contains(unresolved_first_segment) |
323 | || !import_path_string.contains(unresolved_qualifier) | 319 | || !import_path_string.contains(unresolved_qualifier) |
324 | { | 320 | { |
325 | return None; | 321 | return None; |
326 | } | 322 | } |
327 | 323 | ||
328 | let segment_import = find_import_for_segment(db, item_candidate, &unresolved_first_segment)?; | 324 | let segment_import = |
329 | Some(match (segment_import == item_candidate, trait_to_import) { | 325 | find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?; |
326 | let trait_item_to_import = original_item | ||
327 | .as_module_def_id() | ||
328 | .and_then(|module_def_id| { | ||
329 | ModuleDef::from(module_def_id).as_assoc_item(db)?.containing_trait(db) | ||
330 | }) | ||
331 | .map(|trait_| ItemInNs::from(ModuleDef::from(trait_))); | ||
332 | Some(match (segment_import == original_item_candidate, trait_item_to_import) { | ||
330 | (true, Some(_)) => { | 333 | (true, Some(_)) => { |
331 | // FIXME we should be able to import both the trait and the segment, | 334 | // FIXME we should be able to import both the trait and the segment, |
332 | // but it's unclear what to do with overlapping edits (merge imports?) | 335 | // but it's unclear what to do with overlapping edits (merge imports?) |
333 | // especially in case of lazy completion edit resolutions. | 336 | // especially in case of lazy completion edit resolutions. |
334 | return None; | 337 | return None; |
335 | } | 338 | } |
336 | (false, Some(trait_to_import)) => { | 339 | (false, Some(trait_to_import)) => LocatedImport::new( |
337 | LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, original_item) | 340 | mod_path(trait_to_import)?, |
338 | } | 341 | trait_to_import, |
339 | (true, None) => LocatedImport::new(import_path_candidate, item_candidate, original_item), | 342 | original_item, |
340 | (false, None) => { | 343 | mod_path(original_item), |
341 | LocatedImport::new(mod_path(segment_import)?, segment_import, original_item) | 344 | ), |
345 | (true, None) => LocatedImport::new( | ||
346 | import_path_candidate, | ||
347 | original_item_candidate, | ||
348 | original_item, | ||
349 | mod_path(original_item), | ||
350 | ), | ||
351 | (false, None) => LocatedImport::new( | ||
352 | mod_path(segment_import)?, | ||
353 | segment_import, | ||
354 | original_item, | ||
355 | mod_path(original_item), | ||
356 | ), | ||
357 | }) | ||
358 | } | ||
359 | |||
360 | fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> { | ||
361 | Some(match item { | ||
362 | ItemInNs::Types(module_def_id) | ItemInNs::Values(module_def_id) => { | ||
363 | let module_def = ModuleDef::from(module_def_id); | ||
364 | |||
365 | match module_def.as_assoc_item(db) { | ||
366 | Some(assoc_item) => match assoc_item.container(db) { | ||
367 | AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)), | ||
368 | AssocItemContainer::Impl(impl_) => { | ||
369 | ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)) | ||
370 | } | ||
371 | }, | ||
372 | None => item, | ||
373 | } | ||
342 | } | 374 | } |
375 | ItemInNs::Macros(_) => item, | ||
343 | }) | 376 | }) |
344 | } | 377 | } |
345 | 378 | ||
@@ -420,10 +453,12 @@ fn trait_applicable_items( | |||
420 | } | 453 | } |
421 | 454 | ||
422 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | 455 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); |
456 | let original_item = assoc_to_item(assoc); | ||
423 | located_imports.insert(LocatedImport::new( | 457 | located_imports.insert(LocatedImport::new( |
424 | mod_path(item)?, | 458 | mod_path(item)?, |
425 | item, | 459 | item, |
426 | assoc_to_item(assoc), | 460 | original_item, |
461 | mod_path(original_item), | ||
427 | )); | 462 | )); |
428 | } | 463 | } |
429 | None::<()> | 464 | None::<()> |
@@ -439,10 +474,12 @@ fn trait_applicable_items( | |||
439 | let assoc = function.as_assoc_item(db)?; | 474 | let assoc = function.as_assoc_item(db)?; |
440 | if required_assoc_items.contains(&assoc) { | 475 | if required_assoc_items.contains(&assoc) { |
441 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | 476 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); |
477 | let original_item = assoc_to_item(assoc); | ||
442 | located_imports.insert(LocatedImport::new( | 478 | located_imports.insert(LocatedImport::new( |
443 | mod_path(item)?, | 479 | mod_path(item)?, |
444 | item, | 480 | item, |
445 | assoc_to_item(assoc), | 481 | original_item, |
482 | mod_path(original_item), | ||
446 | )); | 483 | )); |
447 | } | 484 | } |
448 | None::<()> | 485 | None::<()> |