diff options
Diffstat (limited to 'crates/assists/src/utils')
-rw-r--r-- | crates/assists/src/utils/import_assets.rs | 40 | ||||
-rw-r--r-- | crates/assists/src/utils/insert_use.rs | 166 |
2 files changed, 149 insertions, 57 deletions
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs index 601f51098..23db3a74b 100644 --- a/crates/assists/src/utils/import_assets.rs +++ b/crates/assists/src/utils/import_assets.rs | |||
@@ -1,6 +1,4 @@ | |||
1 | //! Look up accessible paths for items. | 1 | //! Look up accessible paths for items. |
2 | use std::collections::BTreeSet; | ||
3 | |||
4 | use either::Either; | 2 | use either::Either; |
5 | use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; | 3 | use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; |
6 | use ide_db::{imports_locator, RootDatabase}; | 4 | use ide_db::{imports_locator, RootDatabase}; |
@@ -29,12 +27,12 @@ pub(crate) enum ImportCandidate { | |||
29 | #[derive(Debug)] | 27 | #[derive(Debug)] |
30 | pub(crate) struct TraitImportCandidate { | 28 | pub(crate) struct TraitImportCandidate { |
31 | pub ty: hir::Type, | 29 | pub ty: hir::Type, |
32 | pub name: String, | 30 | pub name: ast::NameRef, |
33 | } | 31 | } |
34 | 32 | ||
35 | #[derive(Debug)] | 33 | #[derive(Debug)] |
36 | pub(crate) struct PathImportCandidate { | 34 | pub(crate) struct PathImportCandidate { |
37 | pub name: String, | 35 | pub name: ast::NameRef, |
38 | } | 36 | } |
39 | 37 | ||
40 | #[derive(Debug)] | 38 | #[derive(Debug)] |
@@ -86,9 +84,9 @@ impl ImportAssets { | |||
86 | fn get_search_query(&self) -> &str { | 84 | fn get_search_query(&self) -> &str { |
87 | match &self.import_candidate { | 85 | match &self.import_candidate { |
88 | ImportCandidate::UnqualifiedName(candidate) | 86 | ImportCandidate::UnqualifiedName(candidate) |
89 | | ImportCandidate::QualifierStart(candidate) => &candidate.name, | 87 | | ImportCandidate::QualifierStart(candidate) => candidate.name.text(), |
90 | ImportCandidate::TraitAssocItem(candidate) | 88 | ImportCandidate::TraitAssocItem(candidate) |
91 | | ImportCandidate::TraitMethod(candidate) => &candidate.name, | 89 | | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), |
92 | } | 90 | } |
93 | } | 91 | } |
94 | 92 | ||
@@ -96,7 +94,7 @@ impl ImportAssets { | |||
96 | &self, | 94 | &self, |
97 | sema: &Semantics<RootDatabase>, | 95 | sema: &Semantics<RootDatabase>, |
98 | config: &InsertUseConfig, | 96 | config: &InsertUseConfig, |
99 | ) -> BTreeSet<hir::ModPath> { | 97 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
100 | let _p = profile::span("import_assists::search_for_imports"); | 98 | let _p = profile::span("import_assists::search_for_imports"); |
101 | self.search_for(sema, Some(config.prefix_kind)) | 99 | self.search_for(sema, Some(config.prefix_kind)) |
102 | } | 100 | } |
@@ -106,7 +104,7 @@ impl ImportAssets { | |||
106 | pub(crate) fn search_for_relative_paths( | 104 | pub(crate) fn search_for_relative_paths( |
107 | &self, | 105 | &self, |
108 | sema: &Semantics<RootDatabase>, | 106 | sema: &Semantics<RootDatabase>, |
109 | ) -> BTreeSet<hir::ModPath> { | 107 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
110 | let _p = profile::span("import_assists::search_for_relative_paths"); | 108 | let _p = profile::span("import_assists::search_for_relative_paths"); |
111 | self.search_for(sema, None) | 109 | self.search_for(sema, None) |
112 | } | 110 | } |
@@ -115,7 +113,7 @@ impl ImportAssets { | |||
115 | &self, | 113 | &self, |
116 | sema: &Semantics<RootDatabase>, | 114 | sema: &Semantics<RootDatabase>, |
117 | prefixed: Option<hir::PrefixKind>, | 115 | prefixed: Option<hir::PrefixKind>, |
118 | ) -> BTreeSet<hir::ModPath> { | 116 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
119 | let db = sema.db; | 117 | let db = sema.db; |
120 | let mut trait_candidates = FxHashSet::default(); | 118 | let mut trait_candidates = FxHashSet::default(); |
121 | let current_crate = self.module_with_name_to_import.krate(); | 119 | let current_crate = self.module_with_name_to_import.krate(); |
@@ -181,7 +179,7 @@ impl ImportAssets { | |||
181 | } | 179 | } |
182 | }; | 180 | }; |
183 | 181 | ||
184 | imports_locator::find_imports(sema, current_crate, &self.get_search_query()) | 182 | let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query()) |
185 | .into_iter() | 183 | .into_iter() |
186 | .filter_map(filter) | 184 | .filter_map(filter) |
187 | .filter_map(|candidate| { | 185 | .filter_map(|candidate| { |
@@ -191,10 +189,13 @@ impl ImportAssets { | |||
191 | } else { | 189 | } else { |
192 | self.module_with_name_to_import.find_use_path(db, item) | 190 | self.module_with_name_to_import.find_use_path(db, item) |
193 | } | 191 | } |
192 | .map(|path| (path, item)) | ||
194 | }) | 193 | }) |
195 | .filter(|use_path| !use_path.segments.is_empty()) | 194 | .filter(|(use_path, _)| !use_path.segments.is_empty()) |
196 | .take(20) | 195 | .take(20) |
197 | .collect::<BTreeSet<_>>() | 196 | .collect::<Vec<_>>(); |
197 | res.sort_by_key(|(path, _)| path.clone()); | ||
198 | res | ||
198 | } | 199 | } |
199 | 200 | ||
200 | fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> { | 201 | fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> { |
@@ -215,7 +216,7 @@ impl ImportCandidate { | |||
215 | Some(_) => None, | 216 | Some(_) => None, |
216 | None => Some(Self::TraitMethod(TraitImportCandidate { | 217 | None => Some(Self::TraitMethod(TraitImportCandidate { |
217 | ty: sema.type_of_expr(&method_call.receiver()?)?, | 218 | ty: sema.type_of_expr(&method_call.receiver()?)?, |
218 | name: method_call.name_ref()?.syntax().to_string(), | 219 | name: method_call.name_ref()?, |
219 | })), | 220 | })), |
220 | } | 221 | } |
221 | } | 222 | } |
@@ -243,24 +244,17 @@ impl ImportCandidate { | |||
243 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { | 244 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { |
244 | ImportCandidate::TraitAssocItem(TraitImportCandidate { | 245 | ImportCandidate::TraitAssocItem(TraitImportCandidate { |
245 | ty: assoc_item_path.ty(sema.db), | 246 | ty: assoc_item_path.ty(sema.db), |
246 | name: segment.syntax().to_string(), | 247 | name: segment.name_ref()?, |
247 | }) | 248 | }) |
248 | } | 249 | } |
249 | _ => return None, | 250 | _ => return None, |
250 | } | 251 | } |
251 | } else { | 252 | } else { |
252 | ImportCandidate::QualifierStart(PathImportCandidate { | 253 | ImportCandidate::QualifierStart(PathImportCandidate { name: qualifier_start }) |
253 | name: qualifier_start.syntax().to_string(), | ||
254 | }) | ||
255 | } | 254 | } |
256 | } else { | 255 | } else { |
257 | ImportCandidate::UnqualifiedName(PathImportCandidate { | 256 | ImportCandidate::UnqualifiedName(PathImportCandidate { |
258 | name: segment | 257 | name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, |
259 | .syntax() | ||
260 | .descendants() | ||
261 | .find_map(ast::NameRef::cast)? | ||
262 | .syntax() | ||
263 | .to_string(), | ||
264 | }) | 258 | }) |
265 | }; | 259 | }; |
266 | Some(candidate) | 260 | Some(candidate) |
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 409985b3b..033fbcedc 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -14,6 +14,7 @@ use syntax::{ | |||
14 | }, | 14 | }, |
15 | InsertPosition, SyntaxElement, SyntaxNode, | 15 | InsertPosition, SyntaxElement, SyntaxNode, |
16 | }; | 16 | }; |
17 | use test_utils::mark; | ||
17 | 18 | ||
18 | #[derive(Debug)] | 19 | #[derive(Debug)] |
19 | pub enum ImportScope { | 20 | pub enum ImportScope { |
@@ -109,6 +110,12 @@ pub(crate) fn insert_use( | |||
109 | // so look for the place we have to insert to | 110 | // so look for the place we have to insert to |
110 | let (insert_position, add_blank) = find_insert_position(scope, path); | 111 | let (insert_position, add_blank) = find_insert_position(scope, path); |
111 | 112 | ||
113 | let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { | ||
114 | Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()) | ||
115 | } else { | ||
116 | None | ||
117 | }; | ||
118 | |||
112 | let to_insert: Vec<SyntaxElement> = { | 119 | let to_insert: Vec<SyntaxElement> = { |
113 | let mut buf = Vec::new(); | 120 | let mut buf = Vec::new(); |
114 | 121 | ||
@@ -120,9 +127,13 @@ pub(crate) fn insert_use( | |||
120 | _ => (), | 127 | _ => (), |
121 | } | 128 | } |
122 | 129 | ||
123 | if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { | 130 | if add_blank.has_before() { |
124 | buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()); | 131 | if let Some(indent) = indent.clone() { |
132 | mark::hit!(insert_use_indent_before); | ||
133 | buf.push(indent); | ||
134 | } | ||
125 | } | 135 | } |
136 | |||
126 | buf.push(use_item.syntax().clone().into()); | 137 | buf.push(use_item.syntax().clone().into()); |
127 | 138 | ||
128 | match add_blank { | 139 | match add_blank { |
@@ -133,6 +144,16 @@ pub(crate) fn insert_use( | |||
133 | _ => (), | 144 | _ => (), |
134 | } | 145 | } |
135 | 146 | ||
147 | // only add indentation *after* our stuff if there's another node directly after it | ||
148 | if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) { | ||
149 | if let Some(indent) = indent { | ||
150 | mark::hit!(insert_use_indent_after); | ||
151 | buf.push(indent); | ||
152 | } | ||
153 | } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) { | ||
154 | mark::hit!(insert_use_no_indent_after); | ||
155 | } | ||
156 | |||
136 | buf | 157 | buf |
137 | }; | 158 | }; |
138 | 159 | ||
@@ -470,6 +491,15 @@ enum AddBlankLine { | |||
470 | AfterTwice, | 491 | AfterTwice, |
471 | } | 492 | } |
472 | 493 | ||
494 | impl AddBlankLine { | ||
495 | fn has_before(&self) -> bool { | ||
496 | matches!(self, AddBlankLine::Before | AddBlankLine::BeforeTwice | AddBlankLine::Around) | ||
497 | } | ||
498 | fn has_after(&self) -> bool { | ||
499 | matches!(self, AddBlankLine::After | AddBlankLine::AfterTwice | AddBlankLine::Around) | ||
500 | } | ||
501 | } | ||
502 | |||
473 | fn find_insert_position( | 503 | fn find_insert_position( |
474 | scope: &ImportScope, | 504 | scope: &ImportScope, |
475 | insert_path: ast::Path, | 505 | insert_path: ast::Path, |
@@ -562,6 +592,21 @@ use std::bar::G;", | |||
562 | } | 592 | } |
563 | 593 | ||
564 | #[test] | 594 | #[test] |
595 | fn insert_start_indent() { | ||
596 | mark::check!(insert_use_indent_after); | ||
597 | check_none( | ||
598 | "std::bar::AA", | ||
599 | r" | ||
600 | use std::bar::B; | ||
601 | use std::bar::D;", | ||
602 | r" | ||
603 | use std::bar::AA; | ||
604 | use std::bar::B; | ||
605 | use std::bar::D;", | ||
606 | ) | ||
607 | } | ||
608 | |||
609 | #[test] | ||
565 | fn insert_middle() { | 610 | fn insert_middle() { |
566 | check_none( | 611 | check_none( |
567 | "std::bar::EE", | 612 | "std::bar::EE", |
@@ -580,6 +625,24 @@ use std::bar::G;", | |||
580 | } | 625 | } |
581 | 626 | ||
582 | #[test] | 627 | #[test] |
628 | fn insert_middle_indent() { | ||
629 | check_none( | ||
630 | "std::bar::EE", | ||
631 | r" | ||
632 | use std::bar::A; | ||
633 | use std::bar::D; | ||
634 | use std::bar::F; | ||
635 | use std::bar::G;", | ||
636 | r" | ||
637 | use std::bar::A; | ||
638 | use std::bar::D; | ||
639 | use std::bar::EE; | ||
640 | use std::bar::F; | ||
641 | use std::bar::G;", | ||
642 | ) | ||
643 | } | ||
644 | |||
645 | #[test] | ||
583 | fn insert_end() { | 646 | fn insert_end() { |
584 | check_none( | 647 | check_none( |
585 | "std::bar::ZZ", | 648 | "std::bar::ZZ", |
@@ -598,6 +661,25 @@ use std::bar::ZZ;", | |||
598 | } | 661 | } |
599 | 662 | ||
600 | #[test] | 663 | #[test] |
664 | fn insert_end_indent() { | ||
665 | mark::check!(insert_use_indent_before); | ||
666 | check_none( | ||
667 | "std::bar::ZZ", | ||
668 | r" | ||
669 | use std::bar::A; | ||
670 | use std::bar::D; | ||
671 | use std::bar::F; | ||
672 | use std::bar::G;", | ||
673 | r" | ||
674 | use std::bar::A; | ||
675 | use std::bar::D; | ||
676 | use std::bar::F; | ||
677 | use std::bar::G; | ||
678 | use std::bar::ZZ;", | ||
679 | ) | ||
680 | } | ||
681 | |||
682 | #[test] | ||
601 | fn insert_middle_nested() { | 683 | fn insert_middle_nested() { |
602 | check_none( | 684 | check_none( |
603 | "std::bar::EE", | 685 | "std::bar::EE", |
@@ -620,18 +702,18 @@ use std::bar::G;", | |||
620 | check_none( | 702 | check_none( |
621 | "foo::bar::GG", | 703 | "foo::bar::GG", |
622 | r" | 704 | r" |
623 | use std::bar::A; | 705 | use std::bar::A; |
624 | use std::bar::D; | 706 | use std::bar::D; |
625 | 707 | ||
626 | use foo::bar::F; | 708 | use foo::bar::F; |
627 | use foo::bar::H;", | 709 | use foo::bar::H;", |
628 | r" | 710 | r" |
629 | use std::bar::A; | 711 | use std::bar::A; |
630 | use std::bar::D; | 712 | use std::bar::D; |
631 | 713 | ||
632 | use foo::bar::F; | 714 | use foo::bar::F; |
633 | use foo::bar::GG; | 715 | use foo::bar::GG; |
634 | use foo::bar::H;", | 716 | use foo::bar::H;", |
635 | ) | 717 | ) |
636 | } | 718 | } |
637 | 719 | ||
@@ -640,22 +722,22 @@ use foo::bar::H;", | |||
640 | check_none( | 722 | check_none( |
641 | "foo::bar::GG", | 723 | "foo::bar::GG", |
642 | r" | 724 | r" |
643 | use foo::bar::A; | 725 | use foo::bar::A; |
644 | use foo::bar::D; | 726 | use foo::bar::D; |
645 | 727 | ||
646 | use std; | 728 | use std; |
647 | 729 | ||
648 | use foo::bar::F; | 730 | use foo::bar::F; |
649 | use foo::bar::H;", | 731 | use foo::bar::H;", |
650 | r" | 732 | r" |
651 | use foo::bar::A; | 733 | use foo::bar::A; |
652 | use foo::bar::D; | 734 | use foo::bar::D; |
653 | use foo::bar::GG; | 735 | use foo::bar::GG; |
654 | 736 | ||
655 | use std; | 737 | use std; |
656 | 738 | ||
657 | use foo::bar::F; | 739 | use foo::bar::F; |
658 | use foo::bar::H;", | 740 | use foo::bar::H;", |
659 | ) | 741 | ) |
660 | } | 742 | } |
661 | 743 | ||
@@ -664,13 +746,13 @@ use foo::bar::H;", | |||
664 | check_none( | 746 | check_none( |
665 | "std::fmt", | 747 | "std::fmt", |
666 | r" | 748 | r" |
667 | use foo::bar::A; | 749 | use foo::bar::A; |
668 | use foo::bar::D;", | 750 | use foo::bar::D;", |
669 | r" | 751 | r" |
670 | use std::fmt; | 752 | use std::fmt; |
671 | 753 | ||
672 | use foo::bar::A; | 754 | use foo::bar::A; |
673 | use foo::bar::D;", | 755 | use foo::bar::D;", |
674 | ) | 756 | ) |
675 | } | 757 | } |
676 | 758 | ||
@@ -714,6 +796,20 @@ fn main() {}", | |||
714 | } | 796 | } |
715 | 797 | ||
716 | #[test] | 798 | #[test] |
799 | fn insert_empty_module() { | ||
800 | mark::check!(insert_use_no_indent_after); | ||
801 | check( | ||
802 | "foo::bar", | ||
803 | "mod x {}", | ||
804 | r"{ | ||
805 | use foo::bar; | ||
806 | }", | ||
807 | None, | ||
808 | true, | ||
809 | ) | ||
810 | } | ||
811 | |||
812 | #[test] | ||
717 | fn insert_after_inner_attr() { | 813 | fn insert_after_inner_attr() { |
718 | check_full( | 814 | check_full( |
719 | "foo::bar", | 815 | "foo::bar", |
@@ -991,11 +1087,13 @@ use foo::bar::baz::Qux;", | |||
991 | ra_fixture_before: &str, | 1087 | ra_fixture_before: &str, |
992 | ra_fixture_after: &str, | 1088 | ra_fixture_after: &str, |
993 | mb: Option<MergeBehaviour>, | 1089 | mb: Option<MergeBehaviour>, |
1090 | module: bool, | ||
994 | ) { | 1091 | ) { |
995 | let file = super::ImportScope::from( | 1092 | let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); |
996 | ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(), | 1093 | if module { |
997 | ) | 1094 | syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone(); |
998 | .unwrap(); | 1095 | } |
1096 | let file = super::ImportScope::from(syntax).unwrap(); | ||
999 | let path = ast::SourceFile::parse(&format!("use {};", path)) | 1097 | let path = ast::SourceFile::parse(&format!("use {};", path)) |
1000 | .tree() | 1098 | .tree() |
1001 | .syntax() | 1099 | .syntax() |
@@ -1008,15 +1106,15 @@ use foo::bar::baz::Qux;", | |||
1008 | } | 1106 | } |
1009 | 1107 | ||
1010 | fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 1108 | fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
1011 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full)) | 1109 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false) |
1012 | } | 1110 | } |
1013 | 1111 | ||
1014 | fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 1112 | fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
1015 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last)) | 1113 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false) |
1016 | } | 1114 | } |
1017 | 1115 | ||
1018 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 1116 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
1019 | check(path, ra_fixture_before, ra_fixture_after, None) | 1117 | check(path, ra_fixture_before, ra_fixture_after, None, false) |
1020 | } | 1118 | } |
1021 | 1119 | ||
1022 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) { | 1120 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) { |