diff options
Diffstat (limited to 'crates/ra_assists')
-rw-r--r-- | crates/ra_assists/src/auto_import.rs | 128 |
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 @@ | |||
1 | use ra_text_edit::TextEditBuilder; | 1 | use ra_text_edit::TextEditBuilder; |
2 | use hir::db::HirDatabase; | 2 | use hir::{ self, db::HirDatabase}; |
3 | 3 | ||
4 | use ra_syntax::{ | 4 | use 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 | }; |
8 | use crate::{ | 8 | use crate::{ |
9 | AssistId, | 9 | AssistId, |
10 | assist_ctx::{AssistCtx, Assist, AssistBuilder}, | 10 | assist_ctx::{AssistCtx, Assist}, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | fn 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 | |||
19 | fn collect_path_segments_raw<'a>( | 13 | fn 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 | ||
49 | fn fmt_segments(segments: &[&ast::PathSegment]) -> String { | 43 | fn 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 | ||
55 | fn fmt_segments_raw(segments: &[&ast::PathSegment], buf: &mut String) { | 49 | fn 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. |
73 | fn compare_path_segments(left: &[&ast::PathSegment], right: &[&ast::PathSegment]) -> usize { | 61 | fn 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 | ||
77 | fn compare_path_segment(a: &ast::PathSegment, b: &ast::PathSegment) -> bool { | 65 | fn 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 | ||
93 | fn compare_path_segment_with_name(a: &ast::PathSegment, b: &ast::Name) -> bool { | 78 | fn 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 | ||
346 | fn best_action_for_target<'b, 'a: 'b>( | 324 | fn 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 | ||
378 | fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut TextEditBuilder) { | 356 | fn 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 | |||
408 | fn make_assist_add_new_use( | 386 | fn 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 | ||
437 | fn make_assist_add_in_tree_list( | 415 | fn 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( | |||
465 | fn make_assist_add_nested_import( | 443 | fn 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 | ||
499 | fn apply_auto_import<'a>( | 477 | fn 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 | ||
517 | pub fn auto_import_text_edit<'a>( | 495 | #[allow(unused)] |
496 | pub 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)] | ||
515 | pub 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 | } |