diff options
-rw-r--r-- | crates/assists/src/handlers/extract_struct_from_enum_variant.rs | 120 |
1 files changed, 92 insertions, 28 deletions
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index 84662d832..067afabf2 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -5,10 +5,9 @@ use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; | |||
5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; |
6 | use rustc_hash::{FxHashMap, FxHashSet}; | 6 | use rustc_hash::{FxHashMap, FxHashSet}; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | algo::find_node_at_offset, | 8 | algo::{find_node_at_offset, SyntaxRewriter}, |
9 | algo::SyntaxRewriter, | 9 | ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner}, |
10 | ast::{self, edit::IndentLevel, make, ArgListOwner, AstNode, NameOwner, VisibilityOwner}, | 10 | SourceFile, SyntaxElement, SyntaxNode, T, |
11 | SourceFile, SyntaxElement, | ||
12 | }; | 11 | }; |
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
@@ -130,17 +129,21 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En | |||
130 | fn insert_import( | 129 | fn insert_import( |
131 | ctx: &AssistContext, | 130 | ctx: &AssistContext, |
132 | rewriter: &mut SyntaxRewriter, | 131 | rewriter: &mut SyntaxRewriter, |
133 | path: &ast::PathExpr, | 132 | scope_node: &SyntaxNode, |
134 | module: &Module, | 133 | module: &Module, |
135 | enum_module_def: &ModuleDef, | 134 | enum_module_def: &ModuleDef, |
136 | variant_hir_name: &Name, | 135 | variant_hir_name: &Name, |
137 | ) -> Option<()> { | 136 | ) -> Option<()> { |
138 | let db = ctx.db(); | 137 | let db = ctx.db(); |
139 | let mod_path = module.find_use_path(db, enum_module_def.clone()); | 138 | let mod_path = module.find_use_path_prefixed( |
139 | db, | ||
140 | enum_module_def.clone(), | ||
141 | ctx.config.insert_use.prefix_kind, | ||
142 | ); | ||
140 | if let Some(mut mod_path) = mod_path { | 143 | if let Some(mut mod_path) = mod_path { |
141 | mod_path.segments.pop(); | 144 | mod_path.segments.pop(); |
142 | mod_path.segments.push(variant_hir_name.clone()); | 145 | mod_path.segments.push(variant_hir_name.clone()); |
143 | let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; | 146 | let scope = ImportScope::find_insert_use_container(scope_node, ctx)?; |
144 | 147 | ||
145 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); | 148 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); |
146 | } | 149 | } |
@@ -204,27 +207,31 @@ fn update_reference( | |||
204 | variant_hir_name: &Name, | 207 | variant_hir_name: &Name, |
205 | visited_modules_set: &mut FxHashSet<Module>, | 208 | visited_modules_set: &mut FxHashSet<Module>, |
206 | ) -> Option<()> { | 209 | ) -> Option<()> { |
207 | let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>( | 210 | let offset = reference.file_range.range.start(); |
208 | source_file.syntax(), | 211 | let (segment, expr) = if let Some(path_expr) = |
209 | reference.file_range.range.start(), | 212 | find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset) |
210 | )?; | 213 | { |
211 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | 214 | // tuple variant |
212 | let list = call.arg_list()?; | 215 | (path_expr.path()?.segment()?, path_expr.syntax().parent()?.clone()) |
213 | let segment = path_expr.path()?.segment()?; | 216 | } else if let Some(record_expr) = |
214 | let module = ctx.sema.scope(&path_expr.syntax()).module()?; | 217 | find_node_at_offset::<ast::RecordExpr>(source_file.syntax(), offset) |
218 | { | ||
219 | // record variant | ||
220 | (record_expr.path()?.segment()?, record_expr.syntax().clone()) | ||
221 | } else { | ||
222 | return None; | ||
223 | }; | ||
224 | |||
225 | let module = ctx.sema.scope(&expr).module()?; | ||
215 | if !visited_modules_set.contains(&module) { | 226 | if !visited_modules_set.contains(&module) { |
216 | if insert_import(ctx, rewriter, &path_expr, &module, enum_module_def, variant_hir_name) | 227 | if insert_import(ctx, rewriter, &expr, &module, enum_module_def, variant_hir_name).is_some() |
217 | .is_some() | ||
218 | { | 228 | { |
219 | visited_modules_set.insert(module); | 229 | visited_modules_set.insert(module); |
220 | } | 230 | } |
221 | } | 231 | } |
222 | 232 | rewriter.insert_after(segment.syntax(), &make::token(T!['('])); | |
223 | let lparen = syntax::SyntaxElement::from(list.l_paren_token()?); | 233 | rewriter.insert_after(segment.syntax(), segment.syntax()); |
224 | let rparen = syntax::SyntaxElement::from(list.r_paren_token()?); | 234 | rewriter.insert_after(&expr, &make::token(T![')'])); |
225 | rewriter.insert_after(&lparen, segment.syntax()); | ||
226 | rewriter.insert_after(&lparen, &lparen); | ||
227 | rewriter.insert_before(&rparen, &rparen); | ||
228 | Some(()) | 235 | Some(()) |
229 | } | 236 | } |
230 | 237 | ||
@@ -320,7 +327,7 @@ fn another_fn() { | |||
320 | r#"use my_mod::my_other_mod::MyField; | 327 | r#"use my_mod::my_other_mod::MyField; |
321 | 328 | ||
322 | mod my_mod { | 329 | mod my_mod { |
323 | use my_other_mod::MyField; | 330 | use self::my_other_mod::MyField; |
324 | 331 | ||
325 | fn another_fn() { | 332 | fn another_fn() { |
326 | let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); | 333 | let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); |
@@ -346,6 +353,33 @@ fn another_fn() { | |||
346 | } | 353 | } |
347 | 354 | ||
348 | #[test] | 355 | #[test] |
356 | fn extract_record_fix_references() { | ||
357 | check_assist( | ||
358 | extract_struct_from_enum_variant, | ||
359 | r#" | ||
360 | enum E { | ||
361 | <|>V { i: i32, j: i32 } | ||
362 | } | ||
363 | |||
364 | fn f() { | ||
365 | let e = E::V { i: 9, j: 2 }; | ||
366 | } | ||
367 | "#, | ||
368 | r#" | ||
369 | struct V{ pub i: i32, pub j: i32 } | ||
370 | |||
371 | enum E { | ||
372 | V(V) | ||
373 | } | ||
374 | |||
375 | fn f() { | ||
376 | let e = E::V(V { i: 9, j: 2 }); | ||
377 | } | ||
378 | "#, | ||
379 | ) | ||
380 | } | ||
381 | |||
382 | #[test] | ||
349 | fn test_several_files() { | 383 | fn test_several_files() { |
350 | check_assist( | 384 | check_assist( |
351 | extract_struct_from_enum_variant, | 385 | extract_struct_from_enum_variant, |
@@ -372,9 +406,7 @@ enum E { | |||
372 | mod foo; | 406 | mod foo; |
373 | 407 | ||
374 | //- /foo.rs | 408 | //- /foo.rs |
375 | use V; | 409 | use crate::{E, V}; |
376 | |||
377 | use crate::E; | ||
378 | fn f() { | 410 | fn f() { |
379 | let e = E::V(V(9, 2)); | 411 | let e = E::V(V(9, 2)); |
380 | } | 412 | } |
@@ -384,7 +416,6 @@ fn f() { | |||
384 | 416 | ||
385 | #[test] | 417 | #[test] |
386 | fn test_several_files_record() { | 418 | fn test_several_files_record() { |
387 | // FIXME: this should fix the usage as well! | ||
388 | check_assist( | 419 | check_assist( |
389 | extract_struct_from_enum_variant, | 420 | extract_struct_from_enum_variant, |
390 | r#" | 421 | r#" |
@@ -401,6 +432,7 @@ fn f() { | |||
401 | } | 432 | } |
402 | "#, | 433 | "#, |
403 | r#" | 434 | r#" |
435 | //- /main.rs | ||
404 | struct V{ pub i: i32, pub j: i32 } | 436 | struct V{ pub i: i32, pub j: i32 } |
405 | 437 | ||
406 | enum E { | 438 | enum E { |
@@ -408,10 +440,42 @@ enum E { | |||
408 | } | 440 | } |
409 | mod foo; | 441 | mod foo; |
410 | 442 | ||
443 | //- /foo.rs | ||
444 | use crate::{E, V}; | ||
445 | fn f() { | ||
446 | let e = E::V(V { i: 9, j: 2 }); | ||
447 | } | ||
411 | "#, | 448 | "#, |
412 | ) | 449 | ) |
413 | } | 450 | } |
414 | 451 | ||
452 | #[test] | ||
453 | fn test_extract_struct_record_nested_call_exp() { | ||
454 | check_assist( | ||
455 | extract_struct_from_enum_variant, | ||
456 | r#" | ||
457 | enum A { <|>One { a: u32, b: u32 } } | ||
458 | |||
459 | struct B(A); | ||
460 | |||
461 | fn foo() { | ||
462 | let _ = B(A::One { a: 1, b: 2 }); | ||
463 | } | ||
464 | "#, | ||
465 | r#" | ||
466 | struct One{ pub a: u32, pub b: u32 } | ||
467 | |||
468 | enum A { One(One) } | ||
469 | |||
470 | struct B(A); | ||
471 | |||
472 | fn foo() { | ||
473 | let _ = B(A::One(One { a: 1, b: 2 })); | ||
474 | } | ||
475 | "#, | ||
476 | ); | ||
477 | } | ||
478 | |||
415 | fn check_not_applicable(ra_fixture: &str) { | 479 | fn check_not_applicable(ra_fixture: &str) { |
416 | let fixture = | 480 | let fixture = |
417 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | 481 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); |