Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs')
| -rw-r--r-- | crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs | 82 |
1 files changed, 58 insertions, 24 deletions
diff --git a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 24f271ded8..1adb3f4fe4 100644 --- a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -1,10 +1,11 @@ use hir::next_solver::{DbInterner, TypingMode}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; -use syntax::ast::{self, AstNode, HasName}; +use syntax::ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory}; +use syntax::syntax_editor::Position; use crate::{ AssistContext, AssistId, Assists, - utils::{generate_trait_impl_text_intransitive, is_selected}, + utils::{generate_trait_impl_intransitive_with_item, is_selected}, }; // Assist: generate_from_impl_for_enum @@ -33,39 +34,72 @@ pub(crate) fn generate_from_impl_for_enum( let variants = selected_variants(ctx, &variant)?; let target = variant.syntax().text_range(); + let file_id = ctx.vfs_file_id(); acc.add( AssistId::generate("generate_from_impl_for_enum"), "Generate `From` impl for this enum variant(s)", target, |edit| { - let start_offset = variant.parent_enum().syntax().text_range().end(); - let from_impl = variants - .into_iter() - .map(|variant_info| { - let from_trait = format!("From<{}>", variant_info.ty); - let impl_code = generate_impl_code(variant_info); - generate_trait_impl_text_intransitive(&adt, &from_trait, &impl_code) - }) - .collect::<String>(); - edit.insert(start_offset, from_impl); + let make = SyntaxFactory::with_mappings(); + let indent = adt.indent_level(); + let mut elements = Vec::new(); + + for variant_info in variants { + let impl_ = build_from_impl(&make, &adt, variant_info).indent(indent); + elements.push(make.whitespace(&format!("\n\n{indent}")).into()); + elements.push(impl_.syntax().clone().into()); + } + + let mut editor = edit.make_editor(adt.syntax()); + editor.insert_all(Position::after(adt.syntax()), elements); + editor.add_mappings(make.finish_with_mappings()); + edit.add_file_edits(file_id, editor); }, ) } -fn generate_impl_code(VariantInfo { name, field_name, ty }: VariantInfo) -> String { - if let Some(field) = field_name { - format!( - r#" fn from({field}: {ty}) -> Self {{ - Self::{name} {{ {field} }} - }}"# - ) +fn build_from_impl(make: &SyntaxFactory, adt: &ast::Adt, variant_info: VariantInfo) -> ast::Impl { + let VariantInfo { name, field_name, ty } = variant_info; + let trait_ty = make.ty(&format!("From<{ty}>")); + let ret_ty = make.ret_type(make.ty_path(make.ident_path("Self")).into()); + + let (params, body_expr) = if let Some(field) = field_name { + let field_str = field.to_string(); + let param = make.param(make.ident_pat(false, false, make.name(&field_str)).into(), ty); + let field_item = make.record_expr_field(make.name_ref(&field_str), None); + let record = make.record_expr( + make.path_from_text(&format!("Self::{name}")), + make.record_expr_field_list([field_item]), + ); + (make.param_list(None, [param]), ast::Expr::from(record)) } else { - format!( - r#" fn from(v: {ty}) -> Self {{ - Self::{name}(v) - }}"# + let param = make.param(make.ident_pat(false, false, make.name("v")).into(), ty); + let call = make.expr_call( + make.expr_path(make.path_from_text(&format!("Self::{name}"))), + make.arg_list([make.expr_path(make.ident_path("v"))]), + ); + (make.param_list(None, [param]), ast::Expr::from(call)) + }; + + let from_fn = make + .fn_( + [], + None, + make.name("from"), + None, + None, + params, + make.block_expr([], Some(body_expr)), + Some(ret_ty), + false, + false, + false, + false, ) - } + .indent(1.into()); + + let body = make.assoc_item_list([ast::AssocItem::Fn(from_fn)]); + generate_trait_impl_intransitive_with_item(make, adt, trait_ty, body) } struct VariantInfo { |