aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/utils
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/utils')
-rw-r--r--crates/assists/src/utils/import_assets.rs40
-rw-r--r--crates/assists/src/utils/insert_use.rs166
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.
2use std::collections::BTreeSet;
3
4use either::Either; 2use either::Either;
5use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; 3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics};
6use ide_db::{imports_locator, RootDatabase}; 4use ide_db::{imports_locator, RootDatabase};
@@ -29,12 +27,12 @@ pub(crate) enum ImportCandidate {
29#[derive(Debug)] 27#[derive(Debug)]
30pub(crate) struct TraitImportCandidate { 28pub(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)]
36pub(crate) struct PathImportCandidate { 34pub(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};
17use test_utils::mark;
17 18
18#[derive(Debug)] 19#[derive(Debug)]
19pub enum ImportScope { 20pub 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
494impl 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
473fn find_insert_position( 503fn 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"
623use std::bar::A; 705 use std::bar::A;
624use std::bar::D; 706 use std::bar::D;
625 707
626use foo::bar::F; 708 use foo::bar::F;
627use foo::bar::H;", 709 use foo::bar::H;",
628 r" 710 r"
629use std::bar::A; 711 use std::bar::A;
630use std::bar::D; 712 use std::bar::D;
631 713
632use foo::bar::F; 714 use foo::bar::F;
633use foo::bar::GG; 715 use foo::bar::GG;
634use 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"
643use foo::bar::A; 725 use foo::bar::A;
644use foo::bar::D; 726 use foo::bar::D;
645 727
646use std; 728 use std;
647 729
648use foo::bar::F; 730 use foo::bar::F;
649use foo::bar::H;", 731 use foo::bar::H;",
650 r" 732 r"
651use foo::bar::A; 733 use foo::bar::A;
652use foo::bar::D; 734 use foo::bar::D;
653use foo::bar::GG; 735 use foo::bar::GG;
654 736
655use std; 737 use std;
656 738
657use foo::bar::F; 739 use foo::bar::F;
658use 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"
667use foo::bar::A; 749 use foo::bar::A;
668use foo::bar::D;", 750 use foo::bar::D;",
669 r" 751 r"
670use std::fmt; 752 use std::fmt;
671 753
672use foo::bar::A; 754 use foo::bar::A;
673use 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) {