diff options
Diffstat (limited to 'crates/ra_assists/src/handlers')
-rw-r--r-- | crates/ra_assists/src/handlers/add_missing_impl_members.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 49 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/change_visibility.rs | 40 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/fill_match_arms.rs | 94 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/merge_imports.rs | 103 |
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 | }; |
20 | use 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)] | ||
61 | struct AutoImportAssets { | 63 | struct 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 | |||
470 | fn 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 | ||
11 | use crate::{Assist, AssistCtx, AssistId}; | 11 | use crate::{Assist, AssistCtx, AssistId}; |
12 | use 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)] |
100 | mod tests { | 104 | mod 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 | ||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use hir::{Adt, HasSource, Semantics}; | 5 | use hir::{Adt, HasSource, ModuleDef, Semantics}; |
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use ra_ide_db::RootDatabase; | 7 | use 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 | ||
156 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { | 156 | fn 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 @@ | |||
1 | use std::iter::successors; | 1 | use std::iter::successors; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use 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 | ||
9 | use crate::{Assist, AssistCtx, AssistId}; | 9 | use crate::{Assist, AssistCtx, AssistId}; |
@@ -22,9 +22,10 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
22 | // ``` | 22 | // ``` |
23 | pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> { | 23 | pub(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; | |||
156 | use std::fmt<|>::Display; | 143 | use std::fmt<|>::Display; |
157 | ", | 144 | ", |
158 | r" | 145 | r" |
159 | use std::fmt<|>::{Display, Debug}; | 146 | use std::fmt:<|>:{Display, Debug}; |
160 | ", | 147 | ", |
161 | ); | 148 | ); |
162 | } | 149 | } |
@@ -178,7 +165,57 @@ use std::{fmt<|>::{Debug, Display}}; | |||
178 | use std::{fmt::Debug, fmt<|>::Display}; | 165 | use std::{fmt::Debug, fmt<|>::Display}; |
179 | ", | 166 | ", |
180 | r" | 167 | r" |
181 | use std::{fmt<|>::{Display, Debug}}; | 168 | use std::{fmt::<|>{Display, Debug}}; |
169 | ", | ||
170 | ); | ||
171 | } | ||
172 | |||
173 | #[test] | ||
174 | fn removes_just_enough_whitespace() { | ||
175 | check_assist( | ||
176 | merge_imports, | ||
177 | r" | ||
178 | use foo<|>::bar; | ||
179 | use foo::baz; | ||
180 | |||
181 | /// Doc comment | ||
182 | ", | ||
183 | r" | ||
184 | use 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" | ||
196 | use { | ||
197 | foo<|>::bar, | ||
198 | foo::baz, | ||
199 | }; | ||
200 | ", | ||
201 | r" | ||
202 | use { | ||
203 | foo<|>::{bar, baz}, | ||
204 | }; | ||
205 | ", | ||
206 | ); | ||
207 | check_assist( | ||
208 | merge_imports, | ||
209 | r" | ||
210 | use { | ||
211 | foo::baz, | ||
212 | foo<|>::bar, | ||
213 | }; | ||
214 | ", | ||
215 | r" | ||
216 | use { | ||
217 | foo::{bar<|>, baz}, | ||
218 | }; | ||
182 | ", | 219 | ", |
183 | ); | 220 | ); |
184 | } | 221 | } |