aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assist_ctx.rs4
-rw-r--r--crates/ra_assists/src/auto_import.rs203
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_hir/src/name.rs13
-rw-r--r--crates/ra_hir/src/source_binder.rs2
-rw-r--r--crates/ra_ide_api/src/completion/complete_scope.rs120
-rw-r--r--crates/ra_ide_api/src/completion/completion_context.rs4
7 files changed, 271 insertions, 77 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index f46de61eb..e744e82d0 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -144,6 +144,10 @@ impl AssistBuilder {
144 self.replace(node.range(), replace_with) 144 self.replace(node.range(), replace_with)
145 } 145 }
146 146
147 pub(crate) fn set_edit_builder(&mut self, edit: TextEditBuilder) {
148 self.edit = edit;
149 }
150
147 #[allow(unused)] 151 #[allow(unused)]
148 pub(crate) fn delete(&mut self, range: TextRange) { 152 pub(crate) fn delete(&mut self, range: TextRange) {
149 self.edit.delete(range) 153 self.edit.delete(range)
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index 3fdf6b0d9..7c856c19b 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -1,20 +1,15 @@
1use hir::db::HirDatabase; 1use ra_text_edit::TextEditBuilder;
2use hir::{ self, db::HirDatabase};
2 3
3use ra_syntax::{ 4use ra_syntax::{
4 ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange, 5 ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange, SmolStr,
5 SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA } 6 SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA }
6}; 7};
7use crate::{ 8use crate::{
8 AssistId, 9 AssistId,
9 assist_ctx::{AssistCtx, Assist, AssistBuilder}, 10 assist_ctx::{AssistCtx, Assist},
10}; 11};
11 12
12fn collect_path_segments(path: &ast::Path) -> Option<Vec<&ast::PathSegment>> {
13 let mut v = Vec::new();
14 collect_path_segments_raw(&mut v, path)?;
15 return Some(v);
16}
17
18fn collect_path_segments_raw<'a>( 13fn collect_path_segments_raw<'a>(
19 segments: &mut Vec<&'a ast::PathSegment>, 14 segments: &mut Vec<&'a ast::PathSegment>,
20 mut path: &'a ast::Path, 15 mut path: &'a ast::Path,
@@ -45,59 +40,43 @@ fn collect_path_segments_raw<'a>(
45 return Some(segments.len() - oldlen); 40 return Some(segments.len() - oldlen);
46} 41}
47 42
48fn fmt_segments(segments: &[&ast::PathSegment]) -> String { 43fn fmt_segments(segments: &[SmolStr]) -> String {
49 let mut buf = String::new(); 44 let mut buf = String::new();
50 fmt_segments_raw(segments, &mut buf); 45 fmt_segments_raw(segments, &mut buf);
51 return buf; 46 return buf;
52} 47}
53 48
54fn fmt_segments_raw(segments: &[&ast::PathSegment], buf: &mut String) { 49fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
55 let mut first = true; 50 let mut iter = segments.iter();
56 for s in segments { 51 if let Some(s) = iter.next() {
57 if !first { 52 buf.push_str(s);
58 buf.push_str("::"); 53 }
59 } 54 for s in iter {
60 match s.kind() { 55 buf.push_str("::");
61 Some(ast::PathSegmentKind::Name(nameref)) => buf.push_str(nameref.text()), 56 buf.push_str(s);
62 Some(ast::PathSegmentKind::SelfKw) => buf.push_str("self"),
63 Some(ast::PathSegmentKind::SuperKw) => buf.push_str("super"),
64 Some(ast::PathSegmentKind::CrateKw) => buf.push_str("crate"),
65 None => {}
66 }
67 first = false;
68 } 57 }
69} 58}
70 59
71// Returns the numeber of common segments. 60// Returns the numeber of common segments.
72fn compare_path_segments(left: &[&ast::PathSegment], right: &[&ast::PathSegment]) -> usize { 61fn compare_path_segments(left: &[SmolStr], right: &[&ast::PathSegment]) -> usize {
73 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();
74} 63}
75 64
76fn compare_path_segment(a: &ast::PathSegment, b: &ast::PathSegment) -> bool { 65fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
77 if let (Some(ka), Some(kb)) = (a.kind(), b.kind()) { 66 if let Some(kb) = b.kind() {
78 match (ka, kb) { 67 match kb {
79 (ast::PathSegmentKind::Name(nameref_a), ast::PathSegmentKind::Name(nameref_b)) => { 68 ast::PathSegmentKind::Name(nameref_b) => a == nameref_b.text(),
80 nameref_a.text() == nameref_b.text() 69 ast::PathSegmentKind::SelfKw => a == "self",
81 } 70 ast::PathSegmentKind::SuperKw => a == "super",
82 (ast::PathSegmentKind::SelfKw, ast::PathSegmentKind::SelfKw) => true, 71 ast::PathSegmentKind::CrateKw => a == "crate",
83 (ast::PathSegmentKind::SuperKw, ast::PathSegmentKind::SuperKw) => true,
84 (ast::PathSegmentKind::CrateKw, ast::PathSegmentKind::CrateKw) => true,
85 (_, _) => false,
86 } 72 }
87 } else { 73 } else {
88 false 74 false
89 } 75 }
90} 76}
91 77
92fn compare_path_segment_with_name(a: &ast::PathSegment, b: &ast::Name) -> bool { 78fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool {
93 if let Some(ka) = a.kind() { 79 a == b.text()
94 return match (ka, b) {
95 (ast::PathSegmentKind::Name(nameref_a), _) => nameref_a.text() == b.text(),
96 (_, _) => false,
97 };
98 } else {
99 false
100 }
101} 80}
102 81
103#[derive(Copy, Clone)] 82#[derive(Copy, Clone)]
@@ -189,7 +168,7 @@ fn walk_use_tree_for_best_action<'a>(
189 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
190 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
191 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
192 target: &[&'a ast::PathSegment], // the path we want to import 171 target: &[SmolStr], // the path we want to import
193) -> ImportAction<'a> { 172) -> ImportAction<'a> {
194 // 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
195 // 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.
@@ -215,7 +194,7 @@ fn walk_use_tree_for_best_action<'a>(
215 194
216 // 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
217 if let Some(name) = alias.and_then(ast::NameOwner::name) { 196 if let Some(name) = alias.and_then(ast::NameOwner::name) {
218 if compare_path_segment_with_name(target[0], name) { 197 if compare_path_segment_with_name(&target[0], name) {
219 return ImportAction::Nothing; 198 return ImportAction::Nothing;
220 } 199 }
221 } 200 }
@@ -344,8 +323,8 @@ fn walk_use_tree_for_best_action<'a>(
344 323
345fn best_action_for_target<'b, 'a: 'b>( 324fn best_action_for_target<'b, 'a: 'b>(
346 container: &'a SyntaxNode, 325 container: &'a SyntaxNode,
347 path: &'a ast::Path, 326 anchor: &'a SyntaxNode,
348 target: &'b [&'a ast::PathSegment], 327 target: &'b [SmolStr],
349) -> ImportAction<'a> { 328) -> ImportAction<'a> {
350 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
351 let best_action = container 330 let best_action = container
@@ -362,19 +341,19 @@ fn best_action_for_target<'b, 'a: 'b>(
362 None => { 341 None => {
363 // We have no action and no UseItem was found in container so we find 342 // We have no action and no UseItem was found in container so we find
364 // another item and we use it as anchor. 343 // another item and we use it as anchor.
365 // If there are no items, we choose the target path itself as anchor. 344 // If there are no items above, we choose the target path itself as anchor.
345 // todo: we should include even whitespace blocks as anchor candidates
366 let anchor = container 346 let anchor = container
367 .children() 347 .children()
368 .find_map(ast::ModuleItem::cast) 348 .find(|n| n.range().start() < anchor.range().start())
369 .map(AstNode::syntax) 349 .or(Some(anchor));
370 .or(Some(path.syntax()));
371 350
372 return ImportAction::add_new_use(anchor, false); 351 return ImportAction::add_new_use(anchor, false);
373 } 352 }
374 } 353 }
375} 354}
376 355
377fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut AssistBuilder) { 356fn make_assist(action: &ImportAction, target: &[SmolStr], edit: &mut TextEditBuilder) {
378 match action { 357 match action {
379 ImportAction::AddNewUse { anchor, add_after_anchor } => { 358 ImportAction::AddNewUse { anchor, add_after_anchor } => {
380 make_assist_add_new_use(anchor, *add_after_anchor, target, edit) 359 make_assist_add_new_use(anchor, *add_after_anchor, target, edit)
@@ -407,8 +386,8 @@ fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut A
407fn make_assist_add_new_use( 386fn make_assist_add_new_use(
408 anchor: &Option<&SyntaxNode>, 387 anchor: &Option<&SyntaxNode>,
409 after: bool, 388 after: bool,
410 target: &[&ast::PathSegment], 389 target: &[SmolStr],
411 edit: &mut AssistBuilder, 390 edit: &mut TextEditBuilder,
412) { 391) {
413 if let Some(anchor) = anchor { 392 if let Some(anchor) = anchor {
414 let indent = ra_fmt::leading_indent(anchor); 393 let indent = ra_fmt::leading_indent(anchor);
@@ -435,9 +414,9 @@ fn make_assist_add_new_use(
435 414
436fn make_assist_add_in_tree_list( 415fn make_assist_add_in_tree_list(
437 tree_list: &ast::UseTreeList, 416 tree_list: &ast::UseTreeList,
438 target: &[&ast::PathSegment], 417 target: &[SmolStr],
439 add_self: bool, 418 add_self: bool,
440 edit: &mut AssistBuilder, 419 edit: &mut TextEditBuilder,
441) { 420) {
442 let last = tree_list.use_trees().last(); 421 let last = tree_list.use_trees().last();
443 if let Some(last) = last { 422 if let Some(last) = last {
@@ -464,9 +443,9 @@ fn make_assist_add_in_tree_list(
464fn make_assist_add_nested_import( 443fn make_assist_add_nested_import(
465 path: &ast::Path, 444 path: &ast::Path,
466 first_segment_to_split: &Option<&ast::PathSegment>, 445 first_segment_to_split: &Option<&ast::PathSegment>,
467 target: &[&ast::PathSegment], 446 target: &[SmolStr],
468 add_self: bool, 447 add_self: bool,
469 edit: &mut AssistBuilder, 448 edit: &mut TextEditBuilder,
470) { 449) {
471 let use_tree = path.syntax().ancestors().find_map(ast::UseTree::cast); 450 let use_tree = path.syntax().ancestors().find_map(ast::UseTree::cast);
472 if let Some(use_tree) = use_tree { 451 if let Some(use_tree) = use_tree {
@@ -491,28 +470,68 @@ fn make_assist_add_nested_import(
491 buf.push_str(", "); 470 buf.push_str(", ");
492 } 471 }
493 edit.insert(start, buf); 472 edit.insert(start, buf);
494 edit.insert(end, "}"); 473 edit.insert(end, "}".to_string());
495 } 474 }
496} 475}
497 476
498fn apply_auto_import<'a>( 477fn apply_auto_import(
499 container: &SyntaxNode, 478 container: &SyntaxNode,
500 path: &ast::Path, 479 path: &ast::Path,
501 target: &[&'a ast::PathSegment], 480 target: &[SmolStr],
502 edit: &mut AssistBuilder, 481 edit: &mut TextEditBuilder,
503) { 482) {
504 let action = best_action_for_target(container, path, target); 483 let action = best_action_for_target(container, path.syntax(), target);
505 make_assist(&action, target, edit); 484 make_assist(&action, target, edit);
506 if let (Some(first), Some(last)) = (target.first(), target.last()) { 485 if let Some(last) = path.segment() {
507 // 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
508 // so we can delete the path qualifier 487 // so we can delete the path qualifier
509 edit.delete(TextRange::from_to( 488 edit.delete(TextRange::from_to(
510 first.syntax().range().start(), 489 path.syntax().range().start(),
511 last.syntax().range().start(), 490 last.syntax().range().start(),
512 )); 491 ));
513 } 492 }
514} 493}
515 494
495pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> {
496 let mut ps = Vec::<SmolStr>::with_capacity(10);
497 match path.kind {
498 hir::PathKind::Abs => ps.push("".into()),
499 hir::PathKind::Crate => ps.push("crate".into()),
500 hir::PathKind::Plain => {}
501 hir::PathKind::Self_ => ps.push("self".into()),
502 hir::PathKind::Super => ps.push("super".into()),
503 }
504 for s in path.segments.iter() {
505 ps.push(s.name.to_string().into());
506 }
507 ps
508}
509
510// This function produces sequence of text edits into edit
511// to import the target path in the most appropriate scope given
512// the cursor position
513pub fn auto_import_text_edit(
514 // Ideally the position of the cursor, used to
515 position: &SyntaxNode,
516 // The statement to use as anchor (last resort)
517 anchor: &SyntaxNode,
518 // The path to import as a sequence of strings
519 target: &[SmolStr],
520 edit: &mut TextEditBuilder,
521) {
522 let container = position.ancestors().find_map(|n| {
523 if let Some(module) = ast::Module::cast(n) {
524 return module.item_list().map(ast::AstNode::syntax);
525 }
526 ast::SourceFile::cast(n).map(ast::AstNode::syntax)
527 });
528
529 if let Some(container) = container {
530 let action = best_action_for_target(container, anchor, target);
531 make_assist(&action, target, edit);
532 }
533}
534
516pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 535pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
517 let path: &ast::Path = ctx.node_at_offset()?; 536 let path: &ast::Path = ctx.node_at_offset()?;
518 // We don't want to mess with use statements 537 // We don't want to mess with use statements
@@ -520,7 +539,8 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
520 return None; 539 return None;
521 } 540 }
522 541
523 let segments = collect_path_segments(path)?; 542 let hir_path = hir::Path::from_ast(path)?;
543 let segments = collect_hir_path_segments(&hir_path);
524 if segments.len() < 2 { 544 if segments.len() < 2 {
525 return None; 545 return None;
526 } 546 }
@@ -531,7 +551,9 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
531 AssistId("auto_import"), 551 AssistId("auto_import"),
532 format!("import {} in mod {}", fmt_segments(&segments), name.text()), 552 format!("import {} in mod {}", fmt_segments(&segments), name.text()),
533 |edit| { 553 |edit| {
534 apply_auto_import(item_list.syntax(), path, &segments, edit); 554 let mut text_edit = TextEditBuilder::default();
555 apply_auto_import(item_list.syntax(), path, &segments, &mut text_edit);
556 edit.set_edit_builder(text_edit);
535 }, 557 },
536 ); 558 );
537 } 559 }
@@ -541,7 +563,9 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
541 AssistId("auto_import"), 563 AssistId("auto_import"),
542 format!("import {} in the current file", fmt_segments(&segments)), 564 format!("import {} in the current file", fmt_segments(&segments)),
543 |edit| { 565 |edit| {
544 apply_auto_import(current_file.syntax(), path, &segments, edit); 566 let mut text_edit = TextEditBuilder::default();
567 apply_auto_import(current_file.syntax(), path, &segments, &mut text_edit);
568 edit.set_edit_builder(text_edit);
545 }, 569 },
546 ); 570 );
547 } 571 }
@@ -568,6 +592,47 @@ Debug<|>
568 ", 592 ",
569 ); 593 );
570 } 594 }
595 #[test]
596 fn test_auto_import_add_use_no_anchor_with_item_below() {
597 check_assist(
598 auto_import,
599 "
600std::fmt::Debug<|>
601
602fn main() {
603}
604 ",
605 "
606use std::fmt::Debug;
607
608Debug<|>
609
610fn main() {
611}
612 ",
613 );
614 }
615
616 #[test]
617 fn test_auto_import_add_use_no_anchor_with_item_above() {
618 check_assist(
619 auto_import,
620 "
621fn main() {
622}
623
624std::fmt::Debug<|>
625 ",
626 "
627use std::fmt::Debug;
628
629fn main() {
630}
631
632Debug<|>
633 ",
634 );
635 }
571 636
572 #[test] 637 #[test]
573 fn test_auto_import_add_use_no_anchor_2seg() { 638 fn test_auto_import_add_use_no_anchor_2seg() {
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 60b4d5c63..4c330c907 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -99,7 +99,7 @@ mod inline_local_variable;
99mod replace_if_let_with_match; 99mod replace_if_let_with_match;
100mod split_import; 100mod split_import;
101mod remove_dbg; 101mod remove_dbg;
102mod auto_import; 102pub mod auto_import;
103mod add_missing_impl_members; 103mod add_missing_impl_members;
104 104
105fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { 105fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
index 283f37845..9a999e66c 100644
--- a/crates/ra_hir/src/name.rs
+++ b/crates/ra_hir/src/name.rs
@@ -46,6 +46,19 @@ impl Name {
46 Name::new(idx.to_string().into()) 46 Name::new(idx.to_string().into())
47 } 47 }
48 48
49 // There's should be no way to extract a string out of `Name`: `Name` in the
50 // future, `Name` will include hygiene information, and you can't encode
51 // hygiene into a String.
52 //
53 // If you need to compare something with `Name`, compare `Name`s directly.
54 //
55 // If you need to render `Name` for the user, use the `Display` impl, but be
56 // aware that it strips hygiene info.
57 #[deprecated(note = "use to_string instead")]
58 pub fn as_smolstr(&self) -> &SmolStr {
59 &self.text
60 }
61
49 pub(crate) fn as_known_name(&self) -> Option<KnownName> { 62 pub(crate) fn as_known_name(&self) -> Option<KnownName> {
50 let name = match self.text.as_str() { 63 let name = match self.text.as_str() {
51 "isize" => KnownName::Isize, 64 "isize" => KnownName::Isize,
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index f1bb13bc6..2959e3eca 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -21,7 +21,7 @@ use crate::{
21 AsName, Module, HirFileId, Crate, Trait, Resolver, Ty, 21 AsName, Module, HirFileId, Crate, Trait, Resolver, Ty,
22 expr::{BodySourceMap, scope::{ScopeId, ExprScopes}}, 22 expr::{BodySourceMap, scope::{ScopeId, ExprScopes}},
23 ids::LocationCtx, 23 ids::LocationCtx,
24 expr, AstId 24 expr, AstId,
25}; 25};
26 26
27/// Locates the module by `FileId`. Picks topmost module in the file. 27/// Locates the module by `FileId`. Picks topmost module in the file.
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs
index fd256fc3b..2473e58b4 100644
--- a/crates/ra_ide_api/src/completion/complete_scope.rs
+++ b/crates/ra_ide_api/src/completion/complete_scope.rs
@@ -1,12 +1,122 @@
1use crate::completion::{Completions, CompletionContext}; 1use rustc_hash::FxHashMap;
2use ra_text_edit::TextEditBuilder;
3use ra_syntax::{SmolStr, ast, AstNode};
4use ra_assists::auto_import;
5
6use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext};
2 7
3pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { 8pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
4 if !ctx.is_trivial_path { 9 if ctx.is_trivial_path {
5 return; 10 let names = ctx.analyzer.all_names(ctx.db);
11 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res));
12
13 // auto-import
14 // We fetch ident from the original file, because we need to pre-filter auto-imports
15 if ast::NameRef::cast(ctx.token.parent()).is_some() {
16 let import_resolver = ImportResolver::new();
17 let import_names = import_resolver.all_names(ctx.token.text());
18 import_names.into_iter().for_each(|(name, path)| {
19 let edit = {
20 let mut builder = TextEditBuilder::default();
21 builder.replace(ctx.source_range(), name.to_string());
22 auto_import::auto_import_text_edit(
23 ctx.token.parent(),
24 ctx.token.parent(),
25 &path,
26 &mut builder,
27 );
28 builder.finish()
29 };
30
31 // Hack: copied this check form conv.rs beacause auto import can produce edits
32 // that invalidate assert in conv_with.
33 if edit
34 .as_atoms()
35 .iter()
36 .filter(|atom| !ctx.source_range().is_subrange(&atom.delete))
37 .all(|atom| ctx.source_range().intersection(&atom.delete).is_none())
38 {
39 CompletionItem::new(
40 CompletionKind::Reference,
41 ctx.source_range(),
42 build_import_label(&name, &path),
43 )
44 .text_edit(edit)
45 .add_to(acc);
46 }
47 });
48 }
6 } 49 }
7 let names = ctx.analyzer.all_names(ctx.db); 50}
8 51
9 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); 52fn build_import_label(name: &str, path: &Vec<SmolStr>) -> String {
53 let mut buf = String::with_capacity(64);
54 buf.push_str(name);
55 buf.push_str(" (");
56 fmt_import_path(path, &mut buf);
57 buf.push_str(")");
58 buf
59}
60
61fn fmt_import_path(path: &Vec<SmolStr>, buf: &mut String) {
62 let mut segments = path.iter();
63 if let Some(s) = segments.next() {
64 buf.push_str(&s);
65 }
66 for s in segments {
67 buf.push_str("::");
68 buf.push_str(&s);
69 }
70}
71
72#[derive(Debug, Clone, Default)]
73pub(crate) struct ImportResolver {
74 // todo: use fst crate or something like that
75 dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
76}
77
78impl ImportResolver {
79 pub(crate) fn new() -> Self {
80 let dummy_names = vec![
81 (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]),
82 (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]),
83 (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]),
84 (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]),
85 (
86 SmolStr::new("Debug"),
87 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")],
88 ),
89 (
90 SmolStr::new("Display"),
91 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")],
92 ),
93 (
94 SmolStr::new("Hash"),
95 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")],
96 ),
97 (
98 SmolStr::new("Hasher"),
99 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")],
100 ),
101 (
102 SmolStr::new("Iterator"),
103 vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")],
104 ),
105 ];
106
107 ImportResolver { dummy_names }
108 }
109
110 // Returns a map of importable items filtered by name.
111 // The map associates item name with its full path.
112 // todo: should return Resolutions
113 pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, Vec<SmolStr>> {
114 if name.len() > 1 {
115 self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
116 } else {
117 FxHashMap::default()
118 }
119 }
10} 120}
11 121
12#[cfg(test)] 122#[cfg(test)]
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs
index 359f2cffa..a8c8cc7b0 100644
--- a/crates/ra_ide_api/src/completion/completion_context.rs
+++ b/crates/ra_ide_api/src/completion/completion_context.rs
@@ -27,7 +27,7 @@ pub(crate) struct CompletionContext<'a> {
27 pub(super) is_pat_binding: bool, 27 pub(super) is_pat_binding: bool,
28 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. 28 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
29 pub(super) is_trivial_path: bool, 29 pub(super) is_trivial_path: bool,
30 /// If not a trivial, path, the prefix (qualifier). 30 /// If not a trivial path, the prefix (qualifier).
31 pub(super) path_prefix: Option<hir::Path>, 31 pub(super) path_prefix: Option<hir::Path>,
32 pub(super) after_if: bool, 32 pub(super) after_if: bool,
33 /// `true` if we are a statement or a last expr in the block. 33 /// `true` if we are a statement or a last expr in the block.
@@ -151,6 +151,7 @@ impl<'a> CompletionContext<'a> {
151 Some(it) => it, 151 Some(it) => it,
152 None => return, 152 None => return,
153 }; 153 };
154
154 if let Some(segment) = ast::PathSegment::cast(parent) { 155 if let Some(segment) = ast::PathSegment::cast(parent) {
155 let path = segment.parent_path(); 156 let path = segment.parent_path();
156 self.is_call = path 157 self.is_call = path
@@ -167,6 +168,7 @@ impl<'a> CompletionContext<'a> {
167 return; 168 return;
168 } 169 }
169 } 170 }
171
170 if path.qualifier().is_none() { 172 if path.qualifier().is_none() {
171 self.is_trivial_path = true; 173 self.is_trivial_path = true;
172 174