From f67586066d5299c11ee69823a6dfd3148eaa5ec7 Mon Sep 17 00:00:00 2001 From: Domantas Jadenkus Date: Sun, 14 Feb 2021 11:09:56 +0200 Subject: rename existing assist to generate_enum_is_method --- .../src/handlers/generate_enum_match_method.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_match_method.rs index aeb887e71..3a7177dbf 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs @@ -8,7 +8,7 @@ use crate::{ AssistContext, AssistId, AssistKind, Assists, }; -// Assist: generate_enum_match_method +// Assist: generate_enum_is_method // // Generate an `is_` method for an enum variant. // @@ -34,7 +34,7 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { +pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let variant_name = variant.name()?; let parent_enum = variant.parent_enum(); @@ -55,7 +55,7 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext) let target = variant.syntax().text_range(); acc.add( - AssistId("generate_enum_match_method", AssistKind::Generate), + AssistId("generate_enum_is_method", AssistKind::Generate), "Generate an `is_` method for an enum variant", target, |builder| { @@ -100,13 +100,13 @@ mod tests { use super::*; fn check_not_applicable(ra_fixture: &str) { - check_assist_not_applicable(generate_enum_match_method, ra_fixture) + check_assist_not_applicable(generate_enum_is_method, ra_fixture) } #[test] fn test_generate_enum_match_from_variant() { check_assist( - generate_enum_match_method, + generate_enum_is_method, r#" enum Variant { Undefined, @@ -162,7 +162,7 @@ enum Variant { #[test] fn test_generate_enum_match_from_variant_with_one_variant() { check_assist( - generate_enum_match_method, + generate_enum_is_method, r#"enum Variant { Undefi$0ned }"#, r#" enum Variant { Undefined } @@ -179,7 +179,7 @@ impl Variant { #[test] fn test_generate_enum_match_from_variant_with_visibility_marker() { check_assist( - generate_enum_match_method, + generate_enum_is_method, r#" pub(crate) enum Variant { Undefined, @@ -204,7 +204,7 @@ impl Variant { #[test] fn test_multiple_generate_enum_match_from_variant() { check_assist( - generate_enum_match_method, + generate_enum_is_method, r#" enum Variant { Undefined, -- cgit v1.2.3 From 4ab285a8e5326211c142e8c772d364a35fbbc409 Mon Sep 17 00:00:00 2001 From: Domantas Jadenkus Date: Sun, 14 Feb 2021 11:33:46 +0200 Subject: make generate_enum_is_method work on any variants --- .../src/handlers/generate_enum_match_method.rs | 113 +++++++++++++++++---- 1 file changed, 95 insertions(+), 18 deletions(-) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_match_method.rs index 3a7177dbf..38aca0c88 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs @@ -1,7 +1,6 @@ use stdx::{format_to, to_lower_snake_case}; use syntax::ast::VisibilityOwner; use syntax::ast::{self, AstNode, NameOwner}; -use test_utils::mark; use crate::{ utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, @@ -37,20 +36,17 @@ use crate::{ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let variant_name = variant.name()?; - let parent_enum = variant.parent_enum(); - if !matches!(variant.kind(), ast::StructKind::Unit) { - mark::hit!(test_gen_enum_match_on_non_unit_variant_not_implemented); - return None; - } + let parent_enum = ast::Adt::Enum(variant.parent_enum()); + let variant_kind = variant_kind(&variant); let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); - let fn_name = to_lower_snake_case(&variant_name.to_string()); + let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); // Return early if we've found an existing new fn let impl_def = find_struct_impl( &ctx, - &ast::Adt::Enum(parent_enum.clone()), - format!("is_{}", fn_name).as_str(), + &parent_enum, + &fn_name, )?; let target = variant.syntax().text_range(); @@ -69,20 +65,21 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> format_to!( buf, " /// Returns `true` if the {} is [`{}`]. - {}fn is_{}(&self) -> bool {{ - matches!(self, Self::{}) + {}fn {}(&self) -> bool {{ + matches!(self, Self::{}{}) }}", enum_lowercase_name, variant_name, vis, fn_name, - variant_name + variant_name, + variant_kind.pattern_suffix(), ); let start_offset = impl_def .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) .unwrap_or_else(|| { - buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf); + buf = generate_impl_text(&parent_enum, &buf); parent_enum.syntax().text_range().end() }); @@ -91,10 +88,53 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> ) } +enum VariantKind { + Unit, + /// Tuple with a single field + NewtypeTuple, + /// Tuple with 0 or more than 2 fields + Tuple, + /// Record with a single field + NewtypeRecord { field_name: Option }, + /// Record with 0 or more than 2 fields + Record, +} + +impl VariantKind { + fn pattern_suffix(&self) -> &'static str { + match self { + VariantKind::Unit => "", + VariantKind::NewtypeTuple | + VariantKind::Tuple => "(..)", + VariantKind::NewtypeRecord { .. } | + VariantKind::Record => " { .. }", + } + } +} + +fn variant_kind(variant: &ast::Variant) -> VariantKind { + match variant.kind() { + ast::StructKind::Record(record) => { + if record.fields().count() == 1 { + let field_name = record.fields().nth(0).unwrap().name(); + VariantKind::NewtypeRecord { field_name } + } else { + VariantKind::Record + } + } + ast::StructKind::Tuple(tuple) => { + if tuple.fields().count() == 1 { + VariantKind::NewtypeTuple + } else { + VariantKind::Tuple + } + } + ast::StructKind::Unit => VariantKind::Unit, + } +} + #[cfg(test)] mod tests { - use test_utils::mark; - use crate::tests::{check_assist, check_assist_not_applicable}; use super::*; @@ -147,14 +187,51 @@ impl Variant { } #[test] - fn test_add_from_impl_no_element() { - mark::check!(test_gen_enum_match_on_non_unit_variant_not_implemented); - check_not_applicable( + fn test_generate_enum_match_from_tuple_variant() { + check_assist( + generate_enum_is_method, r#" enum Variant { Undefined, Minor(u32)$0, Major, +}"#, + r#"enum Variant { + Undefined, + Minor(u32), + Major, +} + +impl Variant { + /// Returns `true` if the variant is [`Minor`]. + fn is_minor(&self) -> bool { + matches!(self, Self::Minor(..)) + } +}"#, + ); + } + + #[test] + fn test_generate_enum_match_from_record_variant() { + check_assist( + generate_enum_is_method, + r#" +enum Variant { + Undefined, + Minor { foo: i32 }$0, + Major, +}"#, + r#"enum Variant { + Undefined, + Minor { foo: i32 }, + Major, +} + +impl Variant { + /// Returns `true` if the variant is [`Minor`]. + fn is_minor(&self) -> bool { + matches!(self, Self::Minor { .. }) + } }"#, ); } -- cgit v1.2.3 From 2dcd5d7a7c41407478360bb2b77b3bfa857cca09 Mon Sep 17 00:00:00 2001 From: Domantas Jadenkus Date: Sun, 14 Feb 2021 12:06:42 +0200 Subject: add generate_enum_into_method assist --- .../src/handlers/generate_enum_match_method.rs | 265 +++++++++++++++++++-- 1 file changed, 245 insertions(+), 20 deletions(-) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_match_method.rs index 38aca0c88..25565d4cc 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use stdx::{format_to, to_lower_snake_case}; use syntax::ast::VisibilityOwner; use syntax::ast::{self, AstNode, NameOwner}; @@ -88,14 +89,104 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> ) } +// Assist: generate_enum_into_method +// +// Generate an `into_` method for an enum variant. +// +// ``` +// enum Value { +// Number(i32), +// Text(String)$0, +// } +// ``` +// -> +// ``` +// enum Value { +// Number(i32), +// Text(String), +// } +// +// impl Value { +// fn into_text(self) -> Option { +// if let Self::Text(v) = self { +// Some(v) +// } else { +// None +// } +// } +// } +// ``` +pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let variant = ctx.find_node_at_offset::()?; + let variant_name = variant.name()?; + let parent_enum = ast::Adt::Enum(variant.parent_enum()); + let variant_kind = variant_kind(&variant); + + let fn_name = format!("into_{}", &to_lower_snake_case(variant_name.text())); + + // Return early if we've found an existing new fn + let impl_def = find_struct_impl( + &ctx, + &parent_enum, + &fn_name, + )?; + + let field_type = variant_kind.single_field_type()?; + let (pattern_suffix, bound_name) = variant_kind.binding_pattern()?; + + let target = variant.syntax().text_range(); + acc.add( + AssistId("generate_enum_into_method", AssistKind::Generate), + "Generate an `into_` method for an enum variant", + target, + |builder| { + let mut buf = String::with_capacity(512); + + if impl_def.is_some() { + buf.push('\n'); + } + + let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); + format_to!( + buf, + " {}fn {}(self) -> Option<{}> {{ + if let Self::{}{} = self {{ + Some({}) + }} else {{ + None + }} + }}", + vis, + fn_name, + field_type.syntax(), + variant_name, + pattern_suffix, + bound_name, + ); + + let start_offset = impl_def + .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) + .unwrap_or_else(|| { + buf = generate_impl_text(&parent_enum, &buf); + parent_enum.syntax().text_range().end() + }); + + builder.insert(start_offset, buf); + }, + ) +} + enum VariantKind { Unit, /// Tuple with a single field - NewtypeTuple, + NewtypeTuple { ty: Option }, /// Tuple with 0 or more than 2 fields Tuple, /// Record with a single field - NewtypeRecord { field_name: Option }, + NewtypeRecord { + field_name: Option, + field_type: Option, + }, /// Record with 0 or more than 2 fields Record, } @@ -104,27 +195,57 @@ impl VariantKind { fn pattern_suffix(&self) -> &'static str { match self { VariantKind::Unit => "", - VariantKind::NewtypeTuple | + VariantKind::NewtypeTuple { .. } | VariantKind::Tuple => "(..)", VariantKind::NewtypeRecord { .. } | VariantKind::Record => " { .. }", } } + + fn binding_pattern(&self) -> Option<(String, String)> { + match self { + VariantKind::Unit | + VariantKind::Tuple | + VariantKind::Record | + VariantKind::NewtypeRecord { field_name: None, .. } => None, + VariantKind::NewtypeTuple { .. } => { + Some(("(v)".to_owned(), "v".to_owned())) + } + VariantKind::NewtypeRecord { field_name: Some(name), .. } => { + Some(( + format!(" {{ {} }}", name.syntax()), + name.syntax().to_string(), + )) + } + } + } + + fn single_field_type(&self) -> Option<&ast::Type> { + match self { + VariantKind::Unit | + VariantKind::Tuple | + VariantKind::Record => None, + VariantKind::NewtypeTuple { ty } => ty.as_ref(), + VariantKind::NewtypeRecord { field_type, .. } => field_type.as_ref(), + } + } } fn variant_kind(variant: &ast::Variant) -> VariantKind { match variant.kind() { ast::StructKind::Record(record) => { - if record.fields().count() == 1 { - let field_name = record.fields().nth(0).unwrap().name(); - VariantKind::NewtypeRecord { field_name } + if let Some((single_field,)) = record.fields().collect_tuple() { + let field_name = single_field.name(); + let field_type = single_field.ty(); + VariantKind::NewtypeRecord { field_name, field_type } } else { VariantKind::Record } } ast::StructKind::Tuple(tuple) => { - if tuple.fields().count() == 1 { - VariantKind::NewtypeTuple + if let Some((single_field,)) = tuple.fields().collect_tuple() { + let ty = single_field.ty(); + VariantKind::NewtypeTuple { ty } } else { VariantKind::Tuple } @@ -139,12 +260,8 @@ mod tests { use super::*; - fn check_not_applicable(ra_fixture: &str) { - check_assist_not_applicable(generate_enum_is_method, ra_fixture) - } - #[test] - fn test_generate_enum_match_from_variant() { + fn test_generate_enum_is_from_variant() { check_assist( generate_enum_is_method, r#" @@ -169,8 +286,9 @@ impl Variant { } #[test] - fn test_generate_enum_match_already_implemented() { - check_not_applicable( + fn test_generate_enum_is_already_implemented() { + check_assist_not_applicable( + generate_enum_is_method, r#" enum Variant { Undefined, @@ -187,7 +305,7 @@ impl Variant { } #[test] - fn test_generate_enum_match_from_tuple_variant() { + fn test_generate_enum_is_from_tuple_variant() { check_assist( generate_enum_is_method, r#" @@ -212,7 +330,7 @@ impl Variant { } #[test] - fn test_generate_enum_match_from_record_variant() { + fn test_generate_enum_is_from_record_variant() { check_assist( generate_enum_is_method, r#" @@ -237,7 +355,7 @@ impl Variant { } #[test] - fn test_generate_enum_match_from_variant_with_one_variant() { + fn test_generate_enum_is_from_variant_with_one_variant() { check_assist( generate_enum_is_method, r#"enum Variant { Undefi$0ned }"#, @@ -254,7 +372,7 @@ impl Variant { } #[test] - fn test_generate_enum_match_from_variant_with_visibility_marker() { + fn test_generate_enum_is_from_variant_with_visibility_marker() { check_assist( generate_enum_is_method, r#" @@ -279,7 +397,7 @@ impl Variant { } #[test] - fn test_multiple_generate_enum_match_from_variant() { + fn test_multiple_generate_enum_is_from_variant() { check_assist( generate_enum_is_method, r#" @@ -311,6 +429,113 @@ impl Variant { fn is_major(&self) -> bool { matches!(self, Self::Major) } +}"#, + ); + } + + #[test] + fn test_generate_enum_into_tuple_variant() { + check_assist( + generate_enum_into_method, + r#" +enum Value { + Number(i32), + Text(String)$0, +}"#, + r#"enum Value { + Number(i32), + Text(String), +} + +impl Value { + fn into_text(self) -> Option { + if let Self::Text(v) = self { + Some(v) + } else { + None + } + } +}"#, + ); + } + + #[test] + fn test_generate_enum_into_already_implemented() { + check_assist_not_applicable( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text(String)$0, +} + +impl Value { + fn into_text(self) -> Option { + if let Self::Text(v) = self { + Some(v) + } else { + None + } + } +}"#, + ); + } + + #[test] + fn test_generate_enum_into_unit_variant() { + check_assist_not_applicable( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text(String), + Unit$0, +}"#, + ); + } + + #[test] + fn test_generate_enum_into_record_with_multiple_fields() { + check_assist_not_applicable( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text(String), + Both { first: i32, second: String }$0, +}"#, + ); + } + + #[test] + fn test_generate_enum_into_tuple_with_multiple_fields() { + check_assist_not_applicable( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text(String, String)$0, +}"#, + ); + } + + #[test] + fn test_generate_enum_into_record_variant() { + check_assist( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text { text: String }$0, +}"#, + r#"enum Value { + Number(i32), + Text { text: String }, +} + +impl Value { + fn into_text(self) -> Option { + if let Self::Text { text } = self { + Some(text) + } else { + None + } + } }"#, ); } -- cgit v1.2.3 From 642786986ff21f33d3a08191d7c19cccf97d25e2 Mon Sep 17 00:00:00 2001 From: Domantas Jadenkus Date: Sun, 14 Feb 2021 12:15:20 +0200 Subject: deduplicate some --- .../src/handlers/generate_enum_match_method.rs | 65 ++++++++++------------ 1 file changed, 28 insertions(+), 37 deletions(-) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_match_method.rs index 25565d4cc..b271b48b6 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs @@ -1,12 +1,9 @@ use itertools::Itertools; -use stdx::{format_to, to_lower_snake_case}; +use stdx::to_lower_snake_case; use syntax::ast::VisibilityOwner; use syntax::ast::{self, AstNode, NameOwner}; -use crate::{ - utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, - AssistContext, AssistId, AssistKind, Assists, -}; +use crate::{AssistContext, AssistId, AssistKind, Assists, assist_context::AssistBuilder, utils::{find_impl_block_end, find_struct_impl, generate_impl_text}}; // Assist: generate_enum_is_method // @@ -56,15 +53,8 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> "Generate an `is_` method for an enum variant", target, |builder| { - let mut buf = String::with_capacity(512); - - if impl_def.is_some() { - buf.push('\n'); - } - let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); - format_to!( - buf, + let method = format!( " /// Returns `true` if the {} is [`{}`]. {}fn {}(&self) -> bool {{ matches!(self, Self::{}{}) @@ -77,14 +67,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> variant_kind.pattern_suffix(), ); - let start_offset = impl_def - .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) - .unwrap_or_else(|| { - buf = generate_impl_text(&parent_enum, &buf); - parent_enum.syntax().text_range().end() - }); - - builder.insert(start_offset, buf); + add_method_to_adt(builder, &parent_enum, impl_def, &method); }, ) } @@ -140,15 +123,8 @@ pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) "Generate an `into_` method for an enum variant", target, |builder| { - let mut buf = String::with_capacity(512); - - if impl_def.is_some() { - buf.push('\n'); - } - let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); - format_to!( - buf, + let method = format!( " {}fn {}(self) -> Option<{}> {{ if let Self::{}{} = self {{ Some({}) @@ -164,18 +140,33 @@ pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) bound_name, ); - let start_offset = impl_def - .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) - .unwrap_or_else(|| { - buf = generate_impl_text(&parent_enum, &buf); - parent_enum.syntax().text_range().end() - }); - - builder.insert(start_offset, buf); + add_method_to_adt(builder, &parent_enum, impl_def, &method); }, ) } +fn add_method_to_adt( + builder: &mut AssistBuilder, + adt: &ast::Adt, + impl_def: Option, + method: &str, +) { + let mut buf = String::with_capacity(method.len() + 2); + if impl_def.is_some() { + buf.push('\n'); + } + buf.push_str(method); + + let start_offset = impl_def + .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) + .unwrap_or_else(|| { + buf = generate_impl_text(&adt, &buf); + adt.syntax().text_range().end() + }); + + builder.insert(start_offset, buf); +} + enum VariantKind { Unit, /// Tuple with a single field -- cgit v1.2.3 From e0f08fcc20ba18a8225b5c591b8b5429090d1943 Mon Sep 17 00:00:00 2001 From: Domantas Jadenkus Date: Sun, 14 Feb 2021 19:26:37 +0200 Subject: add generate_enum_as_method assist --- .../src/handlers/generate_enum_match_method.rs | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_match_method.rs index b271b48b6..45a08acad 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs @@ -145,6 +145,79 @@ pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) ) } +// Assist: generate_enum_as_method +// +// Generate an `as_` method for an enum variant. +// +// ``` +// enum Value { +// Number(i32), +// Text(String)$0, +// } +// ``` +// -> +// ``` +// enum Value { +// Number(i32), +// Text(String), +// } +// +// impl Value { +// fn as_text(&self) -> Option<&String> { +// if let Self::Text(v) = self { +// Some(v) +// } else { +// None +// } +// } +// } +// ``` +pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let variant = ctx.find_node_at_offset::()?; + let variant_name = variant.name()?; + let parent_enum = ast::Adt::Enum(variant.parent_enum()); + let variant_kind = variant_kind(&variant); + + let fn_name = format!("as_{}", &to_lower_snake_case(variant_name.text())); + + // Return early if we've found an existing new fn + let impl_def = find_struct_impl( + &ctx, + &parent_enum, + &fn_name, + )?; + + let field_type = variant_kind.single_field_type()?; + let (pattern_suffix, bound_name) = variant_kind.binding_pattern()?; + + let target = variant.syntax().text_range(); + acc.add( + AssistId("generate_enum_as_method", AssistKind::Generate), + "Generate an `as_` method for an enum variant", + target, + |builder| { + let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); + let method = format!( + " {}fn {}(&self) -> Option<&{}> {{ + if let Self::{}{} = self {{ + Some({}) + }} else {{ + None + }} + }}", + vis, + fn_name, + field_type.syntax(), + variant_name, + pattern_suffix, + bound_name, + ); + + add_method_to_adt(builder, &parent_enum, impl_def, &method); + }, + ) +} + fn add_method_to_adt( builder: &mut AssistBuilder, adt: &ast::Adt, @@ -527,6 +600,57 @@ impl Value { None } } +}"#, + ); + } + + #[test] + fn test_generate_enum_as_tuple_variant() { + check_assist( + generate_enum_as_method, + r#" +enum Value { + Number(i32), + Text(String)$0, +}"#, + r#"enum Value { + Number(i32), + Text(String), +} + +impl Value { + fn as_text(&self) -> Option<&String> { + if let Self::Text(v) = self { + Some(v) + } else { + None + } + } +}"#, + ); + } + + #[test] + fn test_generate_enum_as_record_variant() { + check_assist( + generate_enum_as_method, + r#"enum Value { + Number(i32), + Text { text: String }$0, +}"#, + r#"enum Value { + Number(i32), + Text { text: String }, +} + +impl Value { + fn as_text(&self) -> Option<&String> { + if let Self::Text { text } = self { + Some(text) + } else { + None + } + } }"#, ); } -- cgit v1.2.3 From 6f25fef36a9deb5a22a7bb4a380df664245dcfa9 Mon Sep 17 00:00:00 2001 From: Domantas Jadenkus Date: Sun, 14 Feb 2021 19:28:22 +0200 Subject: cargo fmt --- .../src/handlers/generate_enum_match_method.rs | 55 ++++++++-------------- 1 file changed, 20 insertions(+), 35 deletions(-) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_match_method.rs index 45a08acad..670c82200 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs @@ -3,7 +3,11 @@ use stdx::to_lower_snake_case; use syntax::ast::VisibilityOwner; use syntax::ast::{self, AstNode, NameOwner}; -use crate::{AssistContext, AssistId, AssistKind, Assists, assist_context::AssistBuilder, utils::{find_impl_block_end, find_struct_impl, generate_impl_text}}; +use crate::{ + assist_context::AssistBuilder, + utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, + AssistContext, AssistId, AssistKind, Assists, +}; // Assist: generate_enum_is_method // @@ -41,11 +45,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); // Return early if we've found an existing new fn - let impl_def = find_struct_impl( - &ctx, - &parent_enum, - &fn_name, - )?; + let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; let target = variant.syntax().text_range(); acc.add( @@ -108,11 +108,7 @@ pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) let fn_name = format!("into_{}", &to_lower_snake_case(variant_name.text())); // Return early if we've found an existing new fn - let impl_def = find_struct_impl( - &ctx, - &parent_enum, - &fn_name, - )?; + let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; let field_type = variant_kind.single_field_type()?; let (pattern_suffix, bound_name) = variant_kind.binding_pattern()?; @@ -181,11 +177,7 @@ pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext) -> let fn_name = format!("as_{}", &to_lower_snake_case(variant_name.text())); // Return early if we've found an existing new fn - let impl_def = find_struct_impl( - &ctx, - &parent_enum, - &fn_name, - )?; + let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; let field_type = variant_kind.single_field_type()?; let (pattern_suffix, bound_name) = variant_kind.binding_pattern()?; @@ -243,7 +235,9 @@ fn add_method_to_adt( enum VariantKind { Unit, /// Tuple with a single field - NewtypeTuple { ty: Option }, + NewtypeTuple { + ty: Option, + }, /// Tuple with 0 or more than 2 fields Tuple, /// Record with a single field @@ -259,36 +253,27 @@ impl VariantKind { fn pattern_suffix(&self) -> &'static str { match self { VariantKind::Unit => "", - VariantKind::NewtypeTuple { .. } | - VariantKind::Tuple => "(..)", - VariantKind::NewtypeRecord { .. } | - VariantKind::Record => " { .. }", + VariantKind::NewtypeTuple { .. } | VariantKind::Tuple => "(..)", + VariantKind::NewtypeRecord { .. } | VariantKind::Record => " { .. }", } } fn binding_pattern(&self) -> Option<(String, String)> { match self { - VariantKind::Unit | - VariantKind::Tuple | - VariantKind::Record | - VariantKind::NewtypeRecord { field_name: None, .. } => None, - VariantKind::NewtypeTuple { .. } => { - Some(("(v)".to_owned(), "v".to_owned())) - } + VariantKind::Unit + | VariantKind::Tuple + | VariantKind::Record + | VariantKind::NewtypeRecord { field_name: None, .. } => None, + VariantKind::NewtypeTuple { .. } => Some(("(v)".to_owned(), "v".to_owned())), VariantKind::NewtypeRecord { field_name: Some(name), .. } => { - Some(( - format!(" {{ {} }}", name.syntax()), - name.syntax().to_string(), - )) + Some((format!(" {{ {} }}", name.syntax()), name.syntax().to_string())) } } } fn single_field_type(&self) -> Option<&ast::Type> { match self { - VariantKind::Unit | - VariantKind::Tuple | - VariantKind::Record => None, + VariantKind::Unit | VariantKind::Tuple | VariantKind::Record => None, VariantKind::NewtypeTuple { ty } => ty.as_ref(), VariantKind::NewtypeRecord { field_type, .. } => field_type.as_ref(), } -- cgit v1.2.3 From f098a2b31bffafdbd513f32e8c45d62e709173ea Mon Sep 17 00:00:00 2001 From: Domantas Jadenkus Date: Mon, 15 Feb 2021 23:25:33 +0200 Subject: move into_ and as_ generation to a separate file --- .../src/handlers/generate_enum_match_method.rs | 406 +-------------------- .../handlers/generate_enum_projection_method.rs | 307 ++++++++++++++++ 2 files changed, 314 insertions(+), 399 deletions(-) create mode 100644 crates/ide_assists/src/handlers/generate_enum_projection_method.rs (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_match_method.rs index 670c82200..7e181a480 100644 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_match_method.rs @@ -1,11 +1,9 @@ -use itertools::Itertools; use stdx::to_lower_snake_case; use syntax::ast::VisibilityOwner; use syntax::ast::{self, AstNode, NameOwner}; use crate::{ - assist_context::AssistBuilder, - utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, + utils::{add_method_to_adt, find_struct_impl}, AssistContext, AssistId, AssistKind, Assists, }; @@ -39,7 +37,11 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> let variant = ctx.find_node_at_offset::()?; let variant_name = variant.name()?; let parent_enum = ast::Adt::Enum(variant.parent_enum()); - let variant_kind = variant_kind(&variant); + let pattern_suffix = match variant.kind() { + ast::StructKind::Record(_) => " { .. }", + ast::StructKind::Tuple(_) => "(..)", + ast::StructKind::Unit => "", + }; let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); @@ -59,12 +61,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> {}fn {}(&self) -> bool {{ matches!(self, Self::{}{}) }}", - enum_lowercase_name, - variant_name, - vis, - fn_name, - variant_name, - variant_kind.pattern_suffix(), + enum_lowercase_name, variant_name, vis, fn_name, variant_name, pattern_suffix, ); add_method_to_adt(builder, &parent_enum, impl_def, &method); @@ -72,237 +69,6 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> ) } -// Assist: generate_enum_into_method -// -// Generate an `into_` method for an enum variant. -// -// ``` -// enum Value { -// Number(i32), -// Text(String)$0, -// } -// ``` -// -> -// ``` -// enum Value { -// Number(i32), -// Text(String), -// } -// -// impl Value { -// fn into_text(self) -> Option { -// if let Self::Text(v) = self { -// Some(v) -// } else { -// None -// } -// } -// } -// ``` -pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let variant = ctx.find_node_at_offset::()?; - let variant_name = variant.name()?; - let parent_enum = ast::Adt::Enum(variant.parent_enum()); - let variant_kind = variant_kind(&variant); - - let fn_name = format!("into_{}", &to_lower_snake_case(variant_name.text())); - - // Return early if we've found an existing new fn - let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; - - let field_type = variant_kind.single_field_type()?; - let (pattern_suffix, bound_name) = variant_kind.binding_pattern()?; - - let target = variant.syntax().text_range(); - acc.add( - AssistId("generate_enum_into_method", AssistKind::Generate), - "Generate an `into_` method for an enum variant", - target, - |builder| { - let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); - let method = format!( - " {}fn {}(self) -> Option<{}> {{ - if let Self::{}{} = self {{ - Some({}) - }} else {{ - None - }} - }}", - vis, - fn_name, - field_type.syntax(), - variant_name, - pattern_suffix, - bound_name, - ); - - add_method_to_adt(builder, &parent_enum, impl_def, &method); - }, - ) -} - -// Assist: generate_enum_as_method -// -// Generate an `as_` method for an enum variant. -// -// ``` -// enum Value { -// Number(i32), -// Text(String)$0, -// } -// ``` -// -> -// ``` -// enum Value { -// Number(i32), -// Text(String), -// } -// -// impl Value { -// fn as_text(&self) -> Option<&String> { -// if let Self::Text(v) = self { -// Some(v) -// } else { -// None -// } -// } -// } -// ``` -pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let variant = ctx.find_node_at_offset::()?; - let variant_name = variant.name()?; - let parent_enum = ast::Adt::Enum(variant.parent_enum()); - let variant_kind = variant_kind(&variant); - - let fn_name = format!("as_{}", &to_lower_snake_case(variant_name.text())); - - // Return early if we've found an existing new fn - let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; - - let field_type = variant_kind.single_field_type()?; - let (pattern_suffix, bound_name) = variant_kind.binding_pattern()?; - - let target = variant.syntax().text_range(); - acc.add( - AssistId("generate_enum_as_method", AssistKind::Generate), - "Generate an `as_` method for an enum variant", - target, - |builder| { - let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); - let method = format!( - " {}fn {}(&self) -> Option<&{}> {{ - if let Self::{}{} = self {{ - Some({}) - }} else {{ - None - }} - }}", - vis, - fn_name, - field_type.syntax(), - variant_name, - pattern_suffix, - bound_name, - ); - - add_method_to_adt(builder, &parent_enum, impl_def, &method); - }, - ) -} - -fn add_method_to_adt( - builder: &mut AssistBuilder, - adt: &ast::Adt, - impl_def: Option, - method: &str, -) { - let mut buf = String::with_capacity(method.len() + 2); - if impl_def.is_some() { - buf.push('\n'); - } - buf.push_str(method); - - let start_offset = impl_def - .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) - .unwrap_or_else(|| { - buf = generate_impl_text(&adt, &buf); - adt.syntax().text_range().end() - }); - - builder.insert(start_offset, buf); -} - -enum VariantKind { - Unit, - /// Tuple with a single field - NewtypeTuple { - ty: Option, - }, - /// Tuple with 0 or more than 2 fields - Tuple, - /// Record with a single field - NewtypeRecord { - field_name: Option, - field_type: Option, - }, - /// Record with 0 or more than 2 fields - Record, -} - -impl VariantKind { - fn pattern_suffix(&self) -> &'static str { - match self { - VariantKind::Unit => "", - VariantKind::NewtypeTuple { .. } | VariantKind::Tuple => "(..)", - VariantKind::NewtypeRecord { .. } | VariantKind::Record => " { .. }", - } - } - - fn binding_pattern(&self) -> Option<(String, String)> { - match self { - VariantKind::Unit - | VariantKind::Tuple - | VariantKind::Record - | VariantKind::NewtypeRecord { field_name: None, .. } => None, - VariantKind::NewtypeTuple { .. } => Some(("(v)".to_owned(), "v".to_owned())), - VariantKind::NewtypeRecord { field_name: Some(name), .. } => { - Some((format!(" {{ {} }}", name.syntax()), name.syntax().to_string())) - } - } - } - - fn single_field_type(&self) -> Option<&ast::Type> { - match self { - VariantKind::Unit | VariantKind::Tuple | VariantKind::Record => None, - VariantKind::NewtypeTuple { ty } => ty.as_ref(), - VariantKind::NewtypeRecord { field_type, .. } => field_type.as_ref(), - } - } -} - -fn variant_kind(variant: &ast::Variant) -> VariantKind { - match variant.kind() { - ast::StructKind::Record(record) => { - if let Some((single_field,)) = record.fields().collect_tuple() { - let field_name = single_field.name(); - let field_type = single_field.ty(); - VariantKind::NewtypeRecord { field_name, field_type } - } else { - VariantKind::Record - } - } - ast::StructKind::Tuple(tuple) => { - if let Some((single_field,)) = tuple.fields().collect_tuple() { - let ty = single_field.ty(); - VariantKind::NewtypeTuple { ty } - } else { - VariantKind::Tuple - } - } - ast::StructKind::Unit => VariantKind::Unit, - } -} - #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -478,164 +244,6 @@ impl Variant { fn is_major(&self) -> bool { matches!(self, Self::Major) } -}"#, - ); - } - - #[test] - fn test_generate_enum_into_tuple_variant() { - check_assist( - generate_enum_into_method, - r#" -enum Value { - Number(i32), - Text(String)$0, -}"#, - r#"enum Value { - Number(i32), - Text(String), -} - -impl Value { - fn into_text(self) -> Option { - if let Self::Text(v) = self { - Some(v) - } else { - None - } - } -}"#, - ); - } - - #[test] - fn test_generate_enum_into_already_implemented() { - check_assist_not_applicable( - generate_enum_into_method, - r#"enum Value { - Number(i32), - Text(String)$0, -} - -impl Value { - fn into_text(self) -> Option { - if let Self::Text(v) = self { - Some(v) - } else { - None - } - } -}"#, - ); - } - - #[test] - fn test_generate_enum_into_unit_variant() { - check_assist_not_applicable( - generate_enum_into_method, - r#"enum Value { - Number(i32), - Text(String), - Unit$0, -}"#, - ); - } - - #[test] - fn test_generate_enum_into_record_with_multiple_fields() { - check_assist_not_applicable( - generate_enum_into_method, - r#"enum Value { - Number(i32), - Text(String), - Both { first: i32, second: String }$0, -}"#, - ); - } - - #[test] - fn test_generate_enum_into_tuple_with_multiple_fields() { - check_assist_not_applicable( - generate_enum_into_method, - r#"enum Value { - Number(i32), - Text(String, String)$0, -}"#, - ); - } - - #[test] - fn test_generate_enum_into_record_variant() { - check_assist( - generate_enum_into_method, - r#"enum Value { - Number(i32), - Text { text: String }$0, -}"#, - r#"enum Value { - Number(i32), - Text { text: String }, -} - -impl Value { - fn into_text(self) -> Option { - if let Self::Text { text } = self { - Some(text) - } else { - None - } - } -}"#, - ); - } - - #[test] - fn test_generate_enum_as_tuple_variant() { - check_assist( - generate_enum_as_method, - r#" -enum Value { - Number(i32), - Text(String)$0, -}"#, - r#"enum Value { - Number(i32), - Text(String), -} - -impl Value { - fn as_text(&self) -> Option<&String> { - if let Self::Text(v) = self { - Some(v) - } else { - None - } - } -}"#, - ); - } - - #[test] - fn test_generate_enum_as_record_variant() { - check_assist( - generate_enum_as_method, - r#"enum Value { - Number(i32), - Text { text: String }$0, -}"#, - r#"enum Value { - Number(i32), - Text { text: String }, -} - -impl Value { - fn as_text(&self) -> Option<&String> { - if let Self::Text { text } = self { - Some(text) - } else { - None - } - } }"#, ); } diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs new file mode 100644 index 000000000..71447f310 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs @@ -0,0 +1,307 @@ +use itertools::Itertools; +use stdx::to_lower_snake_case; +use syntax::ast::VisibilityOwner; +use syntax::ast::{self, AstNode, NameOwner}; + +use crate::{ + utils::{add_method_to_adt, find_struct_impl}, + AssistContext, AssistId, AssistKind, Assists, +}; + +// Assist: generate_enum_into_method +// +// Generate an `into_` method for an enum variant. +// +// ``` +// enum Value { +// Number(i32), +// Text(String)$0, +// } +// ``` +// -> +// ``` +// enum Value { +// Number(i32), +// Text(String), +// } +// +// impl Value { +// fn into_text(self) -> Option { +// if let Self::Text(v) = self { +// Some(v) +// } else { +// None +// } +// } +// } +// ``` +pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + generate_enum_projection_method( + acc, + ctx, + "generate_enum_into_method", + "Generate an `into_` method for an enum variant", + "into", + "", + ) +} + +// Assist: generate_enum_as_method +// +// Generate an `as_` method for an enum variant. +// +// ``` +// enum Value { +// Number(i32), +// Text(String)$0, +// } +// ``` +// -> +// ``` +// enum Value { +// Number(i32), +// Text(String), +// } +// +// impl Value { +// fn as_text(&self) -> Option<&String> { +// if let Self::Text(v) = self { +// Some(v) +// } else { +// None +// } +// } +// } +// ``` +pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + generate_enum_projection_method( + acc, + ctx, + "generate_enum_as_method", + "Generate an `as_` method for an enum variant", + "as", + "&", + ) +} + +pub(crate) fn generate_enum_projection_method( + acc: &mut Assists, + ctx: &AssistContext, + assist_id: &'static str, + assist_description: &str, + fn_name_prefix: &str, + ref_prefix: &str, +) -> Option<()> { + let variant = ctx.find_node_at_offset::()?; + let variant_name = variant.name()?; + let parent_enum = ast::Adt::Enum(variant.parent_enum()); + + let (pattern_suffix, field_type, bound_name) = match variant.kind() { + ast::StructKind::Record(record) => { + let (field,) = record.fields().collect_tuple()?; + let name = field.name()?.to_string(); + let ty = field.ty()?; + let pattern_suffix = format!(" {{ {} }}", name); + (pattern_suffix, ty, name) + } + ast::StructKind::Tuple(tuple) => { + let (field,) = tuple.fields().collect_tuple()?; + let ty = field.ty()?; + ("(v)".to_owned(), ty, "v".to_owned()) + } + ast::StructKind::Unit => return None, + }; + + let fn_name = format!("{}_{}", fn_name_prefix, &to_lower_snake_case(variant_name.text())); + + // Return early if we've found an existing new fn + let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; + + let target = variant.syntax().text_range(); + acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| { + let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); + let method = format!( + " {0}fn {1}({2}self) -> Option<{2}{3}> {{ + if let Self::{4}{5} = self {{ + Some({6}) + }} else {{ + None + }} + }}", + vis, + fn_name, + ref_prefix, + field_type.syntax(), + variant_name, + pattern_suffix, + bound_name, + ); + + add_method_to_adt(builder, &parent_enum, impl_def, &method); + }) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn test_generate_enum_into_tuple_variant() { + check_assist( + generate_enum_into_method, + r#" +enum Value { + Number(i32), + Text(String)$0, +}"#, + r#"enum Value { + Number(i32), + Text(String), +} + +impl Value { + fn into_text(self) -> Option { + if let Self::Text(v) = self { + Some(v) + } else { + None + } + } +}"#, + ); + } + + #[test] + fn test_generate_enum_into_already_implemented() { + check_assist_not_applicable( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text(String)$0, +} + +impl Value { + fn into_text(self) -> Option { + if let Self::Text(v) = self { + Some(v) + } else { + None + } + } +}"#, + ); + } + + #[test] + fn test_generate_enum_into_unit_variant() { + check_assist_not_applicable( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text(String), + Unit$0, +}"#, + ); + } + + #[test] + fn test_generate_enum_into_record_with_multiple_fields() { + check_assist_not_applicable( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text(String), + Both { first: i32, second: String }$0, +}"#, + ); + } + + #[test] + fn test_generate_enum_into_tuple_with_multiple_fields() { + check_assist_not_applicable( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text(String, String)$0, +}"#, + ); + } + + #[test] + fn test_generate_enum_into_record_variant() { + check_assist( + generate_enum_into_method, + r#"enum Value { + Number(i32), + Text { text: String }$0, +}"#, + r#"enum Value { + Number(i32), + Text { text: String }, +} + +impl Value { + fn into_text(self) -> Option { + if let Self::Text { text } = self { + Some(text) + } else { + None + } + } +}"#, + ); + } + + #[test] + fn test_generate_enum_as_tuple_variant() { + check_assist( + generate_enum_as_method, + r#" +enum Value { + Number(i32), + Text(String)$0, +}"#, + r#"enum Value { + Number(i32), + Text(String), +} + +impl Value { + fn as_text(&self) -> Option<&String> { + if let Self::Text(v) = self { + Some(v) + } else { + None + } + } +}"#, + ); + } + + #[test] + fn test_generate_enum_as_record_variant() { + check_assist( + generate_enum_as_method, + r#"enum Value { + Number(i32), + Text { text: String }$0, +}"#, + r#"enum Value { + Number(i32), + Text { text: String }, +} + +impl Value { + fn as_text(&self) -> Option<&String> { + if let Self::Text { text } = self { + Some(text) + } else { + None + } + } +}"#, + ); + } +} -- cgit v1.2.3 From d4b6cbe5b6b9c90b577da7788669b6b6995ae524 Mon Sep 17 00:00:00 2001 From: Domantas Jadenkus Date: Mon, 15 Feb 2021 23:28:57 +0200 Subject: rename generate_enum_match_method file to match assist name --- .../src/handlers/generate_enum_is_method.rs | 250 +++++++++++++++++++++ .../src/handlers/generate_enum_match_method.rs | 250 --------------------- 2 files changed, 250 insertions(+), 250 deletions(-) create mode 100644 crates/ide_assists/src/handlers/generate_enum_is_method.rs delete mode 100644 crates/ide_assists/src/handlers/generate_enum_match_method.rs (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/generate_enum_is_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs new file mode 100644 index 000000000..7e181a480 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs @@ -0,0 +1,250 @@ +use stdx::to_lower_snake_case; +use syntax::ast::VisibilityOwner; +use syntax::ast::{self, AstNode, NameOwner}; + +use crate::{ + utils::{add_method_to_adt, find_struct_impl}, + AssistContext, AssistId, AssistKind, Assists, +}; + +// Assist: generate_enum_is_method +// +// Generate an `is_` method for an enum variant. +// +// ``` +// enum Version { +// Undefined, +// Minor$0, +// Major, +// } +// ``` +// -> +// ``` +// enum Version { +// Undefined, +// Minor, +// Major, +// } +// +// impl Version { +// /// Returns `true` if the version is [`Minor`]. +// fn is_minor(&self) -> bool { +// matches!(self, Self::Minor) +// } +// } +// ``` +pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let variant = ctx.find_node_at_offset::()?; + let variant_name = variant.name()?; + let parent_enum = ast::Adt::Enum(variant.parent_enum()); + let pattern_suffix = match variant.kind() { + ast::StructKind::Record(_) => " { .. }", + ast::StructKind::Tuple(_) => "(..)", + ast::StructKind::Unit => "", + }; + + let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); + let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); + + // Return early if we've found an existing new fn + let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; + + let target = variant.syntax().text_range(); + acc.add( + AssistId("generate_enum_is_method", AssistKind::Generate), + "Generate an `is_` method for an enum variant", + target, + |builder| { + let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); + let method = format!( + " /// Returns `true` if the {} is [`{}`]. + {}fn {}(&self) -> bool {{ + matches!(self, Self::{}{}) + }}", + enum_lowercase_name, variant_name, vis, fn_name, variant_name, pattern_suffix, + ); + + add_method_to_adt(builder, &parent_enum, impl_def, &method); + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn test_generate_enum_is_from_variant() { + check_assist( + generate_enum_is_method, + r#" +enum Variant { + Undefined, + Minor$0, + Major, +}"#, + r#"enum Variant { + Undefined, + Minor, + Major, +} + +impl Variant { + /// Returns `true` if the variant is [`Minor`]. + fn is_minor(&self) -> bool { + matches!(self, Self::Minor) + } +}"#, + ); + } + + #[test] + fn test_generate_enum_is_already_implemented() { + check_assist_not_applicable( + generate_enum_is_method, + r#" +enum Variant { + Undefined, + Minor$0, + Major, +} + +impl Variant { + fn is_minor(&self) -> bool { + matches!(self, Self::Minor) + } +}"#, + ); + } + + #[test] + fn test_generate_enum_is_from_tuple_variant() { + check_assist( + generate_enum_is_method, + r#" +enum Variant { + Undefined, + Minor(u32)$0, + Major, +}"#, + r#"enum Variant { + Undefined, + Minor(u32), + Major, +} + +impl Variant { + /// Returns `true` if the variant is [`Minor`]. + fn is_minor(&self) -> bool { + matches!(self, Self::Minor(..)) + } +}"#, + ); + } + + #[test] + fn test_generate_enum_is_from_record_variant() { + check_assist( + generate_enum_is_method, + r#" +enum Variant { + Undefined, + Minor { foo: i32 }$0, + Major, +}"#, + r#"enum Variant { + Undefined, + Minor { foo: i32 }, + Major, +} + +impl Variant { + /// Returns `true` if the variant is [`Minor`]. + fn is_minor(&self) -> bool { + matches!(self, Self::Minor { .. }) + } +}"#, + ); + } + + #[test] + fn test_generate_enum_is_from_variant_with_one_variant() { + check_assist( + generate_enum_is_method, + r#"enum Variant { Undefi$0ned }"#, + r#" +enum Variant { Undefined } + +impl Variant { + /// Returns `true` if the variant is [`Undefined`]. + fn is_undefined(&self) -> bool { + matches!(self, Self::Undefined) + } +}"#, + ); + } + + #[test] + fn test_generate_enum_is_from_variant_with_visibility_marker() { + check_assist( + generate_enum_is_method, + r#" +pub(crate) enum Variant { + Undefined, + Minor$0, + Major, +}"#, + r#"pub(crate) enum Variant { + Undefined, + Minor, + Major, +} + +impl Variant { + /// Returns `true` if the variant is [`Minor`]. + pub(crate) fn is_minor(&self) -> bool { + matches!(self, Self::Minor) + } +}"#, + ); + } + + #[test] + fn test_multiple_generate_enum_is_from_variant() { + check_assist( + generate_enum_is_method, + r#" +enum Variant { + Undefined, + Minor, + Major$0, +} + +impl Variant { + /// Returns `true` if the variant is [`Minor`]. + fn is_minor(&self) -> bool { + matches!(self, Self::Minor) + } +}"#, + r#"enum Variant { + Undefined, + Minor, + Major, +} + +impl Variant { + /// Returns `true` if the variant is [`Minor`]. + fn is_minor(&self) -> bool { + matches!(self, Self::Minor) + } + + /// Returns `true` if the variant is [`Major`]. + fn is_major(&self) -> bool { + matches!(self, Self::Major) + } +}"#, + ); + } +} diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_match_method.rs deleted file mode 100644 index 7e181a480..000000000 --- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs +++ /dev/null @@ -1,250 +0,0 @@ -use stdx::to_lower_snake_case; -use syntax::ast::VisibilityOwner; -use syntax::ast::{self, AstNode, NameOwner}; - -use crate::{ - utils::{add_method_to_adt, find_struct_impl}, - AssistContext, AssistId, AssistKind, Assists, -}; - -// Assist: generate_enum_is_method -// -// Generate an `is_` method for an enum variant. -// -// ``` -// enum Version { -// Undefined, -// Minor$0, -// Major, -// } -// ``` -// -> -// ``` -// enum Version { -// Undefined, -// Minor, -// Major, -// } -// -// impl Version { -// /// Returns `true` if the version is [`Minor`]. -// fn is_minor(&self) -> bool { -// matches!(self, Self::Minor) -// } -// } -// ``` -pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let variant = ctx.find_node_at_offset::()?; - let variant_name = variant.name()?; - let parent_enum = ast::Adt::Enum(variant.parent_enum()); - let pattern_suffix = match variant.kind() { - ast::StructKind::Record(_) => " { .. }", - ast::StructKind::Tuple(_) => "(..)", - ast::StructKind::Unit => "", - }; - - let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); - let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text())); - - // Return early if we've found an existing new fn - let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; - - let target = variant.syntax().text_range(); - acc.add( - AssistId("generate_enum_is_method", AssistKind::Generate), - "Generate an `is_` method for an enum variant", - target, - |builder| { - let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); - let method = format!( - " /// Returns `true` if the {} is [`{}`]. - {}fn {}(&self) -> bool {{ - matches!(self, Self::{}{}) - }}", - enum_lowercase_name, variant_name, vis, fn_name, variant_name, pattern_suffix, - ); - - add_method_to_adt(builder, &parent_enum, impl_def, &method); - }, - ) -} - -#[cfg(test)] -mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; - - use super::*; - - #[test] - fn test_generate_enum_is_from_variant() { - check_assist( - generate_enum_is_method, - r#" -enum Variant { - Undefined, - Minor$0, - Major, -}"#, - r#"enum Variant { - Undefined, - Minor, - Major, -} - -impl Variant { - /// Returns `true` if the variant is [`Minor`]. - fn is_minor(&self) -> bool { - matches!(self, Self::Minor) - } -}"#, - ); - } - - #[test] - fn test_generate_enum_is_already_implemented() { - check_assist_not_applicable( - generate_enum_is_method, - r#" -enum Variant { - Undefined, - Minor$0, - Major, -} - -impl Variant { - fn is_minor(&self) -> bool { - matches!(self, Self::Minor) - } -}"#, - ); - } - - #[test] - fn test_generate_enum_is_from_tuple_variant() { - check_assist( - generate_enum_is_method, - r#" -enum Variant { - Undefined, - Minor(u32)$0, - Major, -}"#, - r#"enum Variant { - Undefined, - Minor(u32), - Major, -} - -impl Variant { - /// Returns `true` if the variant is [`Minor`]. - fn is_minor(&self) -> bool { - matches!(self, Self::Minor(..)) - } -}"#, - ); - } - - #[test] - fn test_generate_enum_is_from_record_variant() { - check_assist( - generate_enum_is_method, - r#" -enum Variant { - Undefined, - Minor { foo: i32 }$0, - Major, -}"#, - r#"enum Variant { - Undefined, - Minor { foo: i32 }, - Major, -} - -impl Variant { - /// Returns `true` if the variant is [`Minor`]. - fn is_minor(&self) -> bool { - matches!(self, Self::Minor { .. }) - } -}"#, - ); - } - - #[test] - fn test_generate_enum_is_from_variant_with_one_variant() { - check_assist( - generate_enum_is_method, - r#"enum Variant { Undefi$0ned }"#, - r#" -enum Variant { Undefined } - -impl Variant { - /// Returns `true` if the variant is [`Undefined`]. - fn is_undefined(&self) -> bool { - matches!(self, Self::Undefined) - } -}"#, - ); - } - - #[test] - fn test_generate_enum_is_from_variant_with_visibility_marker() { - check_assist( - generate_enum_is_method, - r#" -pub(crate) enum Variant { - Undefined, - Minor$0, - Major, -}"#, - r#"pub(crate) enum Variant { - Undefined, - Minor, - Major, -} - -impl Variant { - /// Returns `true` if the variant is [`Minor`]. - pub(crate) fn is_minor(&self) -> bool { - matches!(self, Self::Minor) - } -}"#, - ); - } - - #[test] - fn test_multiple_generate_enum_is_from_variant() { - check_assist( - generate_enum_is_method, - r#" -enum Variant { - Undefined, - Minor, - Major$0, -} - -impl Variant { - /// Returns `true` if the variant is [`Minor`]. - fn is_minor(&self) -> bool { - matches!(self, Self::Minor) - } -}"#, - r#"enum Variant { - Undefined, - Minor, - Major, -} - -impl Variant { - /// Returns `true` if the variant is [`Minor`]. - fn is_minor(&self) -> bool { - matches!(self, Self::Minor) - } - - /// Returns `true` if the variant is [`Major`]. - fn is_major(&self) -> bool { - matches!(self, Self::Major) - } -}"#, - ); - } -} -- cgit v1.2.3 From 558bcf4e0bf9d94ab51238e59f6fc5c170f38c3e Mon Sep 17 00:00:00 2001 From: Domantas Jadenkus Date: Tue, 16 Feb 2021 23:43:21 +0200 Subject: generate try_into instead of into --- .../handlers/generate_enum_projection_method.rs | 108 +++++++++++++-------- 1 file changed, 66 insertions(+), 42 deletions(-) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs index 71447f310..871bcab50 100644 --- a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs @@ -8,9 +8,9 @@ use crate::{ AssistContext, AssistId, AssistKind, Assists, }; -// Assist: generate_enum_into_method +// Assist: generate_enum_try_into_method // -// Generate an `into_` method for an enum variant. +// Generate an `try_into_` method for an enum variant. // // ``` // enum Value { @@ -26,23 +26,29 @@ use crate::{ // } // // impl Value { -// fn into_text(self) -> Option { +// fn try_into_text(self) -> Result { // if let Self::Text(v) = self { -// Some(v) +// Ok(v) // } else { -// None +// Err(self) // } // } // } // ``` -pub(crate) fn generate_enum_into_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { +pub(crate) fn generate_enum_try_into_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { generate_enum_projection_method( acc, ctx, - "generate_enum_into_method", - "Generate an `into_` method for an enum variant", - "into", - "", + "generate_enum_try_into_method", + "Generate an `try_into_` method for an enum variant", + ProjectionProps { + fn_name_prefix: "try_into", + self_param: "self", + return_prefix: "Result<", + return_suffix: ", Self>", + happy_case: "Ok", + sad_case: "Err(self)", + }, ) } @@ -79,18 +85,32 @@ pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext) -> ctx, "generate_enum_as_method", "Generate an `as_` method for an enum variant", - "as", - "&", + ProjectionProps { + fn_name_prefix: "as", + self_param: "&self", + return_prefix: "Option<&", + return_suffix: ">", + happy_case: "Some", + sad_case: "None", + }, ) } -pub(crate) fn generate_enum_projection_method( +struct ProjectionProps { + fn_name_prefix: &'static str, + self_param: &'static str, + return_prefix: &'static str, + return_suffix: &'static str, + happy_case: &'static str, + sad_case: &'static str, +} + +fn generate_enum_projection_method( acc: &mut Assists, ctx: &AssistContext, assist_id: &'static str, assist_description: &str, - fn_name_prefix: &str, - ref_prefix: &str, + props: ProjectionProps, ) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let variant_name = variant.name()?; @@ -112,7 +132,7 @@ pub(crate) fn generate_enum_projection_method( ast::StructKind::Unit => return None, }; - let fn_name = format!("{}_{}", fn_name_prefix, &to_lower_snake_case(variant_name.text())); + let fn_name = format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(variant_name.text())); // Return early if we've found an existing new fn let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; @@ -121,20 +141,24 @@ pub(crate) fn generate_enum_projection_method( acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| { let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); let method = format!( - " {0}fn {1}({2}self) -> Option<{2}{3}> {{ - if let Self::{4}{5} = self {{ - Some({6}) + " {0}fn {1}({2}) -> {3}{4}{5} {{ + if let Self::{6}{7} = self {{ + {8}({9}) }} else {{ - None + {10} }} }}", vis, fn_name, - ref_prefix, + props.self_param, + props.return_prefix, field_type.syntax(), + props.return_suffix, variant_name, pattern_suffix, + props.happy_case, bound_name, + props.sad_case, ); add_method_to_adt(builder, &parent_enum, impl_def, &method); @@ -148,9 +172,9 @@ mod tests { use super::*; #[test] - fn test_generate_enum_into_tuple_variant() { + fn test_generate_enum_try_into_tuple_variant() { check_assist( - generate_enum_into_method, + generate_enum_try_into_method, r#" enum Value { Number(i32), @@ -162,11 +186,11 @@ enum Value { } impl Value { - fn into_text(self) -> Option { + fn try_into_text(self) -> Result { if let Self::Text(v) = self { - Some(v) + Ok(v) } else { - None + Err(self) } } }"#, @@ -174,20 +198,20 @@ impl Value { } #[test] - fn test_generate_enum_into_already_implemented() { + fn test_generate_enum_try_into_already_implemented() { check_assist_not_applicable( - generate_enum_into_method, + generate_enum_try_into_method, r#"enum Value { Number(i32), Text(String)$0, } impl Value { - fn into_text(self) -> Option { + fn try_into_text(self) -> Result { if let Self::Text(v) = self { - Some(v) + Ok(v) } else { - None + Err(self) } } }"#, @@ -195,9 +219,9 @@ impl Value { } #[test] - fn test_generate_enum_into_unit_variant() { + fn test_generate_enum_try_into_unit_variant() { check_assist_not_applicable( - generate_enum_into_method, + generate_enum_try_into_method, r#"enum Value { Number(i32), Text(String), @@ -207,9 +231,9 @@ impl Value { } #[test] - fn test_generate_enum_into_record_with_multiple_fields() { + fn test_generate_enum_try_into_record_with_multiple_fields() { check_assist_not_applicable( - generate_enum_into_method, + generate_enum_try_into_method, r#"enum Value { Number(i32), Text(String), @@ -219,9 +243,9 @@ impl Value { } #[test] - fn test_generate_enum_into_tuple_with_multiple_fields() { + fn test_generate_enum_try_into_tuple_with_multiple_fields() { check_assist_not_applicable( - generate_enum_into_method, + generate_enum_try_into_method, r#"enum Value { Number(i32), Text(String, String)$0, @@ -230,9 +254,9 @@ impl Value { } #[test] - fn test_generate_enum_into_record_variant() { + fn test_generate_enum_try_into_record_variant() { check_assist( - generate_enum_into_method, + generate_enum_try_into_method, r#"enum Value { Number(i32), Text { text: String }$0, @@ -243,11 +267,11 @@ impl Value { } impl Value { - fn into_text(self) -> Option { + fn try_into_text(self) -> Result { if let Self::Text { text } = self { - Some(text) + Ok(text) } else { - None + Err(self) } } }"#, -- cgit v1.2.3