diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 5d75e44586..32c4ae2e86 100644 --- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -56,14 +56,22 @@ pub(crate) fn convert_named_struct_to_tuple_struct( // XXX: We don't currently provide this assist for struct definitions inside macros, but if we // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files // too. - let name = ctx.find_node_at_offset::()?; - let strukt = name.syntax().parent().and_then(>::cast)?; - let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; + let strukt_or_variant = ctx + .find_node_at_offset::() + .map(Either::Left) + .or_else(|| ctx.find_node_at_offset::().map(Either::Right))?; + let field_list = strukt_or_variant.as_ref().either(|s| s.field_list(), |v| v.field_list())?; + + if ctx.offset() > field_list.syntax().text_range().start() { + // Assist could be distracting after the braces + return None; + } + let record_fields = match field_list { ast::FieldList::RecordFieldList(it) => it, ast::FieldList::TupleFieldList(_) => return None, }; - let strukt_def = match &strukt { + let strukt_def = match &strukt_or_variant { Either::Left(s) => Either::Left(ctx.sema.to_def(s)?), Either::Right(v) => Either::Right(ctx.sema.to_def(v)?), }; @@ -71,11 +79,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct( acc.add( AssistId::refactor_rewrite("convert_named_struct_to_tuple_struct"), "Convert to tuple struct", - strukt.syntax().text_range(), + strukt_or_variant.syntax().text_range(), |edit| { edit_field_references(ctx, edit, record_fields.fields()); edit_struct_references(ctx, edit, strukt_def); - edit_struct_def(ctx, edit, &strukt, record_fields); + edit_struct_def(ctx, edit, &strukt_or_variant, record_fields); }, ) } @@ -277,6 +285,88 @@ impl A { struct Inner; struct A(Inner); +impl A { + fn new(inner: Inner) -> A { + A(inner) + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.0 + } +}"#, + ); + } + + #[test] + fn convert_simple_struct_cursor_on_struct_keyword() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Inner; +struct$0 A { inner: Inner } + +impl A { + fn new(inner: Inner) -> A { + A { inner } + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.inner + } +}"#, + r#" +struct Inner; +struct A(Inner); + +impl A { + fn new(inner: Inner) -> A { + A(inner) + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.0 + } +}"#, + ); + } + + #[test] + fn convert_simple_struct_cursor_on_visibility_keyword() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Inner; +pub$0 struct A { inner: Inner } + +impl A { + fn new(inner: Inner) -> A { + A { inner } + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.inner + } +}"#, + r#" +struct Inner; +pub struct A(Inner); + impl A { fn new(inner: Inner) -> A { A(inner) diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 0c0b93bcfb..80756197fb 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -51,18 +51,26 @@ pub(crate) fn convert_tuple_struct_to_named_struct( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - let name = ctx.find_node_at_offset::()?; - let strukt = name.syntax().parent().and_then(>::cast)?; - let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; + let strukt_or_variant = ctx + .find_node_at_offset::() + .map(Either::Left) + .or_else(|| ctx.find_node_at_offset::().map(Either::Right))?; + let field_list = strukt_or_variant.as_ref().either(|s| s.field_list(), |v| v.field_list())?; + + if ctx.offset() > field_list.syntax().text_range().start() { + // Assist could be distracting after the braces + return None; + } + let tuple_fields = match field_list { ast::FieldList::TupleFieldList(it) => it, ast::FieldList::RecordFieldList(_) => return None, }; - let strukt_def = match &strukt { + let strukt_def = match &strukt_or_variant { Either::Left(s) => Either::Left(ctx.sema.to_def(s)?), Either::Right(v) => Either::Right(ctx.sema.to_def(v)?), }; - let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range(); + let target = strukt_or_variant.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range(); acc.add( AssistId::refactor_rewrite("convert_tuple_struct_to_named_struct"), @@ -72,7 +80,7 @@ pub(crate) fn convert_tuple_struct_to_named_struct( let names = generate_names(tuple_fields.fields()); edit_field_references(ctx, edit, tuple_fields.fields(), &names); edit_struct_references(ctx, edit, strukt_def, &names); - edit_struct_def(ctx, edit, &strukt, tuple_fields, names); + edit_struct_def(ctx, edit, &strukt_or_variant, tuple_fields, names); }, ) } @@ -300,6 +308,88 @@ impl A { struct Inner; struct A { field1: Inner } +impl A { + fn new(inner: Inner) -> A { + A { field1: inner } + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.field1 + } +}"#, + ); + } + + #[test] + fn convert_simple_struct_cursor_on_struct_keyword() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +struct Inner; +struct$0 A(Inner); + +impl A { + fn new(inner: Inner) -> A { + A(inner) + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.0 + } +}"#, + r#" +struct Inner; +struct A { field1: Inner } + +impl A { + fn new(inner: Inner) -> A { + A { field1: inner } + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.field1 + } +}"#, + ); + } + + #[test] + fn convert_simple_struct_cursor_on_visibility_keyword() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +struct Inner; +pub$0 struct A(Inner); + +impl A { + fn new(inner: Inner) -> A { + A(inner) + } + + fn new_with_default() -> A { + A::new(Inner) + } + + fn into_inner(self) -> Inner { + self.0 + } +}"#, + r#" +struct Inner; +pub struct A { field1: Inner } + impl A { fn new(inner: Inner) -> A { A { field1: inner }