Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide-assists/src/handlers/generate_enum_variant.rs198
-rw-r--r--crates/ide-assists/src/handlers/generate_function.rs46
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs26
4 files changed, 272 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs
new file mode 100644
index 0000000000..f3a79accf9
--- /dev/null
+++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -0,0 +1,198 @@
+use hir::HasSource;
+use ide_db::assists::{AssistId, AssistKind};
+use syntax::{
+ ast::{self, edit::IndentLevel},
+ AstNode, TextSize,
+};
+
+use crate::assist_context::{AssistContext, Assists};
+
+// Assist: generate_enum_variant
+//
+// Adds a variant to an enum.
+//
+// ```
+// enum Countries {
+// Ghana,
+// }
+//
+// fn main() {
+// let country = Countries::Lesotho$0;
+// }
+// ```
+// ->
+// ```
+// enum Countries {
+// Ghana,
+// Lesotho,
+// }
+//
+// fn main() {
+// let country = Countries::Lesotho;
+// }
+// ```
+pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+ let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
+ let path = path_expr.path()?;
+
+ if ctx.sema.resolve_path(&path).is_some() {
+ // No need to generate anything if the path resolves
+ return None;
+ }
+
+ let name_ref = path.segment()?.name_ref()?;
+ if name_ref.text().starts_with(char::is_lowercase) {
+ // Don't suggest generating variant if the name starts with a lowercase letter
+ return None;
+ }
+
+ if let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e)))) =
+ ctx.sema.resolve_path(&path.qualifier()?)
+ {
+ let target = path.syntax().text_range();
+ return add_variant_to_accumulator(acc, ctx, target, e, &name_ref);
+ }
+
+ None
+}
+
+fn add_variant_to_accumulator(
+ acc: &mut Assists,
+ ctx: &AssistContext,
+ target: syntax::TextRange,
+ adt: hir::Enum,
+ name_ref: &ast::NameRef,
+) -> Option<()> {
+ let adt_ast = adt.source(ctx.db())?.original_ast_node(ctx.db())?.value;
+ let enum_indent = IndentLevel::from_node(&adt_ast.syntax());
+
+ let variant_list = adt_ast.variant_list()?;
+ let offset = variant_list.syntax().text_range().end() - TextSize::of('}');
+ let empty_enum = variant_list.variants().next().is_none();
+
+ acc.add(
+ AssistId("generate_enum_variant", AssistKind::Generate),
+ "Generate variant",
+ target,
+ |builder| {
+ let text = format!(
+ "{maybe_newline}{indent_1}{name},\n{enum_indent}",
+ maybe_newline = if empty_enum { "\n" } else { "" },
+ indent_1 = IndentLevel(1),
+ name = name_ref,
+ enum_indent = enum_indent
+ );
+ builder.insert(offset, text)
+ },
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::*;
+
+ #[test]
+ fn generate_basic_enum_variant_in_empty_enum() {
+ check_assist(
+ generate_enum_variant,
+ r"
+enum Foo {}
+fn main() {
+ Foo::Bar$0
+}
+",
+ r"
+enum Foo {
+ Bar,
+}
+fn main() {
+ Foo::Bar
+}
+",
+ )
+ }
+
+ #[test]
+ fn generate_basic_enum_variant_in_non_empty_enum() {
+ check_assist(
+ generate_enum_variant,
+ r"
+enum Foo {
+ Bar,
+}
+fn main() {
+ Foo::Baz$0
+}
+",
+ r"
+enum Foo {
+ Bar,
+ Baz,
+}
+fn main() {
+ Foo::Baz
+}
+",
+ )
+ }
+
+ #[test]
+ fn not_applicable_for_existing_variant() {
+ check_assist_not_applicable(
+ generate_enum_variant,
+ r"
+enum Foo {
+ Bar,
+}
+fn main() {
+ Foo::Bar$0
+}
+",
+ )
+ }
+
+ #[test]
+ fn not_applicable_for_lowercase() {
+ check_assist_not_applicable(
+ generate_enum_variant,
+ r"
+enum Foo {
+ Bar,
+}
+fn main() {
+ Foo::new$0
+}
+",
+ )
+ }
+
+ #[test]
+ fn indentation_level_is_correct() {
+ check_assist(
+ generate_enum_variant,
+ r"
+mod m {
+ enum Foo {
+ Bar,
+ }
+}
+fn main() {
+ m::Foo::Baz$0
+}
+",
+ r"
+mod m {
+ enum Foo {
+ Bar,
+ Baz,
+ }
+}
+fn main() {
+ m::Foo::Baz
+}
+",
+ )
+ }
+}
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 24cef5630f..c8650979e9 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -71,6 +71,13 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
get_fn_target(ctx, &target_module, call.clone())?
}
Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) => {
+ if let hir::Adt::Enum(_) = adt {
+ // Don't suggest generating function if the name starts with an uppercase letter
+ if name_ref.text().starts_with(char::is_uppercase) {
+ return None;
+ }
+ }
+
let current_module = ctx.sema.scope(call.syntax())?.module();
let module = adt.module(ctx.sema.db);
target_module = if current_module == module { None } else { Some(module) };
@@ -1738,4 +1745,43 @@ fn foo(value: usize) ${0:-> _} {
",
)
}
+
+ #[test]
+ fn not_applicable_for_enum_variant() {
+ check_assist_not_applicable(
+ generate_function,
+ r"
+enum Foo {}
+fn main() {
+ Foo::Bar$0(true)
+}
+",
+ );
+ }
+
+ #[test]
+ fn applicable_for_enum_method() {
+ check_assist(
+ generate_function,
+ r"
+enum Foo {}
+fn main() {
+ Foo::new$0();
+}
+",
+ r"
+enum Foo {}
+fn main() {
+ Foo::new();
+}
+impl Foo {
+
+
+fn new() ${0:-> _} {
+ todo!()
+}
+}
+",
+ )
+ }
}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index b8607579a3..94fbaff400 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -139,6 +139,7 @@ mod handlers {
mod generate_documentation_template;
mod generate_enum_is_method;
mod generate_enum_projection_method;
+ mod generate_enum_variant;
mod generate_from_impl_for_enum;
mod generate_function;
mod generate_getter;
@@ -227,6 +228,7 @@ mod handlers {
generate_enum_is_method::generate_enum_is_method,
generate_enum_projection_method::generate_enum_as_method,
generate_enum_projection_method::generate_enum_try_into_method,
+ generate_enum_variant::generate_enum_variant,
generate_from_impl_for_enum::generate_from_impl_for_enum,
generate_function::generate_function,
generate_impl::generate_impl,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index c9b0b1ce82..2d57101409 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -1007,6 +1007,32 @@ impl Value {
}
#[test]
+fn doctest_generate_enum_variant() {
+ check_doc_test(
+ "generate_enum_variant",
+ r#####"
+enum Countries {
+ Ghana,
+}
+
+fn main() {
+ let country = Countries::Lesotho$0;
+}
+"#####,
+ r#####"
+enum Countries {
+ Ghana,
+ Lesotho,
+}
+
+fn main() {
+ let country = Countries::Lesotho;
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_generate_from_impl_for_enum() {
check_doc_test(
"generate_from_impl_for_enum",