aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r--crates/ra_assists/src/auto_import.rs96
1 files changed, 82 insertions, 14 deletions
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index b251c9369..52c2a0b2b 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -1,6 +1,6 @@
1use hir::db::HirDatabase; 1use hir::db::HirDatabase;
2use ra_syntax::{ 2use ra_syntax::{
3 ast, AstNode, SyntaxNode, Direction, TextRange, 3 ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange,
4 SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA } 4 SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA }
5}; 5};
6use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder}; 6use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder};
@@ -345,9 +345,9 @@ fn best_action_for_target<'b, 'a: 'b>(
345 match best_action { 345 match best_action {
346 Some(action) => return action, 346 Some(action) => return action,
347 None => { 347 None => {
348 // We have no action we no use item was found in container so we find 348 // We have no action and no UseItem was found in container so we find
349 // another item and we use it as anchor. 349 // another item and we use it as anchor.
350 // If there are not items, we choose the target path itself as anchor. 350 // If there are no items, we choose the target path itself as anchor.
351 let anchor = container 351 let anchor = container
352 .children() 352 .children()
353 .find_map(ast::ModuleItem::cast) 353 .find_map(ast::ModuleItem::cast)
@@ -480,6 +480,24 @@ fn make_assist_add_nested_import(
480 } 480 }
481} 481}
482 482
483fn apply_auto_import<'a>(
484 container: &SyntaxNode,
485 path: &ast::Path,
486 target: &[&'a ast::PathSegment],
487 edit: &mut AssistBuilder,
488) {
489 let action = best_action_for_target(container, path, target);
490 make_assist(&action, target, edit);
491 if let (Some(first), Some(last)) = (target.first(), target.last()) {
492 // Here we are assuming the assist will provide a correct use statement
493 // so we can delete the path qualifier
494 edit.delete(TextRange::from_to(
495 first.syntax().range().start(),
496 last.syntax().range().start(),
497 ));
498 }
499}
500
483pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 501pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
484 let node = ctx.covering_node(); 502 let node = ctx.covering_node();
485 let current_file = node.ancestors().find_map(ast::SourceFile::cast)?; 503 let current_file = node.ancestors().find_map(ast::SourceFile::cast)?;
@@ -495,18 +513,20 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
495 return None; 513 return None;
496 } 514 }
497 515
498 ctx.add_action(format!("import {} in the current file", fmt_segments(&segments)), |edit| { 516 if let Some(module) = path.syntax().ancestors().find_map(ast::Module::cast) {
499 let action = best_action_for_target(current_file.syntax(), path, &segments); 517 if let (Some(item_list), Some(name)) = (module.item_list(), module.name()) {
500 make_assist(&action, segments.as_slice(), edit); 518 ctx.add_action(
501 if let Some(last_segment) = path.segment() { 519 format!("import {} in mod {}", fmt_segments(&segments), name.text()),
502 // Here we are assuming the assist will provide a correct use statement 520 |edit| {
503 // so we can delete the path qualifier 521 apply_auto_import(item_list.syntax(), path, &segments, edit);
504 edit.delete(TextRange::from_to( 522 },
505 path.syntax().range().start(), 523 );
506 last_segment.syntax().range().start(),
507 ));
508 } 524 }
509 }); 525 } else {
526 ctx.add_action(format!("import {} in the current file", fmt_segments(&segments)), |edit| {
527 apply_auto_import(current_file.syntax(), path, &segments, edit);
528 });
529 }
510 530
511 ctx.build() 531 ctx.build()
512} 532}
@@ -532,6 +552,21 @@ Debug<|>
532 } 552 }
533 553
534 #[test] 554 #[test]
555 fn test_auto_import_file_add_use_no_anchor_2seg() {
556 check_assist(
557 auto_import,
558 "
559std::fmt<|>::Debug
560 ",
561 "
562use std::fmt;
563
564fmt<|>::Debug
565 ",
566 );
567 }
568
569 #[test]
535 fn test_auto_import_file_add_use() { 570 fn test_auto_import_file_add_use() {
536 check_assist( 571 check_assist(
537 auto_import, 572 auto_import,
@@ -728,4 +763,37 @@ impl foo<|> for Foo {
728", 763",
729 ); 764 );
730 } 765 }
766
767 #[test]
768 fn test_auto_import_not_applicable_in_use() {
769 check_assist_not_applicable(
770 auto_import,
771 "
772use std::fmt<|>;
773",
774 );
775 }
776
777 #[test]
778 fn test_auto_import_file_add_use_no_anchor_in_mod_mod() {
779 check_assist(
780 auto_import,
781 "
782mod foo {
783 mod bar {
784 std::fmt::Debug<|>
785 }
786}
787 ",
788 "
789mod foo {
790 mod bar {
791 use std::fmt::Debug;
792
793 Debug<|>
794 }
795}
796 ",
797 );
798 }
731} 799}