diff options
Diffstat (limited to 'crates/ide_db/src/helpers')
-rw-r--r-- | crates/ide_db/src/helpers/insert_use.rs | 279 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use/tests.rs | 42 |
2 files changed, 142 insertions, 179 deletions
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index be3a22725..a43504a27 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -4,13 +4,9 @@ use std::{cmp::Ordering, iter::successors}; | |||
4 | use hir::Semantics; | 4 | use hir::Semantics; |
5 | use itertools::{EitherOrBoth, Itertools}; | 5 | use itertools::{EitherOrBoth, Itertools}; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | algo::SyntaxRewriter, | 7 | algo, |
8 | ast::{ | 8 | ast::{self, edit::AstNodeEdit, make, AstNode, AttrsOwner, PathSegmentKind, VisibilityOwner}, |
9 | self, | 9 | ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken, |
10 | edit::{AstNodeEdit, IndentLevel}, | ||
11 | make, AstNode, AttrsOwner, PathSegmentKind, VisibilityOwner, | ||
12 | }, | ||
13 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, | ||
14 | }; | 10 | }; |
15 | 11 | ||
16 | use crate::RootDatabase; | 12 | use crate::RootDatabase; |
@@ -42,13 +38,18 @@ impl ImportScope { | |||
42 | } | 38 | } |
43 | 39 | ||
44 | /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. | 40 | /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. |
45 | pub fn find_insert_use_container( | 41 | pub fn find_insert_use_container_with_macros( |
46 | position: &SyntaxNode, | 42 | position: &SyntaxNode, |
47 | sema: &Semantics<'_, RootDatabase>, | 43 | sema: &Semantics<'_, RootDatabase>, |
48 | ) -> Option<Self> { | 44 | ) -> Option<Self> { |
49 | sema.ancestors_with_macros(position.clone()).find_map(Self::from) | 45 | sema.ancestors_with_macros(position.clone()).find_map(Self::from) |
50 | } | 46 | } |
51 | 47 | ||
48 | /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. | ||
49 | pub fn find_insert_use_container(position: &SyntaxNode) -> Option<Self> { | ||
50 | std::iter::successors(Some(position.clone()), SyntaxNode::parent).find_map(Self::from) | ||
51 | } | ||
52 | |||
52 | pub fn as_syntax_node(&self) -> &SyntaxNode { | 53 | pub fn as_syntax_node(&self) -> &SyntaxNode { |
53 | match self { | 54 | match self { |
54 | ImportScope::File(file) => file.syntax(), | 55 | ImportScope::File(file) => file.syntax(), |
@@ -56,127 +57,32 @@ impl ImportScope { | |||
56 | } | 57 | } |
57 | } | 58 | } |
58 | 59 | ||
59 | fn indent_level(&self) -> IndentLevel { | 60 | pub fn clone_for_update(&self) -> Self { |
60 | match self { | ||
61 | ImportScope::File(file) => file.indent_level(), | ||
62 | ImportScope::Module(item_list) => item_list.indent_level() + 1, | ||
63 | } | ||
64 | } | ||
65 | |||
66 | fn first_insert_pos(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { | ||
67 | match self { | 61 | match self { |
68 | ImportScope::File(_) => (InsertPosition::First, AddBlankLine::AfterTwice), | 62 | ImportScope::File(file) => ImportScope::File(file.clone_for_update()), |
69 | // don't insert the imports before the item list's opening curly brace | 63 | ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()), |
70 | ImportScope::Module(item_list) => item_list | ||
71 | .l_curly_token() | ||
72 | .map(|b| (InsertPosition::After(b.into()), AddBlankLine::Around)) | ||
73 | .unwrap_or((InsertPosition::First, AddBlankLine::AfterTwice)), | ||
74 | } | 64 | } |
75 | } | 65 | } |
76 | |||
77 | fn insert_pos_after_last_inner_element(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { | ||
78 | self.as_syntax_node() | ||
79 | .children_with_tokens() | ||
80 | .filter(|child| match child { | ||
81 | NodeOrToken::Node(node) => is_inner_attribute(node.clone()), | ||
82 | NodeOrToken::Token(token) => is_inner_comment(token.clone()), | ||
83 | }) | ||
84 | .last() | ||
85 | .map(|last_inner_element| { | ||
86 | (InsertPosition::After(last_inner_element), AddBlankLine::BeforeTwice) | ||
87 | }) | ||
88 | .unwrap_or_else(|| self.first_insert_pos()) | ||
89 | } | ||
90 | } | ||
91 | |||
92 | fn is_inner_attribute(node: SyntaxNode) -> bool { | ||
93 | ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner) | ||
94 | } | ||
95 | |||
96 | fn is_inner_comment(token: SyntaxToken) -> bool { | ||
97 | ast::Comment::cast(token).and_then(|comment| comment.kind().doc) | ||
98 | == Some(ast::CommentPlacement::Inner) | ||
99 | } | 66 | } |
100 | 67 | ||
101 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. | 68 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. |
102 | pub fn insert_use<'a>( | 69 | pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig) { |
103 | scope: &ImportScope, | ||
104 | path: ast::Path, | ||
105 | cfg: InsertUseConfig, | ||
106 | ) -> SyntaxRewriter<'a> { | ||
107 | let _p = profile::span("insert_use"); | 70 | let _p = profile::span("insert_use"); |
108 | let mut rewriter = SyntaxRewriter::default(); | 71 | let use_item = |
109 | let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false)); | 72 | make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update(); |
110 | // merge into existing imports if possible | 73 | // merge into existing imports if possible |
111 | if let Some(mb) = cfg.merge { | 74 | if let Some(mb) = cfg.merge { |
112 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { | 75 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { |
113 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { | 76 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { |
114 | rewriter.replace(existing_use.syntax(), merged.syntax()); | 77 | ted::replace(existing_use.syntax(), merged.syntax()); |
115 | return rewriter; | 78 | return; |
116 | } | 79 | } |
117 | } | 80 | } |
118 | } | 81 | } |
119 | 82 | ||
120 | // either we weren't allowed to merge or there is no import that fits the merge conditions | 83 | // either we weren't allowed to merge or there is no import that fits the merge conditions |
121 | // so look for the place we have to insert to | 84 | // so look for the place we have to insert to |
122 | let (insert_position, add_blank) = find_insert_position(scope, path, cfg.group); | 85 | insert_use_(scope, path, cfg.group, use_item); |
123 | |||
124 | let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { | ||
125 | Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()) | ||
126 | } else { | ||
127 | None | ||
128 | }; | ||
129 | |||
130 | let to_insert: Vec<SyntaxElement> = { | ||
131 | let mut buf = Vec::new(); | ||
132 | |||
133 | match add_blank { | ||
134 | AddBlankLine::Before | AddBlankLine::Around => { | ||
135 | buf.push(make::tokens::single_newline().into()) | ||
136 | } | ||
137 | AddBlankLine::BeforeTwice => buf.push(make::tokens::blank_line().into()), | ||
138 | _ => (), | ||
139 | } | ||
140 | |||
141 | if add_blank.has_before() { | ||
142 | if let Some(indent) = indent.clone() { | ||
143 | cov_mark::hit!(insert_use_indent_before); | ||
144 | buf.push(indent); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | buf.push(use_item.syntax().clone().into()); | ||
149 | |||
150 | match add_blank { | ||
151 | AddBlankLine::After | AddBlankLine::Around => { | ||
152 | buf.push(make::tokens::single_newline().into()) | ||
153 | } | ||
154 | AddBlankLine::AfterTwice => buf.push(make::tokens::blank_line().into()), | ||
155 | _ => (), | ||
156 | } | ||
157 | |||
158 | // only add indentation *after* our stuff if there's another node directly after it | ||
159 | if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) { | ||
160 | if let Some(indent) = indent { | ||
161 | cov_mark::hit!(insert_use_indent_after); | ||
162 | buf.push(indent); | ||
163 | } | ||
164 | } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) { | ||
165 | cov_mark::hit!(insert_use_no_indent_after); | ||
166 | } | ||
167 | |||
168 | buf | ||
169 | }; | ||
170 | |||
171 | match insert_position { | ||
172 | InsertPosition::First => { | ||
173 | rewriter.insert_many_as_first_children(scope.as_syntax_node(), to_insert) | ||
174 | } | ||
175 | InsertPosition::Last => return rewriter, // actually unreachable | ||
176 | InsertPosition::Before(anchor) => rewriter.insert_many_before(&anchor, to_insert), | ||
177 | InsertPosition::After(anchor) => rewriter.insert_many_after(&anchor, to_insert), | ||
178 | } | ||
179 | rewriter | ||
180 | } | 86 | } |
181 | 87 | ||
182 | fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool { | 88 | fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool { |
@@ -235,7 +141,7 @@ pub fn try_merge_trees( | |||
235 | } else { | 141 | } else { |
236 | (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) | 142 | (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) |
237 | }; | 143 | }; |
238 | recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update()) | 144 | recursive_merge(&lhs, &rhs, merge) |
239 | } | 145 | } |
240 | 146 | ||
241 | /// Recursively "zips" together lhs and rhs. | 147 | /// Recursively "zips" together lhs and rhs. |
@@ -334,7 +240,12 @@ fn recursive_merge( | |||
334 | } | 240 | } |
335 | } | 241 | } |
336 | } | 242 | } |
337 | Some(lhs.with_use_tree_list(make::use_tree_list(use_trees))) | 243 | |
244 | Some(if let Some(old) = lhs.use_tree_list() { | ||
245 | lhs.replace_descendant(old, make::use_tree_list(use_trees)).clone_for_update() | ||
246 | } else { | ||
247 | lhs.clone() | ||
248 | }) | ||
338 | } | 249 | } |
339 | 250 | ||
340 | /// Traverses both paths until they differ, returning the common prefix of both. | 251 | /// Traverses both paths until they differ, returning the common prefix of both. |
@@ -520,32 +431,15 @@ impl ImportGroup { | |||
520 | } | 431 | } |
521 | } | 432 | } |
522 | 433 | ||
523 | #[derive(PartialEq, Eq)] | 434 | fn insert_use_( |
524 | enum AddBlankLine { | ||
525 | Before, | ||
526 | BeforeTwice, | ||
527 | Around, | ||
528 | After, | ||
529 | AfterTwice, | ||
530 | } | ||
531 | |||
532 | impl AddBlankLine { | ||
533 | fn has_before(&self) -> bool { | ||
534 | matches!(self, AddBlankLine::Before | AddBlankLine::BeforeTwice | AddBlankLine::Around) | ||
535 | } | ||
536 | fn has_after(&self) -> bool { | ||
537 | matches!(self, AddBlankLine::After | AddBlankLine::AfterTwice | AddBlankLine::Around) | ||
538 | } | ||
539 | } | ||
540 | |||
541 | fn find_insert_position( | ||
542 | scope: &ImportScope, | 435 | scope: &ImportScope, |
543 | insert_path: ast::Path, | 436 | insert_path: ast::Path, |
544 | group_imports: bool, | 437 | group_imports: bool, |
545 | ) -> (InsertPosition<SyntaxElement>, AddBlankLine) { | 438 | use_item: ast::Use, |
439 | ) { | ||
440 | let scope_syntax = scope.as_syntax_node(); | ||
546 | let group = ImportGroup::new(&insert_path); | 441 | let group = ImportGroup::new(&insert_path); |
547 | let path_node_iter = scope | 442 | let path_node_iter = scope_syntax |
548 | .as_syntax_node() | ||
549 | .children() | 443 | .children() |
550 | .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node))) | 444 | .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node))) |
551 | .flat_map(|(use_, node)| { | 445 | .flat_map(|(use_, node)| { |
@@ -557,9 +451,14 @@ fn find_insert_position( | |||
557 | 451 | ||
558 | if !group_imports { | 452 | if !group_imports { |
559 | if let Some((_, _, node)) = path_node_iter.last() { | 453 | if let Some((_, _, node)) = path_node_iter.last() { |
560 | return (InsertPosition::After(node.into()), AddBlankLine::Before); | 454 | cov_mark::hit!(insert_no_grouping_last); |
455 | ted::insert(ted::Position::after(node), use_item.syntax()); | ||
456 | } else { | ||
457 | cov_mark::hit!(insert_no_grouping_last2); | ||
458 | ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line()); | ||
459 | ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()); | ||
561 | } | 460 | } |
562 | return (InsertPosition::First, AddBlankLine::AfterTwice); | 461 | return; |
563 | } | 462 | } |
564 | 463 | ||
565 | // Iterator that discards anything thats not in the required grouping | 464 | // Iterator that discards anything thats not in the required grouping |
@@ -572,43 +471,91 @@ fn find_insert_position( | |||
572 | // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place | 471 | // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place |
573 | let mut last = None; | 472 | let mut last = None; |
574 | // find the element that would come directly after our new import | 473 | // find the element that would come directly after our new import |
575 | let post_insert = group_iter.inspect(|(.., node)| last = Some(node.clone())).find( | 474 | let post_insert: Option<(_, _, SyntaxNode)> = group_iter |
576 | |&(ref path, has_tl, _)| { | 475 | .inspect(|(.., node)| last = Some(node.clone())) |
476 | .find(|&(ref path, has_tl, _)| { | ||
577 | use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater | 477 | use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater |
578 | }, | 478 | }); |
579 | ); | ||
580 | 479 | ||
581 | match post_insert { | 480 | if let Some((.., node)) = post_insert { |
481 | cov_mark::hit!(insert_group); | ||
582 | // insert our import before that element | 482 | // insert our import before that element |
583 | Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), | 483 | return ted::insert(ted::Position::before(node), use_item.syntax()); |
484 | } | ||
485 | if let Some(node) = last { | ||
486 | cov_mark::hit!(insert_group_last); | ||
584 | // there is no element after our new import, so append it to the end of the group | 487 | // there is no element after our new import, so append it to the end of the group |
585 | None => match last { | 488 | return ted::insert(ted::Position::after(node), use_item.syntax()); |
586 | Some(node) => (InsertPosition::After(node.into()), AddBlankLine::Before), | 489 | } |
587 | // the group we were looking for actually doesnt exist, so insert | 490 | |
491 | // the group we were looking for actually doesn't exist, so insert | ||
492 | |||
493 | let mut last = None; | ||
494 | // find the group that comes after where we want to insert | ||
495 | let post_group = path_node_iter | ||
496 | .inspect(|(.., node)| last = Some(node.clone())) | ||
497 | .find(|(p, ..)| ImportGroup::new(p) > group); | ||
498 | if let Some((.., node)) = post_group { | ||
499 | cov_mark::hit!(insert_group_new_group); | ||
500 | ted::insert(ted::Position::before(&node), use_item.syntax()); | ||
501 | if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) { | ||
502 | ted::insert(ted::Position::after(node), make::tokens::single_newline()); | ||
503 | } | ||
504 | return; | ||
505 | } | ||
506 | // there is no such group, so append after the last one | ||
507 | if let Some(node) = last { | ||
508 | cov_mark::hit!(insert_group_no_group); | ||
509 | ted::insert(ted::Position::after(&node), use_item.syntax()); | ||
510 | ted::insert(ted::Position::after(node), make::tokens::single_newline()); | ||
511 | return; | ||
512 | } | ||
513 | // there are no imports in this file at all | ||
514 | if let Some(last_inner_element) = scope_syntax | ||
515 | .children_with_tokens() | ||
516 | .filter(|child| match child { | ||
517 | NodeOrToken::Node(node) => is_inner_attribute(node.clone()), | ||
518 | NodeOrToken::Token(token) => is_inner_comment(token.clone()), | ||
519 | }) | ||
520 | .last() | ||
521 | { | ||
522 | cov_mark::hit!(insert_group_empty_inner_attr); | ||
523 | ted::insert(ted::Position::after(&last_inner_element), use_item.syntax()); | ||
524 | ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline()); | ||
525 | return; | ||
526 | } | ||
527 | match scope { | ||
528 | ImportScope::File(_) => { | ||
529 | cov_mark::hit!(insert_group_empty_file); | ||
530 | ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line()); | ||
531 | ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()) | ||
532 | } | ||
533 | // don't insert the imports before the item list's opening curly brace | ||
534 | ImportScope::Module(item_list) => match item_list.l_curly_token() { | ||
535 | Some(b) => { | ||
536 | cov_mark::hit!(insert_group_empty_module); | ||
537 | ted::insert(ted::Position::after(&b), make::tokens::single_newline()); | ||
538 | ted::insert(ted::Position::after(&b), use_item.syntax()); | ||
539 | } | ||
588 | None => { | 540 | None => { |
589 | // similar concept here to the `last` from above | 541 | // This should never happens, broken module syntax node |
590 | let mut last = None; | 542 | ted::insert( |
591 | // find the group that comes after where we want to insert | 543 | ted::Position::first_child_of(scope_syntax), |
592 | let post_group = path_node_iter | 544 | make::tokens::blank_line(), |
593 | .inspect(|(.., node)| last = Some(node.clone())) | 545 | ); |
594 | .find(|(p, ..)| ImportGroup::new(p) > group); | 546 | ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()); |
595 | match post_group { | ||
596 | Some((.., node)) => { | ||
597 | (InsertPosition::Before(node.into()), AddBlankLine::AfterTwice) | ||
598 | } | ||
599 | // there is no such group, so append after the last one | ||
600 | None => match last { | ||
601 | Some(node) => { | ||
602 | (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) | ||
603 | } | ||
604 | // there are no imports in this file at all | ||
605 | None => scope.insert_pos_after_last_inner_element(), | ||
606 | }, | ||
607 | } | ||
608 | } | 547 | } |
609 | }, | 548 | }, |
610 | } | 549 | } |
611 | } | 550 | } |
612 | 551 | ||
552 | fn is_inner_attribute(node: SyntaxNode) -> bool { | ||
553 | ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner) | ||
554 | } | ||
555 | |||
556 | fn is_inner_comment(token: SyntaxToken) -> bool { | ||
557 | ast::Comment::cast(token).and_then(|comment| comment.kind().doc) | ||
558 | == Some(ast::CommentPlacement::Inner) | ||
559 | } | ||
613 | #[cfg(test)] | 560 | #[cfg(test)] |
614 | mod tests; | 561 | mod tests; |
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 3d151e629..048c213e2 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs | |||
@@ -5,6 +5,7 @@ use test_utils::assert_eq_text; | |||
5 | 5 | ||
6 | #[test] | 6 | #[test] |
7 | fn insert_not_group() { | 7 | fn insert_not_group() { |
8 | cov_mark::check!(insert_no_grouping_last); | ||
8 | check( | 9 | check( |
9 | "use external_crate2::bar::A", | 10 | "use external_crate2::bar::A", |
10 | r" | 11 | r" |
@@ -27,6 +28,21 @@ use external_crate2::bar::A;", | |||
27 | } | 28 | } |
28 | 29 | ||
29 | #[test] | 30 | #[test] |
31 | fn insert_not_group_empty() { | ||
32 | cov_mark::check!(insert_no_grouping_last2); | ||
33 | check( | ||
34 | "use external_crate2::bar::A", | ||
35 | r"", | ||
36 | r"use external_crate2::bar::A; | ||
37 | |||
38 | ", | ||
39 | None, | ||
40 | false, | ||
41 | false, | ||
42 | ); | ||
43 | } | ||
44 | |||
45 | #[test] | ||
30 | fn insert_existing() { | 46 | fn insert_existing() { |
31 | check_full("std::fs", "use std::fs;", "use std::fs;") | 47 | check_full("std::fs", "use std::fs;", "use std::fs;") |
32 | } | 48 | } |
@@ -51,21 +67,21 @@ use std::bar::G;", | |||
51 | 67 | ||
52 | #[test] | 68 | #[test] |
53 | fn insert_start_indent() { | 69 | fn insert_start_indent() { |
54 | cov_mark::check!(insert_use_indent_after); | ||
55 | check_none( | 70 | check_none( |
56 | "std::bar::AA", | 71 | "std::bar::AA", |
57 | r" | 72 | r" |
58 | use std::bar::B; | 73 | use std::bar::B; |
59 | use std::bar::D;", | 74 | use std::bar::C;", |
60 | r" | 75 | r" |
61 | use std::bar::AA; | 76 | use std::bar::AA; |
62 | use std::bar::B; | 77 | use std::bar::B; |
63 | use std::bar::D;", | 78 | use std::bar::C;", |
64 | ) | 79 | ); |
65 | } | 80 | } |
66 | 81 | ||
67 | #[test] | 82 | #[test] |
68 | fn insert_middle() { | 83 | fn insert_middle() { |
84 | cov_mark::check!(insert_group); | ||
69 | check_none( | 85 | check_none( |
70 | "std::bar::EE", | 86 | "std::bar::EE", |
71 | r" | 87 | r" |
@@ -102,6 +118,7 @@ fn insert_middle_indent() { | |||
102 | 118 | ||
103 | #[test] | 119 | #[test] |
104 | fn insert_end() { | 120 | fn insert_end() { |
121 | cov_mark::check!(insert_group_last); | ||
105 | check_none( | 122 | check_none( |
106 | "std::bar::ZZ", | 123 | "std::bar::ZZ", |
107 | r" | 124 | r" |
@@ -120,7 +137,6 @@ use std::bar::ZZ;", | |||
120 | 137 | ||
121 | #[test] | 138 | #[test] |
122 | fn insert_end_indent() { | 139 | fn insert_end_indent() { |
123 | cov_mark::check!(insert_use_indent_before); | ||
124 | check_none( | 140 | check_none( |
125 | "std::bar::ZZ", | 141 | "std::bar::ZZ", |
126 | r" | 142 | r" |
@@ -201,6 +217,7 @@ fn insert_first_matching_group() { | |||
201 | 217 | ||
202 | #[test] | 218 | #[test] |
203 | fn insert_missing_group_std() { | 219 | fn insert_missing_group_std() { |
220 | cov_mark::check!(insert_group_new_group); | ||
204 | check_none( | 221 | check_none( |
205 | "std::fmt", | 222 | "std::fmt", |
206 | r" | 223 | r" |
@@ -216,6 +233,7 @@ fn insert_missing_group_std() { | |||
216 | 233 | ||
217 | #[test] | 234 | #[test] |
218 | fn insert_missing_group_self() { | 235 | fn insert_missing_group_self() { |
236 | cov_mark::check!(insert_group_no_group); | ||
219 | check_none( | 237 | check_none( |
220 | "self::fmt", | 238 | "self::fmt", |
221 | r" | 239 | r" |
@@ -242,6 +260,7 @@ fn main() {}", | |||
242 | 260 | ||
243 | #[test] | 261 | #[test] |
244 | fn insert_empty_file() { | 262 | fn insert_empty_file() { |
263 | cov_mark::check!(insert_group_empty_file); | ||
245 | // empty files will get two trailing newlines | 264 | // empty files will get two trailing newlines |
246 | // this is due to the test case insert_no_imports above | 265 | // this is due to the test case insert_no_imports above |
247 | check_full( | 266 | check_full( |
@@ -255,7 +274,7 @@ fn insert_empty_file() { | |||
255 | 274 | ||
256 | #[test] | 275 | #[test] |
257 | fn insert_empty_module() { | 276 | fn insert_empty_module() { |
258 | cov_mark::check!(insert_use_no_indent_after); | 277 | cov_mark::check!(insert_group_empty_module); |
259 | check( | 278 | check( |
260 | "foo::bar", | 279 | "foo::bar", |
261 | "mod x {}", | 280 | "mod x {}", |
@@ -270,6 +289,7 @@ fn insert_empty_module() { | |||
270 | 289 | ||
271 | #[test] | 290 | #[test] |
272 | fn insert_after_inner_attr() { | 291 | fn insert_after_inner_attr() { |
292 | cov_mark::check!(insert_group_empty_inner_attr); | ||
273 | check_full( | 293 | check_full( |
274 | "foo::bar", | 294 | "foo::bar", |
275 | r"#![allow(unused_imports)]", | 295 | r"#![allow(unused_imports)]", |
@@ -615,7 +635,7 @@ fn check( | |||
615 | if module { | 635 | if module { |
616 | syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone(); | 636 | syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone(); |
617 | } | 637 | } |
618 | let file = super::ImportScope::from(syntax).unwrap(); | 638 | let file = super::ImportScope::from(syntax.clone_for_update()).unwrap(); |
619 | let path = ast::SourceFile::parse(&format!("use {};", path)) | 639 | let path = ast::SourceFile::parse(&format!("use {};", path)) |
620 | .tree() | 640 | .tree() |
621 | .syntax() | 641 | .syntax() |
@@ -623,12 +643,8 @@ fn check( | |||
623 | .find_map(ast::Path::cast) | 643 | .find_map(ast::Path::cast) |
624 | .unwrap(); | 644 | .unwrap(); |
625 | 645 | ||
626 | let rewriter = insert_use( | 646 | insert_use(&file, path, InsertUseConfig { merge: mb, prefix_kind: PrefixKind::Plain, group }); |
627 | &file, | 647 | let result = file.as_syntax_node().to_string(); |
628 | path, | ||
629 | InsertUseConfig { merge: mb, prefix_kind: PrefixKind::Plain, group }, | ||
630 | ); | ||
631 | let result = rewriter.rewrite(file.as_syntax_node()).to_string(); | ||
632 | assert_eq_text!(ra_fixture_after, &result); | 648 | assert_eq_text!(ra_fixture_after, &result); |
633 | } | 649 | } |
634 | 650 | ||