aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs120
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};
5use ide_db::{defs::Definition, search::Reference, RootDatabase}; 5use ide_db::{defs::Definition, search::Reference, RootDatabase};
6use rustc_hash::{FxHashMap, FxHashSet}; 6use rustc_hash::{FxHashMap, FxHashSet};
7use syntax::{ 7use 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
14use crate::{ 13use crate::{
@@ -130,17 +129,21 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En
130fn insert_import( 129fn 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
322mod my_mod { 329mod 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#"
360enum E {
361 <|>V { i: i32, j: i32 }
362}
363
364fn f() {
365 let e = E::V { i: 9, j: 2 };
366}
367"#,
368 r#"
369struct V{ pub i: i32, pub j: i32 }
370
371enum E {
372 V(V)
373}
374
375fn 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 {
372mod foo; 406mod foo;
373 407
374//- /foo.rs 408//- /foo.rs
375use V; 409use crate::{E, V};
376
377use crate::E;
378fn f() { 410fn 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
404struct V{ pub i: i32, pub j: i32 } 436struct V{ pub i: i32, pub j: i32 }
405 437
406enum E { 438enum E {
@@ -408,10 +440,42 @@ enum E {
408} 440}
409mod foo; 441mod foo;
410 442
443//- /foo.rs
444use crate::{E, V};
445fn 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#"
457enum A { <|>One { a: u32, b: u32 } }
458
459struct B(A);
460
461fn foo() {
462 let _ = B(A::One { a: 1, b: 2 });
463}
464"#,
465 r#"
466struct One{ pub a: u32, pub b: u32 }
467
468enum A { One(One) }
469
470struct B(A);
471
472fn 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);