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