aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/add_import.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/add_import.rs')
-rw-r--r--crates/ra_assists/src/handlers/add_import.rs967
1 files changed, 967 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/add_import.rs b/crates/ra_assists/src/handlers/add_import.rs
new file mode 100644
index 000000000..f03dddac8
--- /dev/null
+++ b/crates/ra_assists/src/handlers/add_import.rs
@@ -0,0 +1,967 @@
1use hir::{self, ModPath};
2use ra_syntax::{
3 ast::{self, NameOwner},
4 AstNode, Direction, SmolStr,
5 SyntaxKind::{PATH, PATH_SEGMENT},
6 SyntaxNode, TextRange, T,
7};
8use ra_text_edit::TextEditBuilder;
9
10use crate::{
11 assist_ctx::{Assist, AssistCtx},
12 AssistId,
13};
14
15/// This function produces sequence of text edits into edit
16/// to import the target path in the most appropriate scope given
17/// the cursor position
18pub fn auto_import_text_edit(
19 // Ideally the position of the cursor, used to
20 position: &SyntaxNode,
21 // The statement to use as anchor (last resort)
22 anchor: &SyntaxNode,
23 path_to_import: &ModPath,
24 edit: &mut TextEditBuilder,
25) {
26 let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
27 let container = position.ancestors().find_map(|n| {
28 if let Some(module) = ast::Module::cast(n.clone()) {
29 return module.item_list().map(|it| it.syntax().clone());
30 }
31 ast::SourceFile::cast(n).map(|it| it.syntax().clone())
32 });
33
34 if let Some(container) = container {
35 let action = best_action_for_target(container, anchor.clone(), &target);
36 make_assist(&action, &target, edit);
37 }
38}
39
40// Assist: add_import
41//
42// Adds a use statement for a given fully-qualified path.
43//
44// ```
45// fn process(map: std::collections::<|>HashMap<String, String>) {}
46// ```
47// ->
48// ```
49// use std::collections::HashMap;
50//
51// fn process(map: HashMap<String, String>) {}
52// ```
53pub(crate) fn add_import(ctx: AssistCtx) -> Option<Assist> {
54 let path: ast::Path = ctx.find_node_at_offset()?;
55 // We don't want to mess with use statements
56 if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
57 return None;
58 }
59
60 let hir_path = hir::Path::from_ast(path.clone())?;
61 let segments = collect_hir_path_segments(&hir_path)?;
62 if segments.len() < 2 {
63 return None;
64 }
65
66 let module = path.syntax().ancestors().find_map(ast::Module::cast);
67 let position = match module.and_then(|it| it.item_list()) {
68 Some(item_list) => item_list.syntax().clone(),
69 None => {
70 let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?;
71 current_file.syntax().clone()
72 }
73 };
74
75 ctx.add_assist(AssistId("add_import"), format!("Import {}", fmt_segments(&segments)), |edit| {
76 apply_auto_import(&position, &path, &segments, edit.text_edit_builder());
77 })
78}
79
80fn collect_path_segments_raw(
81 segments: &mut Vec<ast::PathSegment>,
82 mut path: ast::Path,
83) -> Option<usize> {
84 let oldlen = segments.len();
85 loop {
86 let mut children = path.syntax().children_with_tokens();
87 let (first, second, third) = (
88 children.next().map(|n| (n.clone(), n.kind())),
89 children.next().map(|n| (n.clone(), n.kind())),
90 children.next().map(|n| (n.clone(), n.kind())),
91 );
92 match (first, second, third) {
93 (Some((subpath, PATH)), Some((_, T![::])), Some((segment, PATH_SEGMENT))) => {
94 path = ast::Path::cast(subpath.as_node()?.clone())?;
95 segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
96 }
97 (Some((segment, PATH_SEGMENT)), _, _) => {
98 segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
99 break;
100 }
101 (_, _, _) => return None,
102 }
103 }
104 // We need to reverse only the new added segments
105 let only_new_segments = segments.split_at_mut(oldlen).1;
106 only_new_segments.reverse();
107 Some(segments.len() - oldlen)
108}
109
110fn fmt_segments(segments: &[SmolStr]) -> String {
111 let mut buf = String::new();
112 fmt_segments_raw(segments, &mut buf);
113 buf
114}
115
116fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
117 let mut iter = segments.iter();
118 if let Some(s) = iter.next() {
119 buf.push_str(s);
120 }
121 for s in iter {
122 buf.push_str("::");
123 buf.push_str(s);
124 }
125}
126
127/// Returns the number of common segments.
128fn compare_path_segments(left: &[SmolStr], right: &[ast::PathSegment]) -> usize {
129 left.iter().zip(right).take_while(|(l, r)| compare_path_segment(l, r)).count()
130}
131
132fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
133 if let Some(kb) = b.kind() {
134 match kb {
135 ast::PathSegmentKind::Name(nameref_b) => a == nameref_b.text(),
136 ast::PathSegmentKind::SelfKw => a == "self",
137 ast::PathSegmentKind::SuperKw => a == "super",
138 ast::PathSegmentKind::CrateKw => a == "crate",
139 ast::PathSegmentKind::Type { .. } => false, // not allowed in imports
140 }
141 } else {
142 false
143 }
144}
145
146fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool {
147 a == b.text()
148}
149
150#[derive(Clone, Debug)]
151enum ImportAction {
152 Nothing,
153 // Add a brand new use statement.
154 AddNewUse {
155 anchor: Option<SyntaxNode>, // anchor node
156 add_after_anchor: bool,
157 },
158
159 // To split an existing use statement creating a nested import.
160 AddNestedImport {
161 // how may segments matched with the target path
162 common_segments: usize,
163 path_to_split: ast::Path,
164 // the first segment of path_to_split we want to add into the new nested list
165 first_segment_to_split: Option<ast::PathSegment>,
166 // Wether to add 'self' in addition to the target path
167 add_self: bool,
168 },
169 // To add the target path to an existing nested import tree list.
170 AddInTreeList {
171 common_segments: usize,
172 // The UseTreeList where to add the target path
173 tree_list: ast::UseTreeList,
174 add_self: bool,
175 },
176}
177
178impl ImportAction {
179 fn add_new_use(anchor: Option<SyntaxNode>, add_after_anchor: bool) -> Self {
180 ImportAction::AddNewUse { anchor, add_after_anchor }
181 }
182
183 fn add_nested_import(
184 common_segments: usize,
185 path_to_split: ast::Path,
186 first_segment_to_split: Option<ast::PathSegment>,
187 add_self: bool,
188 ) -> Self {
189 ImportAction::AddNestedImport {
190 common_segments,
191 path_to_split,
192 first_segment_to_split,
193 add_self,
194 }
195 }
196
197 fn add_in_tree_list(
198 common_segments: usize,
199 tree_list: ast::UseTreeList,
200 add_self: bool,
201 ) -> Self {
202 ImportAction::AddInTreeList { common_segments, tree_list, add_self }
203 }
204
205 fn better(left: ImportAction, right: ImportAction) -> ImportAction {
206 if left.is_better(&right) {
207 left
208 } else {
209 right
210 }
211 }
212
213 fn is_better(&self, other: &ImportAction) -> bool {
214 match (self, other) {
215 (ImportAction::Nothing, _) => true,
216 (ImportAction::AddInTreeList { .. }, ImportAction::Nothing) => false,
217 (
218 ImportAction::AddNestedImport { common_segments: n, .. },
219 ImportAction::AddInTreeList { common_segments: m, .. },
220 )
221 | (
222 ImportAction::AddInTreeList { common_segments: n, .. },
223 ImportAction::AddNestedImport { common_segments: m, .. },
224 )
225 | (
226 ImportAction::AddInTreeList { common_segments: n, .. },
227 ImportAction::AddInTreeList { common_segments: m, .. },
228 )
229 | (
230 ImportAction::AddNestedImport { common_segments: n, .. },
231 ImportAction::AddNestedImport { common_segments: m, .. },
232 ) => n > m,
233 (ImportAction::AddInTreeList { .. }, _) => true,
234 (ImportAction::AddNestedImport { .. }, ImportAction::Nothing) => false,
235 (ImportAction::AddNestedImport { .. }, _) => true,
236 (ImportAction::AddNewUse { .. }, _) => false,
237 }
238 }
239}
240
241// Find out the best ImportAction to import target path against current_use_tree.
242// If current_use_tree has a nested import the function gets called recursively on every UseTree inside a UseTreeList.
243fn walk_use_tree_for_best_action(
244 current_path_segments: &mut Vec<ast::PathSegment>, // buffer containing path segments
245 current_parent_use_tree_list: Option<ast::UseTreeList>, // will be Some value if we are in a nested import
246 current_use_tree: ast::UseTree, // the use tree we are currently examinating
247 target: &[SmolStr], // the path we want to import
248) -> ImportAction {
249 // We save the number of segments in the buffer so we can restore the correct segments
250 // before returning. Recursive call will add segments so we need to delete them.
251 let prev_len = current_path_segments.len();
252
253 let tree_list = current_use_tree.use_tree_list();
254 let alias = current_use_tree.alias();
255
256 let path = match current_use_tree.path() {
257 Some(path) => path,
258 None => {
259 // If the use item don't have a path, it means it's broken (syntax error)
260 return ImportAction::add_new_use(
261 current_use_tree
262 .syntax()
263 .ancestors()
264 .find_map(ast::UseItem::cast)
265 .map(|it| it.syntax().clone()),
266 true,
267 );
268 }
269 };
270
271 // This can happen only if current_use_tree is a direct child of a UseItem
272 if let Some(name) = alias.and_then(|it| it.name()) {
273 if compare_path_segment_with_name(&target[0], &name) {
274 return ImportAction::Nothing;
275 }
276 }
277
278 collect_path_segments_raw(current_path_segments, path.clone());
279
280 // We compare only the new segments added in the line just above.
281 // The first prev_len segments were already compared in 'parent' recursive calls.
282 let left = target.split_at(prev_len).1;
283 let right = current_path_segments.split_at(prev_len).1;
284 let common = compare_path_segments(left, &right);
285 let mut action = match common {
286 0 => ImportAction::add_new_use(
287 // e.g: target is std::fmt and we can have
288 // use foo::bar
289 // We add a brand new use statement
290 current_use_tree
291 .syntax()
292 .ancestors()
293 .find_map(ast::UseItem::cast)
294 .map(|it| it.syntax().clone()),
295 true,
296 ),
297 common if common == left.len() && left.len() == right.len() => {
298 // e.g: target is std::fmt and we can have
299 // 1- use std::fmt;
300 // 2- use std::fmt::{ ... }
301 if let Some(list) = tree_list {
302 // In case 2 we need to add self to the nested list
303 // unless it's already there
304 let has_self = list.use_trees().map(|it| it.path()).any(|p| {
305 p.and_then(|it| it.segment())
306 .and_then(|it| it.kind())
307 .filter(|k| *k == ast::PathSegmentKind::SelfKw)
308 .is_some()
309 });
310
311 if has_self {
312 ImportAction::Nothing
313 } else {
314 ImportAction::add_in_tree_list(current_path_segments.len(), list, true)
315 }
316 } else {
317 // Case 1
318 ImportAction::Nothing
319 }
320 }
321 common if common != left.len() && left.len() == right.len() => {
322 // e.g: target is std::fmt and we have
323 // use std::io;
324 // We need to split.
325 let segments_to_split = current_path_segments.split_at(prev_len + common).1;
326 ImportAction::add_nested_import(
327 prev_len + common,
328 path,
329 Some(segments_to_split[0].clone()),
330 false,
331 )
332 }
333 common if common == right.len() && left.len() > right.len() => {
334 // e.g: target is std::fmt and we can have
335 // 1- use std;
336 // 2- use std::{ ... };
337
338 // fallback action
339 let mut better_action = ImportAction::add_new_use(
340 current_use_tree
341 .syntax()
342 .ancestors()
343 .find_map(ast::UseItem::cast)
344 .map(|it| it.syntax().clone()),
345 true,
346 );
347 if let Some(list) = tree_list {
348 // Case 2, check recursively if the path is already imported in the nested list
349 for u in list.use_trees() {
350 let child_action = walk_use_tree_for_best_action(
351 current_path_segments,
352 Some(list.clone()),
353 u,
354 target,
355 );
356 if child_action.is_better(&better_action) {
357 better_action = child_action;
358 if let ImportAction::Nothing = better_action {
359 return better_action;
360 }
361 }
362 }
363 } else {
364 // Case 1, split adding self
365 better_action = ImportAction::add_nested_import(prev_len + common, path, None, true)
366 }
367 better_action
368 }
369 common if common == left.len() && left.len() < right.len() => {
370 // e.g: target is std::fmt and we can have
371 // use std::fmt::Debug;
372 let segments_to_split = current_path_segments.split_at(prev_len + common).1;
373 ImportAction::add_nested_import(
374 prev_len + common,
375 path,
376 Some(segments_to_split[0].clone()),
377 true,
378 )
379 }
380 common if common < left.len() && common < right.len() => {
381 // e.g: target is std::fmt::nested::Debug
382 // use std::fmt::Display
383 let segments_to_split = current_path_segments.split_at(prev_len + common).1;
384 ImportAction::add_nested_import(
385 prev_len + common,
386 path,
387 Some(segments_to_split[0].clone()),
388 false,
389 )
390 }
391 _ => unreachable!(),
392 };
393
394 // If we are inside a UseTreeList adding a use statement become adding to the existing
395 // tree list.
396 action = match (current_parent_use_tree_list, action.clone()) {
397 (Some(use_tree_list), ImportAction::AddNewUse { .. }) => {
398 ImportAction::add_in_tree_list(prev_len, use_tree_list, false)
399 }
400 (_, _) => action,
401 };
402
403 // We remove the segments added
404 current_path_segments.truncate(prev_len);
405 action
406}
407
408fn best_action_for_target(
409 container: SyntaxNode,
410 anchor: SyntaxNode,
411 target: &[SmolStr],
412) -> ImportAction {
413 let mut storage = Vec::with_capacity(16); // this should be the only allocation
414 let best_action = container
415 .children()
416 .filter_map(ast::UseItem::cast)
417 .filter_map(|it| it.use_tree())
418 .map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target))
419 .fold(None, |best, a| match best {
420 Some(best) => Some(ImportAction::better(best, a)),
421 None => Some(a),
422 });
423
424 match best_action {
425 Some(action) => action,
426 None => {
427 // We have no action and no UseItem was found in container so we find
428 // another item and we use it as anchor.
429 // If there are no items above, we choose the target path itself as anchor.
430 // todo: we should include even whitespace blocks as anchor candidates
431 let anchor = container
432 .children()
433 .find(|n| n.text_range().start() < anchor.text_range().start())
434 .or_else(|| Some(anchor));
435
436 ImportAction::add_new_use(anchor, false)
437 }
438 }
439}
440
441fn make_assist(action: &ImportAction, target: &[SmolStr], edit: &mut TextEditBuilder) {
442 match action {
443 ImportAction::AddNewUse { anchor, add_after_anchor } => {
444 make_assist_add_new_use(anchor, *add_after_anchor, target, edit)
445 }
446 ImportAction::AddInTreeList { common_segments, tree_list, add_self } => {
447 // We know that the fist n segments already exists in the use statement we want
448 // to modify, so we want to add only the last target.len() - n segments.
449 let segments_to_add = target.split_at(*common_segments).1;
450 make_assist_add_in_tree_list(tree_list, segments_to_add, *add_self, edit)
451 }
452 ImportAction::AddNestedImport {
453 common_segments,
454 path_to_split,
455 first_segment_to_split,
456 add_self,
457 } => {
458 let segments_to_add = target.split_at(*common_segments).1;
459 make_assist_add_nested_import(
460 path_to_split,
461 first_segment_to_split,
462 segments_to_add,
463 *add_self,
464 edit,
465 )
466 }
467 _ => {}
468 }
469}
470
471fn make_assist_add_new_use(
472 anchor: &Option<SyntaxNode>,
473 after: bool,
474 target: &[SmolStr],
475 edit: &mut TextEditBuilder,
476) {
477 if let Some(anchor) = anchor {
478 let indent = ra_fmt::leading_indent(anchor);
479 let mut buf = String::new();
480 if after {
481 buf.push_str("\n");
482 if let Some(spaces) = &indent {
483 buf.push_str(spaces);
484 }
485 }
486 buf.push_str("use ");
487 fmt_segments_raw(target, &mut buf);
488 buf.push_str(";");
489 if !after {
490 buf.push_str("\n\n");
491 if let Some(spaces) = &indent {
492 buf.push_str(&spaces);
493 }
494 }
495 let position = if after { anchor.text_range().end() } else { anchor.text_range().start() };
496 edit.insert(position, buf);
497 }
498}
499
500fn make_assist_add_in_tree_list(
501 tree_list: &ast::UseTreeList,
502 target: &[SmolStr],
503 add_self: bool,
504 edit: &mut TextEditBuilder,
505) {
506 let last = tree_list.use_trees().last();
507 if let Some(last) = last {
508 let mut buf = String::new();
509 let comma = last.syntax().siblings(Direction::Next).find(|n| n.kind() == T![,]);
510 let offset = if let Some(comma) = comma {
511 comma.text_range().end()
512 } else {
513 buf.push_str(",");
514 last.syntax().text_range().end()
515 };
516 if add_self {
517 buf.push_str(" self")
518 } else {
519 buf.push_str(" ");
520 }
521 fmt_segments_raw(target, &mut buf);
522 edit.insert(offset, buf);
523 } else {
524 }
525}
526
527fn make_assist_add_nested_import(
528 path: &ast::Path,
529 first_segment_to_split: &Option<ast::PathSegment>,
530 target: &[SmolStr],
531 add_self: bool,
532 edit: &mut TextEditBuilder,
533) {
534 let use_tree = path.syntax().ancestors().find_map(ast::UseTree::cast);
535 if let Some(use_tree) = use_tree {
536 let (start, add_colon_colon) = if let Some(first_segment_to_split) = first_segment_to_split
537 {
538 (first_segment_to_split.syntax().text_range().start(), false)
539 } else {
540 (use_tree.syntax().text_range().end(), true)
541 };
542 let end = use_tree.syntax().text_range().end();
543
544 let mut buf = String::new();
545 if add_colon_colon {
546 buf.push_str("::");
547 }
548 buf.push_str("{");
549 if add_self {
550 buf.push_str("self, ");
551 }
552 fmt_segments_raw(target, &mut buf);
553 if !target.is_empty() {
554 buf.push_str(", ");
555 }
556 edit.insert(start, buf);
557 edit.insert(end, "}".to_string());
558 }
559}
560
561fn apply_auto_import(
562 container: &SyntaxNode,
563 path: &ast::Path,
564 target: &[SmolStr],
565 edit: &mut TextEditBuilder,
566) {
567 let action = best_action_for_target(container.clone(), path.syntax().clone(), target);
568 make_assist(&action, target, edit);
569 if let Some(last) = path.segment() {
570 // Here we are assuming the assist will provide a correct use statement
571 // so we can delete the path qualifier
572 edit.delete(TextRange::from_to(
573 path.syntax().text_range().start(),
574 last.syntax().text_range().start(),
575 ));
576 }
577}
578
579fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
580 let mut ps = Vec::<SmolStr>::with_capacity(10);
581 match path.kind() {
582 hir::PathKind::Abs => ps.push("".into()),
583 hir::PathKind::Crate => ps.push("crate".into()),
584 hir::PathKind::Plain => {}
585 hir::PathKind::Super(0) => ps.push("self".into()),
586 hir::PathKind::Super(lvl) => {
587 let mut chain = "super".to_string();
588 for _ in 0..*lvl {
589 chain += "::super";
590 }
591 ps.push(chain.into());
592 }
593 hir::PathKind::DollarCrate(_) => return None,
594 }
595 ps.extend(path.segments().iter().map(|it| it.name.to_string().into()));
596 Some(ps)
597}
598
599#[cfg(test)]
600mod tests {
601 use crate::helpers::{check_assist, check_assist_not_applicable};
602
603 use super::*;
604
605 #[test]
606 fn test_auto_import_add_use_no_anchor() {
607 check_assist(
608 add_import,
609 "
610std::fmt::Debug<|>
611 ",
612 "
613use std::fmt::Debug;
614
615Debug<|>
616 ",
617 );
618 }
619 #[test]
620 fn test_auto_import_add_use_no_anchor_with_item_below() {
621 check_assist(
622 add_import,
623 "
624std::fmt::Debug<|>
625
626fn main() {
627}
628 ",
629 "
630use std::fmt::Debug;
631
632Debug<|>
633
634fn main() {
635}
636 ",
637 );
638 }
639
640 #[test]
641 fn test_auto_import_add_use_no_anchor_with_item_above() {
642 check_assist(
643 add_import,
644 "
645fn main() {
646}
647
648std::fmt::Debug<|>
649 ",
650 "
651use std::fmt::Debug;
652
653fn main() {
654}
655
656Debug<|>
657 ",
658 );
659 }
660
661 #[test]
662 fn test_auto_import_add_use_no_anchor_2seg() {
663 check_assist(
664 add_import,
665 "
666std::fmt<|>::Debug
667 ",
668 "
669use std::fmt;
670
671fmt<|>::Debug
672 ",
673 );
674 }
675
676 #[test]
677 fn test_auto_import_add_use() {
678 check_assist(
679 add_import,
680 "
681use stdx;
682
683impl std::fmt::Debug<|> for Foo {
684}
685 ",
686 "
687use stdx;
688use std::fmt::Debug;
689
690impl Debug<|> for Foo {
691}
692 ",
693 );
694 }
695
696 #[test]
697 fn test_auto_import_file_use_other_anchor() {
698 check_assist(
699 add_import,
700 "
701impl std::fmt::Debug<|> for Foo {
702}
703 ",
704 "
705use std::fmt::Debug;
706
707impl Debug<|> for Foo {
708}
709 ",
710 );
711 }
712
713 #[test]
714 fn test_auto_import_add_use_other_anchor_indent() {
715 check_assist(
716 add_import,
717 "
718 impl std::fmt::Debug<|> for Foo {
719 }
720 ",
721 "
722 use std::fmt::Debug;
723
724 impl Debug<|> for Foo {
725 }
726 ",
727 );
728 }
729
730 #[test]
731 fn test_auto_import_split_different() {
732 check_assist(
733 add_import,
734 "
735use std::fmt;
736
737impl std::io<|> for Foo {
738}
739 ",
740 "
741use std::{io, fmt};
742
743impl io<|> for Foo {
744}
745 ",
746 );
747 }
748
749 #[test]
750 fn test_auto_import_split_self_for_use() {
751 check_assist(
752 add_import,
753 "
754use std::fmt;
755
756impl std::fmt::Debug<|> for Foo {
757}
758 ",
759 "
760use std::fmt::{self, Debug, };
761
762impl Debug<|> for Foo {
763}
764 ",
765 );
766 }
767
768 #[test]
769 fn test_auto_import_split_self_for_target() {
770 check_assist(
771 add_import,
772 "
773use std::fmt::Debug;
774
775impl std::fmt<|> for Foo {
776}
777 ",
778 "
779use std::fmt::{self, Debug};
780
781impl fmt<|> for Foo {
782}
783 ",
784 );
785 }
786
787 #[test]
788 fn test_auto_import_add_to_nested_self_nested() {
789 check_assist(
790 add_import,
791 "
792use std::fmt::{Debug, nested::{Display}};
793
794impl std::fmt::nested<|> for Foo {
795}
796",
797 "
798use std::fmt::{Debug, nested::{Display, self}};
799
800impl nested<|> for Foo {
801}
802",
803 );
804 }
805
806 #[test]
807 fn test_auto_import_add_to_nested_self_already_included() {
808 check_assist(
809 add_import,
810 "
811use std::fmt::{Debug, nested::{self, Display}};
812
813impl std::fmt::nested<|> for Foo {
814}
815",
816 "
817use std::fmt::{Debug, nested::{self, Display}};
818
819impl nested<|> for Foo {
820}
821",
822 );
823 }
824
825 #[test]
826 fn test_auto_import_add_to_nested_nested() {
827 check_assist(
828 add_import,
829 "
830use std::fmt::{Debug, nested::{Display}};
831
832impl std::fmt::nested::Debug<|> for Foo {
833}
834",
835 "
836use std::fmt::{Debug, nested::{Display, Debug}};
837
838impl Debug<|> for Foo {
839}
840",
841 );
842 }
843
844 #[test]
845 fn test_auto_import_split_common_target_longer() {
846 check_assist(
847 add_import,
848 "
849use std::fmt::Debug;
850
851impl std::fmt::nested::Display<|> for Foo {
852}
853",
854 "
855use std::fmt::{nested::Display, Debug};
856
857impl Display<|> for Foo {
858}
859",
860 );
861 }
862
863 #[test]
864 fn test_auto_import_split_common_use_longer() {
865 check_assist(
866 add_import,
867 "
868use std::fmt::nested::Debug;
869
870impl std::fmt::Display<|> for Foo {
871}
872",
873 "
874use std::fmt::{Display, nested::Debug};
875
876impl Display<|> for Foo {
877}
878",
879 );
880 }
881
882 #[test]
883 fn test_auto_import_use_nested_import() {
884 check_assist(
885 add_import,
886 "
887use crate::{
888 ty::{Substs, Ty},
889 AssocItem,
890};
891
892fn foo() { crate::ty::lower<|>::trait_env() }
893",
894 "
895use crate::{
896 ty::{Substs, Ty, lower},
897 AssocItem,
898};
899
900fn foo() { lower<|>::trait_env() }
901",
902 );
903 }
904
905 #[test]
906 fn test_auto_import_alias() {
907 check_assist(
908 add_import,
909 "
910use std::fmt as foo;
911
912impl foo::Debug<|> for Foo {
913}
914",
915 "
916use std::fmt as foo;
917
918impl Debug<|> for Foo {
919}
920",
921 );
922 }
923
924 #[test]
925 fn test_auto_import_not_applicable_one_segment() {
926 check_assist_not_applicable(
927 add_import,
928 "
929impl foo<|> for Foo {
930}
931",
932 );
933 }
934
935 #[test]
936 fn test_auto_import_not_applicable_in_use() {
937 check_assist_not_applicable(
938 add_import,
939 "
940use std::fmt<|>;
941",
942 );
943 }
944
945 #[test]
946 fn test_auto_import_add_use_no_anchor_in_mod_mod() {
947 check_assist(
948 add_import,
949 "
950mod foo {
951 mod bar {
952 std::fmt::Debug<|>
953 }
954}
955 ",
956 "
957mod foo {
958 mod bar {
959 use std::fmt::Debug;
960
961 Debug<|>
962 }
963}
964 ",
965 );
966 }
967}