diff options
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r-- | crates/ra_assists/src/auto_import.rs | 96 |
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 @@ | |||
1 | use hir::db::HirDatabase; | 1 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ | 2 | use 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 | }; |
6 | use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder}; | 6 | use 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 | ||
483 | fn 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 | |||
483 | pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 501 | pub(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 | " | ||
559 | std::fmt<|>::Debug | ||
560 | ", | ||
561 | " | ||
562 | use std::fmt; | ||
563 | |||
564 | fmt<|>::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 | " | ||
772 | use 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 | " | ||
782 | mod foo { | ||
783 | mod bar { | ||
784 | std::fmt::Debug<|> | ||
785 | } | ||
786 | } | ||
787 | ", | ||
788 | " | ||
789 | mod foo { | ||
790 | mod bar { | ||
791 | use std::fmt::Debug; | ||
792 | |||
793 | Debug<|> | ||
794 | } | ||
795 | } | ||
796 | ", | ||
797 | ); | ||
798 | } | ||
731 | } | 799 | } |