Skip to content

Commit 18f796a

Browse files
Refactor to be just one assist
1 parent ca9ffba commit 18f796a

File tree

3 files changed

+328
-532
lines changed

3 files changed

+328
-532
lines changed

crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs

Lines changed: 328 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use either::Either;
12
use ide_db::defs::{Definition, NameRefClass};
23
use syntax::{
34
ast::{self, AstNode, GenericParamsOwner, VisibilityOwner},
@@ -8,7 +9,7 @@ use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind,
89

910
// Assist: convert_tuple_struct_to_named_struct
1011
//
11-
// Converts tuple struct to struct with named fields.
12+
// Converts tuple struct to struct with named fields, and analogously for tuple enum variants.
1213
//
1314
// ```
1415
// struct Point$0(f32, f32);
@@ -49,14 +50,21 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
4950
acc: &mut Assists,
5051
ctx: &AssistContext,
5152
) -> Option<()> {
52-
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
53-
let tuple_fields = match strukt.field_list()? {
53+
let strukt = ctx
54+
.find_node_at_offset::<ast::Struct>()
55+
.map(Either::Left)
56+
.or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
57+
let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
58+
let tuple_fields = match field_list {
5459
ast::FieldList::TupleFieldList(it) => it,
5560
ast::FieldList::RecordFieldList(_) => return None,
5661
};
57-
let strukt_def = ctx.sema.to_def(&strukt)?;
62+
let strukt_def = match &strukt {
63+
Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
64+
Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
65+
};
66+
let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
5867

59-
let target = strukt.syntax().text_range();
6068
acc.add(
6169
AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite),
6270
"Convert to named struct",
@@ -73,7 +81,7 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
7381
fn edit_struct_def(
7482
ctx: &AssistContext,
7583
edit: &mut AssistBuilder,
76-
strukt: &ast::Struct,
84+
strukt: &Either<ast::Struct, ast::Variant>,
7785
tuple_fields: ast::TupleFieldList,
7886
names: Vec<ast::Name>,
7987
) {
@@ -86,27 +94,34 @@ fn edit_struct_def(
8694

8795
edit.edit_file(ctx.frange.file_id);
8896

89-
if let Some(w) = strukt.where_clause() {
90-
edit.delete(w.syntax().text_range());
91-
edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text());
92-
edit.insert(tuple_fields_text_range.start(), w.syntax().text());
93-
edit.insert(tuple_fields_text_range.start(), ",");
94-
edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text());
97+
if let Either::Left(strukt) = strukt {
98+
if let Some(w) = strukt.where_clause() {
99+
edit.delete(w.syntax().text_range());
100+
edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text());
101+
edit.insert(tuple_fields_text_range.start(), w.syntax().text());
102+
edit.insert(tuple_fields_text_range.start(), ",");
103+
edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text());
104+
} else {
105+
edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
106+
}
107+
strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
95108
} else {
96109
edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
97110
}
98111

99112
edit.replace(tuple_fields_text_range, record_fields.to_string());
100-
strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
101113
}
102114

103115
fn edit_struct_references(
104116
ctx: &AssistContext,
105117
edit: &mut AssistBuilder,
106-
strukt: hir::Struct,
118+
strukt: Either<hir::Struct, hir::Variant>,
107119
names: &[ast::Name],
108120
) {
109-
let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt)));
121+
let strukt_def = match strukt {
122+
Either::Left(s) => Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(s))),
123+
Either::Right(v) => Definition::ModuleDef(hir::ModuleDef::Variant(v)),
124+
};
110125
let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
111126

