aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/auto_import.rs138
1 files changed, 29 insertions, 109 deletions
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index 77380b816..6a0c351f1 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -5,42 +5,6 @@ use ra_syntax::{
5}; 5};
6use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder}; 6use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder};
7 7
8// TODO: refactor this before merge
9mod formatting {
10 use ra_syntax::{
11 AstNode, SyntaxNode,
12 ast::{self, AstToken},
13 algo::generate,
14};
15
16 /// If the node is on the beginning of the line, calculate indent.
17 pub fn leading_indent(node: &SyntaxNode) -> Option<&str> {
18 for leaf in prev_leaves(node) {
19 if let Some(ws) = ast::Whitespace::cast(leaf) {
20 let ws_text = ws.text();
21 if let Some(pos) = ws_text.rfind('\n') {
22 return Some(&ws_text[pos + 1..]);
23 }
24 }
25 if leaf.leaf_text().unwrap().contains('\n') {
26 break;
27 }
28 }
29 None
30 }
31
32 fn prev_leaves(node: &SyntaxNode) -> impl Iterator<Item = &SyntaxNode> {
33 generate(prev_leaf(node), |&node| prev_leaf(node))
34 }
35
36 fn prev_leaf(node: &SyntaxNode) -> Option<&SyntaxNode> {
37 generate(node.ancestors().find_map(SyntaxNode::prev_sibling), |it| {
38 it.last_child()
39 })
40 .last()
41 }
42}
43
44fn collect_path_segments(path: &ast::Path) -> Option<Vec<&ast::PathSegment>> { 8fn collect_path_segments(path: &ast::Path) -> Option<Vec<&ast::PathSegment>> {
45 let mut v = Vec::new(); 9 let mut v = Vec::new();
46 collect_path_segments_raw(&mut v, path)?; 10 collect_path_segments_raw(&mut v, path)?;
@@ -102,11 +66,7 @@ fn fmt_segments_raw(segments: &[&ast::PathSegment], buf: &mut String) {
102 66
103// Returns the numeber of common segments. 67// Returns the numeber of common segments.
104fn compare_path_segments(left: &[&ast::PathSegment], right: &[&ast::PathSegment]) -> usize { 68fn compare_path_segments(left: &[&ast::PathSegment], right: &[&ast::PathSegment]) -> usize {
105 return left 69 return left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count();
106 .iter()
107 .zip(right)
108 .filter(|(l, r)| compare_path_segment(l, r))
109 .count();
110} 70}
111 71
112fn compare_path_segment(a: &ast::PathSegment, b: &ast::PathSegment) -> bool { 72fn compare_path_segment(a: &ast::PathSegment, b: &ast::PathSegment) -> bool {
@@ -166,10 +126,7 @@ enum ImportAction<'a> {
166 126
167impl<'a> ImportAction<'a> { 127impl<'a> ImportAction<'a> {
168 fn add_new_use(anchor: Option<&'a SyntaxNode>, add_after_anchor: bool) -> Self { 128 fn add_new_use(anchor: Option<&'a SyntaxNode>, add_after_anchor: bool) -> Self {
169 ImportAction::AddNewUse { 129 ImportAction::AddNewUse { anchor, add_after_anchor }
170 anchor,
171 add_after_anchor,
172 }
173 } 130 }
174 131
175 fn add_nested_import( 132 fn add_nested_import(
@@ -191,11 +148,7 @@ impl<'a> ImportAction<'a> {
191 tree_list: &'a ast::UseTreeList, 148 tree_list: &'a ast::UseTreeList,
192 add_self: bool, 149 add_self: bool,
193 ) -> Self { 150 ) -> Self {
194 ImportAction::AddInTreeList { 151 ImportAction::AddInTreeList { common_segments, tree_list, add_self }
195 common_segments,
196 tree_list,
197 add_self,
198 }
199 } 152 }
200 153
201 fn better<'b>(left: &'b ImportAction<'a>, right: &'b ImportAction<'a>) -> &'b ImportAction<'a> { 154 fn better<'b>(left: &'b ImportAction<'a>, right: &'b ImportAction<'a>) -> &'b ImportAction<'a> {
@@ -211,20 +164,12 @@ impl<'a> ImportAction<'a> {
211 (ImportAction::Nothing, _) => true, 164 (ImportAction::Nothing, _) => true,
212 (ImportAction::AddInTreeList { .. }, ImportAction::Nothing) => false, 165 (ImportAction::AddInTreeList { .. }, ImportAction::Nothing) => false,
213 ( 166 (
214 ImportAction::AddNestedImport { 167 ImportAction::AddNestedImport { common_segments: n, .. },
215 common_segments: n, .. 168 ImportAction::AddInTreeList { common_segments: m, .. },
216 },
217 ImportAction::AddInTreeList {
218 common_segments: m, ..
219 },
220 ) => n > m, 169 ) => n > m,
221 ( 170 (
222 ImportAction::AddInTreeList { 171 ImportAction::AddInTreeList { common_segments: n, .. },
223 common_segments: n, .. 172 ImportAction::AddNestedImport { common_segments: m, .. },
224 },
225 ImportAction::AddNestedImport {
226 common_segments: m, ..
227 },
228 ) => n > m, 173 ) => n > m,
229 (ImportAction::AddInTreeList { .. }, _) => true, 174 (ImportAction::AddInTreeList { .. }, _) => true,
230 (ImportAction::AddNestedImport { .. }, ImportAction::Nothing) => false, 175 (ImportAction::AddNestedImport { .. }, ImportAction::Nothing) => false,
@@ -283,11 +228,7 @@ fn walk_use_tree_for_best_action<'a>(
283 // e.g: target is std::fmt and we can have 228 // e.g: target is std::fmt and we can have
284 // use foo::bar 229 // use foo::bar
285 // We add a brand new use statement 230 // We add a brand new use statement
286 current_use_tree 231 current_use_tree.syntax().ancestors().find_map(ast::UseItem::cast).map(AstNode::syntax),
287 .syntax()
288 .ancestors()
289 .find_map(ast::UseItem::cast)
290 .map(AstNode::syntax),
291 true, 232 true,
292 ), 233 ),
293 common if common == left.len() && left.len() == right.len() => { 234 common if common == left.len() && left.len() == right.len() => {
@@ -398,8 +339,7 @@ fn best_action_for_target<'b, 'a: 'b>(
398 .filter_map(ast::UseItem::use_tree) 339 .filter_map(ast::UseItem::use_tree)
399 .map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target)) 340 .map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target))
400 .fold(None, |best, a| { 341 .fold(None, |best, a| {
401 best.and_then(|best| Some(*ImportAction::better(&best, &a))) 342 best.and_then(|best| Some(*ImportAction::better(&best, &a))).or(Some(a))
402 .or(Some(a))
403 }); 343 });
404 344
405 match best_action { 345 match best_action {
@@ -421,15 +361,10 @@ fn best_action_for_target<'b, 'a: 'b>(
421 361
422fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut AssistBuilder) { 362fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut AssistBuilder) {
423 match action { 363 match action {
424 ImportAction::AddNewUse { 364 ImportAction::AddNewUse { anchor, add_after_anchor } => {
425 anchor, 365 make_assist_add_new_use(anchor, *add_after_anchor, target, edit)
426 add_after_anchor, 366 }
427 } => make_assist_add_new_use(anchor, *add_after_anchor, target, edit), 367 ImportAction::AddInTreeList { common_segments, tree_list, add_self } => {
428 ImportAction::AddInTreeList {
429 common_segments,
430 tree_list,
431 add_self,
432 } => {
433 // We know that the fist n segments already exists in the use statement we want 368 // We know that the fist n segments already exists in the use statement we want
434 // to modify, so we want to add only the last target.len() - n segments. 369 // to modify, so we want to add only the last target.len() - n segments.
435 let segments_to_add = target.split_at(*common_segments).1; 370 let segments_to_add = target.split_at(*common_segments).1;
@@ -461,7 +396,7 @@ fn make_assist_add_new_use(
461 edit: &mut AssistBuilder, 396 edit: &mut AssistBuilder,
462) { 397) {
463 if let Some(anchor) = anchor { 398 if let Some(anchor) = anchor {
464 let indent = formatting::leading_indent(anchor); 399 let indent = ra_fmt::leading_indent(anchor);
465 let mut buf = String::new(); 400 let mut buf = String::new();
466 if after { 401 if after {
467 buf.push_str("\n"); 402 buf.push_str("\n");
@@ -478,11 +413,7 @@ fn make_assist_add_new_use(
478 buf.push_str(spaces); 413 buf.push_str(spaces);
479 } 414 }
480 } 415 }
481 let position = if after { 416 let position = if after { anchor.range().end() } else { anchor.range().start() };
482 anchor.range().end()
483 } else {
484 anchor.range().start()
485 };
486 edit.insert(position, buf); 417 edit.insert(position, buf);
487 } 418 }
488} 419}
@@ -496,10 +427,7 @@ fn make_assist_add_in_tree_list(
496 let last = tree_list.use_trees().last(); 427 let last = tree_list.use_trees().last();
497 if let Some(last) = last { 428 if let Some(last) = last {
498 let mut buf = String::new(); 429 let mut buf = String::new();
499 let comma = last 430 let comma = last.syntax().siblings(Direction::Next).find(|n| n.kind() == COMMA);
500 .syntax()
501 .siblings(Direction::Next)
502 .find(|n| n.kind() == COMMA);
503 let offset = if let Some(comma) = comma { 431 let offset = if let Some(comma) = comma {
504 comma.range().end() 432 comma.range().end()
505 } else { 433 } else {
@@ -558,12 +486,7 @@ pub(crate) fn auto_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
558 486
559 let path = node.ancestors().find_map(ast::Path::cast)?; 487 let path = node.ancestors().find_map(ast::Path::cast)?;
560 // We don't want to mess with use statements 488 // We don't want to mess with use statements
561 if path 489 if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
562 .syntax()
563 .ancestors()
564 .find_map(ast::UseItem::cast)
565 .is_some()
566 {
567 return None; 490 return None;
568 } 491 }
569 492
@@ -572,21 +495,18 @@ pub(crate) fn auto_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
572 return None; 495 return None;
573 } 496 }
574 497
575 ctx.build( 498 ctx.build(format!("import {} in the current file", fmt_segments(&segments)), |edit| {
576 format!("import {} in the current file", fmt_segments(&segments)), 499 let action = best_action_for_target(current_file.syntax(), path, &segments);
577 |edit| { 500 make_assist(&action, segments.as_slice(), edit);
578 let action = best_action_for_target(current_file.syntax(), path, &segments); 501 if let Some(last_segment) = path.segment() {
579 make_assist(&action, segments.as_slice(), edit); 502 // Here we are assuming the assist will provide a correct use statement
580 if let Some(last_segment) = path.segment() { 503 // so we can delete the path qualifier
581 // Here we are assuming the assist will provide a correct use statement 504 edit.delete(TextRange::from_to(
582 // so we can delete the path qualifier 505 path.syntax().range().start(),
583 edit.delete(TextRange::from_to( 506 last_segment.syntax().range().start(),
584 path.syntax().range().start(), 507 ));
585 last_segment.syntax().range().start(), 508 }
586 )); 509 })
587 }
588 },
589 )
590} 510}
591 511
592#[cfg(test)] 512#[cfg(test)]