aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers')
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs2
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs49
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs40
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs94
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs103
5 files changed, 188 insertions, 100 deletions
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index e5920b6f6..722f207e2 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -151,7 +151,7 @@ fn add_missing_impl_members_inner(
151 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), 151 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)),
152 _ => it, 152 _ => it,
153 }) 153 })
154 .map(|it| edit::strip_attrs_and_docs(&it)); 154 .map(|it| edit::remove_attrs_and_docs(&it));
155 let new_impl_item_list = impl_item_list.append_items(items); 155 let new_impl_item_list = impl_item_list.append_items(items);
156 let cursor_position = { 156 let cursor_position = {
157 let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); 157 let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap();
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index bb280f633..99682e023 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -17,6 +17,7 @@ use crate::{
17 utils::insert_use_statement, 17 utils::insert_use_statement,
18 AssistId, 18 AssistId,
19}; 19};
20use either::Either;
20 21
21// Assist: auto_import 22// Assist: auto_import
22// 23//
@@ -58,6 +59,7 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
58 group.finish() 59 group.finish()
59} 60}
60 61
62#[derive(Debug)]
61struct AutoImportAssets { 63struct AutoImportAssets {
62 import_candidate: ImportCandidate, 64 import_candidate: ImportCandidate,
63 module_with_name_to_import: Module, 65 module_with_name_to_import: Module,
@@ -127,14 +129,14 @@ impl AutoImportAssets {
127 ImportsLocator::new(db) 129 ImportsLocator::new(db)
128 .find_imports(&self.get_search_query()) 130 .find_imports(&self.get_search_query())
129 .into_iter() 131 .into_iter()
130 .filter_map(|module_def| match &self.import_candidate { 132 .filter_map(|candidate| match &self.import_candidate {
131 ImportCandidate::TraitAssocItem(assoc_item_type, _) => { 133 ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
132 let located_assoc_item = match module_def { 134 let located_assoc_item = match candidate {
133 ModuleDef::Function(located_function) => located_function 135 Either::Left(ModuleDef::Function(located_function)) => located_function
134 .as_assoc_item(db) 136 .as_assoc_item(db)
135 .map(|assoc| assoc.container(db)) 137 .map(|assoc| assoc.container(db))
136 .and_then(Self::assoc_to_trait), 138 .and_then(Self::assoc_to_trait),
137 ModuleDef::Const(located_const) => located_const 139 Either::Left(ModuleDef::Const(located_const)) => located_const
138 .as_assoc_item(db) 140 .as_assoc_item(db)
139 .map(|assoc| assoc.container(db)) 141 .map(|assoc| assoc.container(db))
140 .and_then(Self::assoc_to_trait), 142 .and_then(Self::assoc_to_trait),
@@ -153,10 +155,11 @@ impl AutoImportAssets {
153 |_, assoc| Self::assoc_to_trait(assoc.container(db)), 155 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
154 ) 156 )
155 .map(ModuleDef::from) 157 .map(ModuleDef::from)
158 .map(Either::Left)
156 } 159 }
157 ImportCandidate::TraitMethod(function_callee, _) => { 160 ImportCandidate::TraitMethod(function_callee, _) => {
158 let located_assoc_item = 161 let located_assoc_item =
159 if let ModuleDef::Function(located_function) = module_def { 162 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
160 located_function 163 located_function
161 .as_assoc_item(db) 164 .as_assoc_item(db)
162 .map(|assoc| assoc.container(db)) 165 .map(|assoc| assoc.container(db))
@@ -179,10 +182,18 @@ impl AutoImportAssets {
179 }, 182 },
180 ) 183 )
181 .map(ModuleDef::from) 184 .map(ModuleDef::from)
185 .map(Either::Left)
186 }
187 _ => Some(candidate),
188 })
189 .filter_map(|candidate| match candidate {
190 Either::Left(module_def) => {
191 self.module_with_name_to_import.find_use_path(db, module_def)
192 }
193 Either::Right(macro_def) => {
194 self.module_with_name_to_import.find_use_path(db, macro_def)
182 } 195 }
183 _ => Some(module_def),
184 }) 196 })
185 .filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def))
186 .filter(|use_path| !use_path.segments.is_empty()) 197 .filter(|use_path| !use_path.segments.is_empty())
187 .take(20) 198 .take(20)
188 .collect::<BTreeSet<_>>() 199 .collect::<BTreeSet<_>>()
@@ -440,6 +451,30 @@ mod tests {
440 } 451 }
441 452
442 #[test] 453 #[test]
454 fn macro_import() {
455 check_assist(
456 auto_import,
457 r"
458 //- /lib.rs crate:crate_with_macro
459 #[macro_export]
460 macro_rules! foo {
461 () => ()
462 }
463
464 //- /main.rs crate:main deps:crate_with_macro
465 fn main() {
466 foo<|>
467 }",
468 r"use crate_with_macro::foo;
469
470fn main() {
471 foo<|>
472}
473",
474 );
475 }
476
477 #[test]
443 fn auto_import_target() { 478 fn auto_import_target() {
444 check_assist_target( 479 check_assist_target(
445 auto_import, 480 auto_import,
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index 54e0a6c84..cd6d1ee6c 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -2,13 +2,14 @@ use ra_syntax::{
2 ast::{self, NameOwner, VisibilityOwner}, 2 ast::{self, NameOwner, VisibilityOwner},
3 AstNode, 3 AstNode,
4 SyntaxKind::{ 4 SyntaxKind::{
5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, IDENT, MODULE, STRUCT_DEF, TRAIT_DEF, 5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
6 VISIBILITY, WHITESPACE, 6 WHITESPACE,
7 }, 7 },
8 SyntaxNode, TextUnit, T, 8 SyntaxNode, TextUnit, T,
9}; 9};
10 10
11use crate::{Assist, AssistCtx, AssistId}; 11use crate::{Assist, AssistCtx, AssistId};
12use test_utils::tested_by;
12 13
13// Assist: change_visibility 14// Assist: change_visibility
14// 15//
@@ -47,13 +48,16 @@ fn add_vis(ctx: AssistCtx) -> Option<Assist> {
47 } 48 }
48 (vis_offset(&parent), keyword.text_range()) 49 (vis_offset(&parent), keyword.text_range())
49 } else { 50 } else {
50 let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?; 51 let field_name: ast::Name = ctx.find_node_at_offset()?;
51 let field = ident.parent().ancestors().find_map(ast::RecordFieldDef::cast)?; 52 let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?;
52 if field.name()?.syntax().text_range() != ident.text_range() && field.visibility().is_some() 53 if field.name()? != field_name {
53 { 54 tested_by!(change_visibility_field_false_positive);
54 return None; 55 return None;
55 } 56 }
56 (vis_offset(field.syntax()), ident.text_range()) 57 if field.visibility().is_some() {
58 return None;
59 }
60 (vis_offset(field.syntax()), field_name.syntax().text_range())
57 }; 61 };
58 62
59 ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| { 63 ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| {
@@ -98,8 +102,11 @@ fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option<Assist> {
98 102
99#[cfg(test)] 103#[cfg(test)]
100mod tests { 104mod tests {
105 use test_utils::covers;
106
107 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
108
101 use super::*; 109 use super::*;
102 use crate::helpers::{check_assist, check_assist_target};
103 110
104 #[test] 111 #[test]
105 fn change_visibility_adds_pub_crate_to_items() { 112 fn change_visibility_adds_pub_crate_to_items() {
@@ -120,8 +127,17 @@ mod tests {
120 fn change_visibility_works_with_struct_fields() { 127 fn change_visibility_works_with_struct_fields() {
121 check_assist( 128 check_assist(
122 change_visibility, 129 change_visibility,
123 "struct S { <|>field: u32 }", 130 r"struct S { <|>field: u32 }",
124 "struct S { <|>pub(crate) field: u32 }", 131 r"struct S { <|>pub(crate) field: u32 }",
132 )
133 }
134
135 #[test]
136 fn change_visibility_field_false_positive() {
137 covers!(change_visibility_field_false_positive);
138 check_assist_not_applicable(
139 change_visibility,
140 r"struct S { field: [(); { let <|>x = ();}] }",
125 ) 141 )
126 } 142 }
127 143
@@ -144,7 +160,7 @@ mod tests {
144 fn change_visibility_handles_comment_attrs() { 160 fn change_visibility_handles_comment_attrs() {
145 check_assist( 161 check_assist(
146 change_visibility, 162 change_visibility,
147 " 163 r"
148 /// docs 164 /// docs
149 165
150 // comments 166 // comments
@@ -152,7 +168,7 @@ mod tests {
152 #[derive(Debug)] 168 #[derive(Debug)]
153 <|>struct Foo; 169 <|>struct Foo;
154 ", 170 ",
155 " 171 r"
156 /// docs 172 /// docs
157 173
158 // comments 174 // comments
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 7463b2af7..add82e5b1 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -2,7 +2,7 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use hir::{Adt, HasSource, Semantics}; 5use hir::{Adt, HasSource, ModuleDef, Semantics};
6use itertools::Itertools; 6use itertools::Itertools;
7use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
8 8
@@ -30,8 +30,8 @@ use ast::{MatchArm, Pat};
30// 30//
31// fn handle(action: Action) { 31// fn handle(action: Action) {
32// match action { 32// match action {
33// Action::Move { distance } => (), 33// Action::Move { distance } => {}
34// Action::Stop => (), 34// Action::Stop => {}
35// } 35// }
36// } 36// }
37// ``` 37// ```
@@ -57,7 +57,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
57 .into_iter() 57 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db, module, variant)) 58 .filter_map(|variant| build_pat(ctx.db, module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) 60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect() 61 .collect()
62 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { 62 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
63 // Partial fill not currently supported for tuple of enums. 63 // Partial fill not currently supported for tuple of enums.
@@ -86,7 +86,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
86 ast::Pat::from(make::tuple_pat(patterns)) 86 ast::Pat::from(make::tuple_pat(patterns))
87 }) 87 })
88 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 88 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
89 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) 89 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
90 .collect() 90 .collect()
91 } else { 91 } else {
92 return None; 92 return None;
@@ -154,7 +154,7 @@ fn resolve_tuple_of_enum_def(
154} 154}
155 155
156fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { 156fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
157 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?); 157 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, ModuleDef::from(var))?);
158 158
159 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 159 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
160 let pat: ast::Pat = match var.source(db).value.kind() { 160 let pat: ast::Pat = match var.source(db).value.kind() {
@@ -192,8 +192,8 @@ mod tests {
192 fn main() { 192 fn main() {
193 match A::As<|> { 193 match A::As<|> {
194 A::As, 194 A::As,
195 A::Bs{x,y:Some(_)} => (), 195 A::Bs{x,y:Some(_)} => {}
196 A::Cs(_, Some(_)) => (), 196 A::Cs(_, Some(_)) => {}
197 } 197 }
198 } 198 }
199 "#, 199 "#,
@@ -227,8 +227,8 @@ mod tests {
227 } 227 }
228 fn main() { 228 fn main() {
229 match A::As<|> { 229 match A::As<|> {
230 A::Bs{x,y:Some(_)} => (), 230 A::Bs{x,y:Some(_)} => {}
231 A::Cs(_, Some(_)) => (), 231 A::Cs(_, Some(_)) => {}
232 } 232 }
233 } 233 }
234 "#, 234 "#,
@@ -240,9 +240,9 @@ mod tests {
240 } 240 }
241 fn main() { 241 fn main() {
242 match <|>A::As { 242 match <|>A::As {
243 A::Bs{x,y:Some(_)} => (), 243 A::Bs{x,y:Some(_)} => {}
244 A::Cs(_, Some(_)) => (), 244 A::Cs(_, Some(_)) => {}
245 A::As => (), 245 A::As => {}
246 } 246 }
247 } 247 }
248 "#, 248 "#,
@@ -261,7 +261,7 @@ mod tests {
261 } 261 }
262 fn main() { 262 fn main() {
263 match A::As<|> { 263 match A::As<|> {
264 A::Cs(_) | A::Bs => (), 264 A::Cs(_) | A::Bs => {}
265 } 265 }
266 } 266 }
267 "#, 267 "#,
@@ -273,8 +273,8 @@ mod tests {
273 } 273 }
274 fn main() { 274 fn main() {
275 match <|>A::As { 275 match <|>A::As {
276 A::Cs(_) | A::Bs => (), 276 A::Cs(_) | A::Bs => {}
277 A::As => (), 277 A::As => {}
278 } 278 }
279 } 279 }
280 "#, 280 "#,
@@ -299,8 +299,8 @@ mod tests {
299 } 299 }
300 fn main() { 300 fn main() {
301 match A::As<|> { 301 match A::As<|> {
302 A::Bs if 0 < 1 => (), 302 A::Bs if 0 < 1 => {}
303 A::Ds(_value) => (), 303 A::Ds(_value) => { let x = 1; }
304 A::Es(B::Xs) => (), 304 A::Es(B::Xs) => (),
305 } 305 }
306 } 306 }
@@ -319,11 +319,11 @@ mod tests {
319 } 319 }
320 fn main() { 320 fn main() {
321 match <|>A::As { 321 match <|>A::As {
322 A::Bs if 0 < 1 => (), 322 A::Bs if 0 < 1 => {}
323 A::Ds(_value) => (), 323 A::Ds(_value) => { let x = 1; }
324 A::Es(B::Xs) => (), 324 A::Es(B::Xs) => (),
325 A::As => (), 325 A::As => {}
326 A::Cs => (), 326 A::Cs => {}
327 } 327 }
328 } 328 }
329 "#, 329 "#,
@@ -360,11 +360,11 @@ mod tests {
360 fn main() { 360 fn main() {
361 let a = A::As; 361 let a = A::As;
362 match <|>a { 362 match <|>a {
363 A::As => (), 363 A::As => {}
364 A::Bs => (), 364 A::Bs => {}
365 A::Cs(_) => (), 365 A::Cs(_) => {}
366 A::Ds(_, _) => (), 366 A::Ds(_, _) => {}
367 A::Es { x, y } => (), 367 A::Es { x, y } => {}
368 } 368 }
369 } 369 }
370 "#, 370 "#,
@@ -405,10 +405,10 @@ mod tests {
405 let a = A::One; 405 let a = A::One;
406 let b = B::One; 406 let b = B::One;
407 match <|>(a, b) { 407 match <|>(a, b) {
408 (A::One, B::One) => (), 408 (A::One, B::One) => {}
409 (A::One, B::Two) => (), 409 (A::One, B::Two) => {}
410 (A::Two, B::One) => (), 410 (A::Two, B::One) => {}
411 (A::Two, B::Two) => (), 411 (A::Two, B::Two) => {}
412 } 412 }
413 } 413 }
414 "#, 414 "#,
@@ -449,10 +449,10 @@ mod tests {
449 let a = A::One; 449 let a = A::One;
450 let b = B::One; 450 let b = B::One;
451 match <|>(&a, &b) { 451 match <|>(&a, &b) {
452 (A::One, B::One) => (), 452 (A::One, B::One) => {}
453 (A::One, B::Two) => (), 453 (A::One, B::Two) => {}
454 (A::Two, B::One) => (), 454 (A::Two, B::One) => {}
455 (A::Two, B::Two) => (), 455 (A::Two, B::Two) => {}
456 } 456 }
457 } 457 }
458 "#, 458 "#,
@@ -477,7 +477,7 @@ mod tests {
477 let a = A::One; 477 let a = A::One;
478 let b = B::One; 478 let b = B::One;
479 match (a<|>, b) { 479 match (a<|>, b) {
480 (A::Two, B::One) => (), 480 (A::Two, B::One) => {}
481 } 481 }
482 } 482 }
483 "#, 483 "#,
@@ -502,10 +502,10 @@ mod tests {
502 let a = A::One; 502 let a = A::One;
503 let b = B::One; 503 let b = B::One;
504 match (a<|>, b) { 504 match (a<|>, b) {
505 (A::Two, B::One) => (), 505 (A::Two, B::One) => {}
506 (A::One, B::One) => (), 506 (A::One, B::One) => {}
507 (A::One, B::Two) => (), 507 (A::One, B::Two) => {}
508 (A::Two, B::Two) => (), 508 (A::Two, B::Two) => {}
509 } 509 }
510 } 510 }
511 "#, 511 "#,
@@ -555,7 +555,7 @@ mod tests {
555 555
556 fn foo(a: &A) { 556 fn foo(a: &A) {
557 match <|>a { 557 match <|>a {
558 A::As => (), 558 A::As => {}
559 } 559 }
560 } 560 }
561 "#, 561 "#,
@@ -580,7 +580,7 @@ mod tests {
580 580
581 fn foo(a: &mut A) { 581 fn foo(a: &mut A) {
582 match <|>a { 582 match <|>a {
583 A::Es { x, y } => (), 583 A::Es { x, y } => {}
584 } 584 }
585 } 585 }
586 "#, 586 "#,
@@ -611,7 +611,7 @@ mod tests {
611 611
612 fn main() { 612 fn main() {
613 match E::X { 613 match E::X {
614 <|>_ => {}, 614 <|>_ => {}
615 } 615 }
616 } 616 }
617 "#, 617 "#,
@@ -620,8 +620,8 @@ mod tests {
620 620
621 fn main() { 621 fn main() {
622 match <|>E::X { 622 match <|>E::X {
623 E::X => (), 623 E::X => {}
624 E::Y => (), 624 E::Y => {}
625 } 625 }
626 } 626 }
627 "#, 627 "#,
@@ -648,8 +648,8 @@ mod tests {
648 648
649 fn main() { 649 fn main() {
650 match <|>X { 650 match <|>X {
651 X => (), 651 X => {}
652 foo::E::Y => (), 652 foo::E::Y => {}
653 } 653 }
654 } 654 }
655 "#, 655 "#,
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index 89bc975bd..9c57d1e30 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -1,9 +1,9 @@
1use std::iter::successors; 1use std::iter::successors;
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 algo::neighbor, 4 algo::{neighbor, SyntaxRewriter},
5 ast::{self, edit::AstNodeEdit, make}, 5 ast::{self, edit::AstNodeEdit, make},
6 AstNode, AstToken, Direction, InsertPosition, SyntaxElement, TextRange, T, 6 AstNode, Direction, InsertPosition, SyntaxElement, T,
7}; 7};
8 8
9use crate::{Assist, AssistCtx, AssistId}; 9use crate::{Assist, AssistCtx, AssistId};
@@ -22,9 +22,10 @@ use crate::{Assist, AssistCtx, AssistId};
22// ``` 22// ```
23pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> { 23pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> {
24 let tree: ast::UseTree = ctx.find_node_at_offset()?; 24 let tree: ast::UseTree = ctx.find_node_at_offset()?;
25 let (new_tree, to_delete) = if let Some(use_item) = 25 let mut rewriter = SyntaxRewriter::default();
26 tree.syntax().parent().and_then(ast::UseItem::cast) 26 let mut offset = ctx.frange.range.start();
27 { 27
28 if let Some(use_item) = tree.syntax().parent().and_then(ast::UseItem::cast) {
28 let (merged, to_delete) = next_prev() 29 let (merged, to_delete) = next_prev()
29 .filter_map(|dir| neighbor(&use_item, dir)) 30 .filter_map(|dir| neighbor(&use_item, dir))
30 .filter_map(|it| Some((it.clone(), it.use_tree()?))) 31 .filter_map(|it| Some((it.clone(), it.use_tree()?)))
@@ -32,42 +33,28 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> {
32 Some((try_merge_trees(&tree, &use_tree)?, use_item.clone())) 33 Some((try_merge_trees(&tree, &use_tree)?, use_item.clone()))
33 })?; 34 })?;
34 35
35 let mut range = to_delete.syntax().text_range(); 36 rewriter.replace_ast(&tree, &merged);
36 let next_ws = to_delete 37 rewriter += to_delete.remove();
37 .syntax() 38
38 .next_sibling_or_token() 39 if to_delete.syntax().text_range().end() < offset {
39 .and_then(|it| it.into_token()) 40 offset -= to_delete.syntax().text_range().len();
40 .and_then(ast::Whitespace::cast);
41 if let Some(ws) = next_ws {
42 range = range.extend_to(&ws.syntax().text_range())
43 } 41 }
44 (merged, range)
45 } else { 42 } else {
46 let (merged, to_delete) = next_prev() 43 let (merged, to_delete) = next_prev()
47 .filter_map(|dir| neighbor(&tree, dir)) 44 .filter_map(|dir| neighbor(&tree, dir))
48 .find_map(|use_tree| Some((try_merge_trees(&tree, &use_tree)?, use_tree.clone())))?; 45 .find_map(|use_tree| Some((try_merge_trees(&tree, &use_tree)?, use_tree.clone())))?;
49 46
50 let mut range = to_delete.syntax().text_range(); 47 rewriter.replace_ast(&tree, &merged);
51 if let Some((dir, nb)) = next_prev().find_map(|dir| Some((dir, neighbor(&to_delete, dir)?))) 48 rewriter += to_delete.remove();
52 { 49
53 let nb_range = nb.syntax().text_range(); 50 if to_delete.syntax().text_range().end() < offset {
54 if dir == Direction::Prev { 51 offset -= to_delete.syntax().text_range().len();
55 range = TextRange::from_to(nb_range.end(), range.end());
56 } else {
57 range = TextRange::from_to(range.start(), nb_range.start());
58 }
59 } 52 }
60 (merged, range)
61 }; 53 };
62 54
63 let mut offset = ctx.frange.range.start();
64 ctx.add_assist(AssistId("merge_imports"), "Merge imports", |edit| { 55 ctx.add_assist(AssistId("merge_imports"), "Merge imports", |edit| {
65 edit.replace_ast(tree, new_tree); 56 edit.rewrite(rewriter);
66 edit.delete(to_delete); 57 // FIXME: we only need because our diff is imprecise
67
68 if to_delete.end() <= offset {
69 offset -= to_delete.len();
70 }
71 edit.set_cursor(offset); 58 edit.set_cursor(offset);
72 }) 59 })
73} 60}
@@ -156,7 +143,7 @@ use std::fmt::Debug;
156use std::fmt<|>::Display; 143use std::fmt<|>::Display;
157", 144",
158 r" 145 r"
159use std::fmt<|>::{Display, Debug}; 146use std::fmt:<|>:{Display, Debug};
160", 147",
161 ); 148 );
162 } 149 }
@@ -178,7 +165,57 @@ use std::{fmt<|>::{Debug, Display}};
178use std::{fmt::Debug, fmt<|>::Display}; 165use std::{fmt::Debug, fmt<|>::Display};
179", 166",
180 r" 167 r"
181use std::{fmt<|>::{Display, Debug}}; 168use std::{fmt::<|>{Display, Debug}};
169",
170 );
171 }
172
173 #[test]
174 fn removes_just_enough_whitespace() {
175 check_assist(
176 merge_imports,
177 r"
178use foo<|>::bar;
179use foo::baz;
180
181/// Doc comment
182",
183 r"
184use foo<|>::{bar, baz};
185
186/// Doc comment
187",
188 );
189 }
190
191 #[test]
192 fn works_with_trailing_comma() {
193 check_assist(
194 merge_imports,
195 r"
196use {
197 foo<|>::bar,
198 foo::baz,
199};
200",
201 r"
202use {
203 foo<|>::{bar, baz},
204};
205",
206 );
207 check_assist(
208 merge_imports,
209 r"
210use {
211 foo::baz,
212 foo<|>::bar,
213};
214",
215 r"
216use {
217 foo::{bar<|>, baz},
218};
182", 219",
183 ); 220 );
184 } 221 }