aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/auto_import.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/auto_import.rs')
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs109
1 files changed, 71 insertions, 38 deletions
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 99682e023..edf96d50e 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -1,5 +1,6 @@
1use std::collections::BTreeSet; 1use std::collections::BTreeSet;
2 2
3use either::Either;
3use hir::{ 4use hir::{
4 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, 5 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
5 Type, 6 Type,
@@ -12,12 +13,7 @@ use ra_syntax::{
12}; 13};
13use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
14 15
15use crate::{ 16use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel};
16 assist_ctx::{Assist, AssistCtx},
17 utils::insert_use_statement,
18 AssistId,
19};
20use either::Either;
21 17
22// Assist: auto_import 18// Assist: auto_import
23// 19//
@@ -38,25 +34,32 @@ use either::Either;
38// } 34// }
39// # pub mod std { pub mod collections { pub struct HashMap { } } } 35// # pub mod std { pub mod collections { pub struct HashMap { } } }
40// ``` 36// ```
41pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { 37pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42 let auto_import_assets = AutoImportAssets::new(&ctx)?; 38 let auto_import_assets = AutoImportAssets::new(&ctx)?;
43 let proposed_imports = auto_import_assets.search_for_imports(ctx.db); 39 let proposed_imports = auto_import_assets.search_for_imports(ctx.db);
44 if proposed_imports.is_empty() { 40 if proposed_imports.is_empty() {
45 return None; 41 return None;
46 } 42 }
47 43
48 let mut group = ctx.add_assist_group(auto_import_assets.get_import_group_message()); 44 let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range;
45 let group = auto_import_assets.get_import_group_message();
49 for import in proposed_imports { 46 for import in proposed_imports {
50 group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { 47 acc.add_group(
51 edit.target(auto_import_assets.syntax_under_caret.text_range()); 48 &group,
52 insert_use_statement( 49 AssistId("auto_import"),
53 &auto_import_assets.syntax_under_caret, 50 format!("Import `{}`", &import),
54 &import, 51 range,
55 edit.text_edit_builder(), 52 |builder| {
56 ); 53 insert_use_statement(
57 }); 54 &auto_import_assets.syntax_under_caret,
58 } 55 &import,
59 group.finish() 56 ctx,
57 builder.text_edit_builder(),
58 );
59 },
60 );
61 }
62 Some(())
60} 63}
61 64
62#[derive(Debug)] 65#[derive(Debug)]
@@ -67,15 +70,15 @@ struct AutoImportAssets {
67} 70}
68 71
69impl AutoImportAssets { 72impl AutoImportAssets {
70 fn new(ctx: &AssistCtx) -> Option<Self> { 73 fn new(ctx: &AssistContext) -> Option<Self> {
71 if let Some(path_under_caret) = ctx.find_node_at_offset::<ast::Path>() { 74 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
72 Self::for_regular_path(path_under_caret, &ctx) 75 Self::for_regular_path(path_under_caret, &ctx)
73 } else { 76 } else {
74 Self::for_method_call(ctx.find_node_at_offset()?, &ctx) 77 Self::for_method_call(ctx.find_node_at_offset_with_descend()?, &ctx)
75 } 78 }
76 } 79 }
77 80
78 fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { 81 fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistContext) -> Option<Self> {
79 let syntax_under_caret = method_call.syntax().to_owned(); 82 let syntax_under_caret = method_call.syntax().to_owned();
80 let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?; 83 let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?;
81 Some(Self { 84 Some(Self {
@@ -85,7 +88,7 @@ impl AutoImportAssets {
85 }) 88 })
86 } 89 }
87 90
88 fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option<Self> { 91 fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistContext) -> Option<Self> {
89 let syntax_under_caret = path_under_caret.syntax().to_owned(); 92 let syntax_under_caret = path_under_caret.syntax().to_owned();
90 if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { 93 if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() {
91 return None; 94 return None;
@@ -108,8 +111,8 @@ impl AutoImportAssets {
108 } 111 }
109 } 112 }
110 113
111 fn get_import_group_message(&self) -> String { 114 fn get_import_group_message(&self) -> GroupLabel {
112 match &self.import_candidate { 115 let name = match &self.import_candidate {
113 ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), 116 ImportCandidate::UnqualifiedName(name) => format!("Import {}", name),
114 ImportCandidate::QualifierStart(qualifier_start) => { 117 ImportCandidate::QualifierStart(qualifier_start) => {
115 format!("Import {}", qualifier_start) 118 format!("Import {}", qualifier_start)
@@ -120,7 +123,8 @@ impl AutoImportAssets {
120 ImportCandidate::TraitMethod(_, trait_method_name) => { 123 ImportCandidate::TraitMethod(_, trait_method_name) => {
121 format!("Import a trait for method {}", trait_method_name) 124 format!("Import a trait for method {}", trait_method_name)
122 } 125 }
123 } 126 };
127 GroupLabel(name)
124 } 128 }
125 129
126 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { 130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
@@ -280,7 +284,7 @@ impl ImportCandidate {
280#[cfg(test)] 284#[cfg(test)]
281mod tests { 285mod tests {
282 use super::*; 286 use super::*;
283 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 287 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
284 288
285 #[test] 289 #[test]
286 fn applicable_when_found_an_import() { 290 fn applicable_when_found_an_import() {
@@ -294,7 +298,7 @@ mod tests {
294 } 298 }
295 ", 299 ",
296 r" 300 r"
297 <|>use PubMod::PubStruct; 301 use PubMod::PubStruct;
298 302
299 PubStruct 303 PubStruct
300 304
@@ -306,6 +310,35 @@ mod tests {
306 } 310 }
307 311
308 #[test] 312 #[test]
313 fn applicable_when_found_an_import_in_macros() {
314 check_assist(
315 auto_import,
316 r"
317 macro_rules! foo {
318 ($i:ident) => { fn foo(a: $i) {} }
319 }
320 foo!(Pub<|>Struct);
321
322 pub mod PubMod {
323 pub struct PubStruct;
324 }
325 ",
326 r"
327 use PubMod::PubStruct;
328
329 macro_rules! foo {
330 ($i:ident) => { fn foo(a: $i) {} }
331 }
332 foo!(PubStruct);
333
334 pub mod PubMod {
335 pub struct PubStruct;
336 }
337 ",
338 );
339 }
340
341 #[test]
309 fn auto_imports_are_merged() { 342 fn auto_imports_are_merged() {
310 check_assist( 343 check_assist(
311 auto_import, 344 auto_import,
@@ -327,7 +360,7 @@ mod tests {
327 use PubMod::{PubStruct2, PubStruct1}; 360 use PubMod::{PubStruct2, PubStruct1};
328 361
329 struct Test { 362 struct Test {
330 test: Pub<|>Struct2<u8>, 363 test: PubStruct2<u8>,
331 } 364 }
332 365
333 pub mod PubMod { 366 pub mod PubMod {
@@ -358,9 +391,9 @@ mod tests {
358 } 391 }
359 ", 392 ",
360 r" 393 r"
361 use PubMod1::PubStruct; 394 use PubMod3::PubStruct;
362 395
363 PubSt<|>ruct 396 PubStruct
364 397
365 pub mod PubMod1 { 398 pub mod PubMod1 {
366 pub struct PubStruct; 399 pub struct PubStruct;
@@ -441,7 +474,7 @@ mod tests {
441 r" 474 r"
442 use PubMod::test_function; 475 use PubMod::test_function;
443 476
444 test_function<|> 477 test_function
445 478
446 pub mod PubMod { 479 pub mod PubMod {
447 pub fn test_function() {}; 480 pub fn test_function() {};
@@ -468,7 +501,7 @@ mod tests {
468 r"use crate_with_macro::foo; 501 r"use crate_with_macro::foo;
469 502
470fn main() { 503fn main() {
471 foo<|> 504 foo
472} 505}
473", 506",
474 ); 507 );
@@ -554,7 +587,7 @@ fn main() {
554 } 587 }
555 588
556 fn main() { 589 fn main() {
557 TestStruct::test_function<|> 590 TestStruct::test_function
558 } 591 }
559 ", 592 ",
560 ); 593 );
@@ -587,7 +620,7 @@ fn main() {
587 } 620 }
588 621
589 fn main() { 622 fn main() {
590 TestStruct::TEST_CONST<|> 623 TestStruct::TEST_CONST
591 } 624 }
592 ", 625 ",
593 ); 626 );
@@ -626,7 +659,7 @@ fn main() {
626 } 659 }
627 660
628 fn main() { 661 fn main() {
629 test_mod::TestStruct::test_function<|> 662 test_mod::TestStruct::test_function
630 } 663 }
631 ", 664 ",
632 ); 665 );
@@ -697,7 +730,7 @@ fn main() {
697 } 730 }
698 731
699 fn main() { 732 fn main() {
700 test_mod::TestStruct::TEST_CONST<|> 733 test_mod::TestStruct::TEST_CONST
701 } 734 }
702 ", 735 ",
703 ); 736 );
@@ -770,7 +803,7 @@ fn main() {
770 803
771 fn main() { 804 fn main() {
772 let test_struct = test_mod::TestStruct {}; 805 let test_struct = test_mod::TestStruct {};
773 test_struct.test_meth<|>od() 806 test_struct.test_method()
774 } 807 }
775 ", 808 ",
776 ); 809 );