112127
let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> {
@@ -510,6 +525,304 @@ where
510525
T: Display,
511526
{ field1: T }
512527
528+
"#,
529+
);
530+
}
531+
#[test]
532+
fn not_applicable_other_than_tuple_variant() {
533+
check_assist_not_applicable(
534+
convert_tuple_struct_to_named_struct,
535+
r#"enum Enum { Variant$0 { value: usize } };"#,
536+
);
537+
check_assist_not_applicable(convert_tuple_struct_to_named_struct, r#"enum Enum { Variant$0 }"#);
538+
}
539+
540+
#[test]
541+
fn convert_simple_variant() {
542+
check_assist(
543+
convert_tuple_struct_to_named_struct,
544+
r#"
545+
enum A {
546+
$0Variant(usize),
547+
}
548+
549+
impl A {
550+
fn new(value: usize) -> A {
551+
A::Variant(value)
552+
}
553+
554+
fn new_with_default() -> A {
555+
A::new(Default::default())
556+
}
557+
558+
fn value(self) -> usize {
559+
match self {
560+
A::Variant(value) => value,
561+
}
562+
}
563+
}"#,
564+
r#"
565+
enum A {
566+
Variant { field1: usize },
567+
}
568+
569+
impl A {
570+
fn new(value: usize) -> A {
571+
A::Variant { field1: value }
572+
}
573+
574+
fn new_with_default() -> A {
575+
A::new(Default::default())
576+
}
577+
578+
fn value(self) -> usize {
579+
match self {
580+
A::Variant { field1: value } => value,
581+
}
582+
}
583+
}"#,
584+
);
585+
}
586+
587+
#[test]
588+
fn convert_variant_referenced_via_self_kw() {
589+
check_assist(
590+
convert_tuple_struct_to_named_struct,
591+
r#"
592+
enum A {
593+
$0Variant(usize),
594+
}
595+
596+
impl A {
597+
fn new(value: usize) -> A {
598+
Self::Variant(value)
599+
}
600+
601+
fn new_with_default() -> A {
602+
Self::new(Default::default())
603+
}
604+
605+
fn value(self) -> usize {
606+
match self {
607+
Self::Variant(value) => value,
608+
}
609+
}
610+
}"#,
611+
r#"
612+
enum A {
613+
Variant { field1: usize },
614+
}
615+
616+
impl A {
617+
fn new(value: usize) -> A {
618+
Self::Variant { field1: value }
619+
}
620+
621+
fn new_with_default() -> A {
622+
Self::new(Default::default())
623+
}
624+
625+
fn value(self) -> usize {
626+
match self {
627+
Self::Variant { field1: value } => value,
628+
}
629+
}
630+
}"#,
631+
);
632+
}
633+
634+
#[test]
635+
fn convert_destructured_variant() {
636+
check_assist(
637+
convert_tuple_struct_to_named_struct,
638+
r#"
639+
enum A {
640+
$0Variant(usize),
641+
}
642+
643+
impl A {
644+
fn into_inner(self) -> usize {
645+
let A::Variant(first) = self;
646+
first
647+
}
648+
649+
fn into_inner_via_self(self) -> usize {
650+
let Self::Variant(first) = self;
651+
first
652+
}
653+
}"#,
654+
r#"
655+
enum A {
656+
Variant { field1: usize },
657+
}
658+
659+
impl A {
660+
fn into_inner(self) -> usize {
661+
let A::Variant { field1: first } = self;
662+
first
663+
}
664+
665+
fn into_inner_via_self(self) -> usize {
666+
let Self::Variant { field1: first } = self;
667+
first
668+
}
669+
}"#,
670+
);
671+
}
672+
673+
#[test]
674+
fn convert_variant_with_wrapped_references() {
675+
check_assist(
676+
convert_tuple_struct_to_named_struct,
677+
r#"
678+
enum Inner {
679+
$0Variant(usize),
680+
}
681+
enum Outer {
682+
Variant(Inner),
683+
}
684+
685+
impl Outer {
686+
fn new() -> Self {
687+
Self::Variant(Inner::Variant(42))
688+
}
689+
690+
fn into_inner_destructed(self) -> u32 {
691+
let Outer::Variant(Inner::Variant(x)) = self;
692+
x
693+
}
694+
}"#,
695+
r#"
696+
enum Inner {
697+
Variant { field1: usize },
698+
}
699+
enum Outer {
700+
Variant(Inner),
701+
}
702+
703+
impl Outer {
704+
fn new() -> Self {
705+
Self::Variant(Inner::Variant { field1: 42 })
706+
}
707+
708+
fn into_inner_destructed(self) -> u32 {
709+
let Outer::Variant(Inner::Variant { field1: x }) = self;
710+
x
711+
}
712+
}"#,
713+
);
714+
715+
check_assist(
716+
convert_tuple_struct_to_named_struct,
717+
r#"
718+
enum Inner {
719+
Variant(usize),
720+
}
721+
enum Outer {
722+
$0Variant(Inner),
723+
}
724+
725+
impl Outer {
726+
fn new() -> Self {
727+
Self::Variant(Inner::Variant(42))
728+
}
729+
730+
fn into_inner_destructed(self) -> u32 {
731+
let Outer::Variant(Inner::Variant(x)) = self;
732+
x
733+
}
734+
}"#,
735+
r#"
736+
enum Inner {
737+
Variant(usize),
738+
}
739+
enum Outer {
740+
Variant { field1: Inner },
741+
}
742+
743+
impl Outer {
744+
fn new() -> Self {
745+
Self::Variant { field1: Inner::Variant(42) }
746+
}
747+
748+
fn into_inner_destructed(self) -> u32 {
749+
let Outer::Variant { field1: Inner::Variant(x) } = self;
750+
x
751+
}
752+
}"#,
753+
);
754+
}
755+
756+
#[test]
757+
fn convert_variant_with_multi_file_references() {
758+
check_assist(
759+
convert_tuple_struct_to_named_struct,
760+
r#"
761+
//- /main.rs
762+
struct Inner;
763+
enum A {
764+
$0Variant(Inner),
765+
}
766+
767+
mod foo;
768+
769+
//- /foo.rs
770+
use crate::{A, Inner};
771+
fn f() {
772+
let a = A::Variant(Inner);
773+
}
774+
"#,
775+
r#"
776+
//- /main.rs
777+
struct Inner;
778+
enum A {
779+
Variant { field1: Inner },
780+
}
781+
782+
mod foo;
783+
784+
//- /foo.rs
785+
use crate::{A, Inner};
786+
fn f() {
787+
let a = A::Variant { field1: Inner };
788+
}
789+
"#,
790+
);
791+
}
792+
793+
#[test]
794+
fn convert_directly_used_variant() {
795+
check_assist(
796+
convert_tuple_struct_to_named_struct,
797+
r#"
798+
//- /main.rs
799+
struct Inner;
800+
enum A {
801+
$0Variant(Inner),
802+
}
803+
804+
mod foo;
805+
806+
//- /foo.rs
807+
use crate::{A::Variant, Inner};
808+
fn f() {
809+
let a = Variant(Inner);
810+
}
811+
"#,
812+
r#"
813+
//- /main.rs
814+
struct Inner;
815+
enum A {
816+
Variant { field1: Inner },
817+
}
818+
819+
mod foo;
820+
821+
//- /foo.rs
822+
use crate::{A::Variant, Inner};
823+
fn f() {
824+
let a = Variant { field1: Inner };
825+
}
513826
"#,
514827
);
515828
}

0 commit comments

Comments
 (0)