aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/auto_import.rs128
1 files changed, 65 insertions, 63 deletions
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index 6bc5caf76..7527d805d 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -1,21 +1,15 @@
1use ra_text_edit::TextEditBuilder; 1use ra_text_edit::TextEditBuilder;
2use hir::db::HirDatabase; 2use hir::{ self, db::HirDatabase};
3 3
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange, 5 ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange, SmolStr,
6 SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA } 6 SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA }
7}; 7};
8use crate::{ 8use crate::{
9 AssistId, 9 AssistId,
10 assist_ctx::{AssistCtx, Assist, AssistBuilder}, 10 assist_ctx::{AssistCtx, Assist},
11}; 11};
12 12
13fn collect_path_segments(path: &ast::Path) -> Option<Vec<&ast::PathSegment>> {
14 let mut v = Vec::new();
15 collect_path_segments_raw(&mut v, path)?;
16 return Some(v);
17}
18
19fn collect_path_segments_raw<'a>( 13fn collect_path_segments_raw<'a>(
20 segments: &mut Vec<&'a ast::PathSegment>, 14 segments: &mut Vec<&'a ast::PathSegment>,
21 mut path: &'a ast::Path, 15 mut path: &'a ast::Path,
@@ -46,59 +40,43 @@ fn collect_path_segments_raw<'a>(
46 return Some(segments.len() - oldlen); 40 return Some(segments.len() - oldlen);
47} 41}
48 42
49fn fmt_segments(segments: &[&ast::PathSegment]) -> String { 43fn fmt_segments(segments: &[SmolStr]) -> String {
50 let mut buf = String::new(); 44 let mut buf = String::new();
51 fmt_segments_raw(segments, &mut buf); 45 fmt_segments_raw(segments, &mut buf);
52 return buf; 46 return buf;
53} 47}
54 48
55fn fmt_segments_raw(segments: &[&ast::PathSegment], buf: &mut String) { 49fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
56 let mut first = true; 50 let mut iter = segments.iter();
57 for s in segments { 51 if let Some(s) = iter.next() {
58 if !first { 52 buf.push_str(s);
59 buf.push_str("::"); 53 }
60 } 54 for s in iter {
61 match s.kind() { 55 buf.push_str("::");
62 Some(ast::PathSegmentKind::Name(nameref)) => buf.push_str(nameref.text()), 56 buf.push_str(s);
63 Some(ast::PathSegmentKind::SelfKw) => buf.push_str("self"),
64 Some(ast::PathSegmentKind::SuperKw) => buf.push_str("super"),
65 Some(ast::PathSegmentKind::CrateKw) => buf.push_str("crate"),
66 None => {}
67 }
68 first = false;
69 } 57 }
70} 58}
71 59
72// Returns the numeber of common segments. 60// Returns the numeber of common segments.
73fn compare_path_segments(left: &[&ast::PathSegment], right: &[&ast::PathSegment]) -> usize { 61fn compare_path_segments(left: &[SmolStr], right: &[&ast::PathSegment]) -> usize {
74 return left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count(); 62 return left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count();
75} 63}
76 64
77fn compare_path_segment(a: &ast::PathSegment, b: &ast::PathSegment) -> bool { 65fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
78 if let (Some(ka), Some(kb)) = (a.kind(), b.kind()) { 66 if let Some(kb) = b.kind() {
79 match (ka, kb) { 67 match kb {
80 (ast::PathSegmentKind::Name(nameref_a), ast::PathSegmentKind::Name(nameref_b)) => { 68 ast::PathSegmentKind::Name(nameref_b) => a == nameref_b.text(),
81 nameref_a.text() == nameref_b.text() 69 ast::PathSegmentKind::SelfKw => a == "self",
82 } 70 ast::PathSegmentKind::SuperKw => a == "super",
83 (ast::PathSegmentKind::SelfKw, ast::PathSegmentKind::SelfKw) => true, 71 ast::PathSegmentKind::CrateKw => a == "crate",
84 (ast::PathSegmentKind::SuperKw, ast::PathSegmentKind::SuperKw) => true,
85 (ast::PathSegmentKind::CrateKw, ast::PathSegmentKind::CrateKw) => true,
86 (_, _) => false,
87 } 72 }
88 } else { 73 } else {
89 false 74 false
90 } 75 }
91} 76}
92 77
93fn compare_path_segment_with_name(a: &ast::PathSegment, b: &ast::Name) -> bool { 78fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool {
94 if let Some(ka) = a.kind() { 79 a == b.text()
95 return match (ka, b) {
96 (ast::PathSegmentKind::Name(nameref_a), _) => nameref_a.text() == b.text(),
97 (_, _) => false,
98 };
99 } else {
100 false
101 }
102} 80}
103 81
104#[derive(Copy, Clone)] 82#[derive(Copy, Clone)]
@@ -190,7 +168,7 @@ fn walk_use_tree_for_best_action<'a>(
190 current_path_segments: &mut Vec<&'a ast::PathSegment>, // buffer containing path segments 168 current_path_segments: &mut Vec<&'a ast::PathSegment>, // buffer containing path segments
191 current_parent_use_tree_list: Option<&'a ast::UseTreeList>, // will be Some value if we are in a nested import 169 current_parent_use_tree_list: Option<&'a ast::UseTreeList>, // will be Some value if we are in a nested import
192 current_use_tree: &'a ast::UseTree, // the use tree we are currently examinating 170 current_use_tree: &'a ast::UseTree, // the use tree we are currently examinating
193 target: &[&'a ast::PathSegment], // the path we want to import 171 target: &[SmolStr], // the path we want to import
194) -> ImportAction<'a> { 172) -> ImportAction<'a> {
195 // We save the number of segments in the buffer so we can restore the correct segments 173 // We save the number of segments in the buffer so we can restore the correct segments
196 // before returning. Recursive call will add segments so we need to delete them. 174 // before returning. Recursive call will add segments so we need to delete them.
@@ -216,7 +194,7 @@ fn walk_use_tree_for_best_action<'a>(
216 194
217 // This can happen only if current_use_tree is a direct child of a UseItem 195 // This can happen only if current_use_tree is a direct child of a UseItem
218 if let Some(name) = alias.and_then(ast::NameOwner::name) { 196 if let Some(name) = alias.and_then(ast::NameOwner::name) {
219 if compare_path_segment_with_name(target[0], name) { 197 if compare_path_segment_with_name(&target[0], name) {
220 return ImportAction::Nothing; 198 return ImportAction::Nothing;
221 } 199 }
222 } 200 }
@@ -345,8 +323,8 @@ fn walk_use_tree_for_best_action<'a>(
345 323
346fn best_action_for_target<'b, 'a: 'b>( 324fn best_action_for_target<'b, 'a: 'b>(
347 container: &'a SyntaxNode, 325 container: &'a SyntaxNode,
348 path: &'a ast::Path, 326 anchor: &'a SyntaxNode,
349 target: &'b [&'a ast::PathSegment], 327 target: &'b [SmolStr],
350) -> ImportAction<'a> { 328) -> ImportAction<'a> {
351 let mut storage = Vec::with_capacity(16); // this should be the only allocation 329 let mut storage = Vec::with_capacity(16); // this should be the only allocation
352 let best_action = container 330 let best_action = container
@@ -368,14 +346,14 @@ fn best_action_for_target<'b, 'a: 'b>(
368 .children() 346 .children()
369 .find_map(ast::ModuleItem::cast) 347 .find_map(ast::ModuleItem::cast)
370 .map(AstNode::syntax) 348 .map(AstNode::syntax)
371 .or(Some(path.syntax())); 349 .or(Some(anchor));
372 350
373 return ImportAction::add_new_use(anchor, false); 351 return ImportAction::add_new_use(anchor, false);
374 } 352 }
375 } 353 }
376} 354}
377 355
378fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut TextEditBuilder) { 356fn make_assist(action: &ImportAction, target: &[SmolStr], edit: &mut TextEditBuilder) {
379 match action { 357 match action {
380 ImportAction::AddNewUse { anchor, add_after_anchor } => { 358 ImportAction::AddNewUse { anchor, add_after_anchor } => {
381 make_assist_add_new_use(anchor, *add_after_anchor, target, edit) 359 make_assist_add_new_use(anchor, *add_after_anchor, target, edit)
@@ -408,7 +386,7 @@ fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut T
408fn make_assist_add_new_use( 386fn make_assist_add_new_use(
409 anchor: &Option<&SyntaxNode>, 387 anchor: &Option<&SyntaxNode>,
410 after: bool, 388 after: bool,
411 target: &[&ast::PathSegment], 389 target: &[SmolStr],
412 edit: &mut TextEditBuilder, 390 edit: &mut TextEditBuilder,
413) { 391) {
414 if let Some(anchor) = anchor { 392 if let Some(anchor) = anchor {
@@ -436,7 +414,7 @@ fn make_assist_add_new_use(
436 414
437fn make_assist_add_in_tree_list( 415fn make_assist_add_in_tree_list(
438 tree_list: &ast::UseTreeList, 416 tree_list: &ast::UseTreeList,
439 target: &[&ast::PathSegment], 417 target: &[SmolStr],
440 add_self: bool, 418 add_self: bool,
441 edit: &mut TextEditBuilder, 419 edit: &mut TextEditBuilder,
442) { 420) {
@@ -465,7 +443,7 @@ fn make_assist_add_in_tree_list(
465fn make_assist_add_nested_import( 443fn make_assist_add_nested_import(
466 path: &ast::Path, 444 path: &ast::Path,
467 first_segment_to_split: &Option<&ast::PathSegment>, 445 first_segment_to_split: &Option<&ast::PathSegment>,
468 target: &[&ast::PathSegment], 446 target: &[SmolStr],
469 add_self: bool, 447 add_self: bool,
470 edit: &mut TextEditBuilder, 448 edit: &mut TextEditBuilder,
471) { 449) {
@@ -496,28 +474,51 @@ fn make_assist_add_nested_import(
496 } 474 }
497} 475}
498 476
499fn apply_auto_import<'a>( 477fn apply_auto_import(
500 container: &SyntaxNode, 478 container: &SyntaxNode,
501 path: &ast::Path, 479 path: &ast::Path,
502 target: &[&'a ast::PathSegment], 480 target: &[SmolStr],
503 edit: &mut TextEditBuilder, 481 edit: &mut TextEditBuilder,
504) { 482) {
505 let action = best_action_for_target(container, path, target); 483 let action = best_action_for_target(container, path.syntax(), target);
506 make_assist(&action, target, edit); 484 make_assist(&action, target, edit);
507 if let (Some(first), Some(last)) = (target.first(), target.last()) { 485 if let Some(last) = path.segment() {
508 // Here we are assuming the assist will provide a correct use statement 486 // Here we are assuming the assist will provide a correct use statement
509 // so we can delete the path qualifier 487 // so we can delete the path qualifier
510 edit.delete(TextRange::from_to( 488 edit.delete(TextRange::from_to(
511 first.syntax().range().start(), 489 path.syntax().range().start(),
512 last.syntax().range().start(), 490 last.syntax().range().start(),
513 )); 491 ));
514 } 492 }
515} 493}
516 494
517pub fn auto_import_text_edit<'a>( 495#[allow(unused)]
496pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> {
497 let mut ps = Vec::<SmolStr>::with_capacity(10);
498 match path.kind {
499 hir::PathKind::Abs => ps.push("".into()),
500 hir::PathKind::Crate => ps.push("crate".into()),
501 hir::PathKind::Plain => {},
502 hir::PathKind::Self_ => ps.push("self".into()),
503 hir::PathKind::Super => ps.push("super".into())
504 }
505 for s in path.segments.iter() {
506 ps.push(s.name.to_smolstr());
507 }
508 ps
509}
510
511// This function produces sequence of text edits into edit
512// to import the target path in the most appropriate scope given
513// the cursor position
514#[allow(unused)]
515pub fn auto_import_text_edit(
516 // Ideally the position of the cursor, used to
518 position: &SyntaxNode, 517 position: &SyntaxNode,
519 path: &ast::Path, 518 // The statement to use as anchor (last resort)
520 target: &[&'a ast::PathSegment], 519 anchor: &SyntaxNode,
520 // The path to import as a sequence of strings
521 target: &[SmolStr],
521 edit: &mut TextEditBuilder, 522 edit: &mut TextEditBuilder,
522) { 523) {
523 let container = position.ancestors().find_map(|n| { 524 let container = position.ancestors().find_map(|n| {
@@ -528,7 +529,7 @@ pub fn auto_import_text_edit<'a>(
528 }); 529 });
529 530
530 if let Some(container) = container { 531 if let Some(container) = container {
531 let action = best_action_for_target(container, path, target); 532 let action = best_action_for_target(container, anchor, target);
532 make_assist(&action, target, edit); 533 make_assist(&action, target, edit);
533 } 534 }
534} 535}
@@ -540,7 +541,8 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
540 return None; 541 return None;
541 } 542 }
542 543
543 let segments = collect_path_segments(path)?; 544 let hir_path = hir::Path::from_ast(path)?;
545 let segments = collect_hir_path_segments(&hir_path);
544 if segments.len() < 2 { 546 if segments.len() < 2 {
545 return None; 547 return None;
546 } 548 }