|
1 | 1 | use ide_db::assists::GroupLabel; |
2 | | -use itertools::Itertools; |
3 | 2 | use stdx::to_lower_snake_case; |
4 | | -use syntax::ast::HasVisibility; |
5 | | -use syntax::ast::{self, AstNode, HasName}; |
| 3 | +use syntax::{ |
| 4 | + AstNode, Edition, |
| 5 | + ast::{self, HasName, HasVisibility, edit::AstNodeEdit}, |
| 6 | + syntax_editor::Position, |
| 7 | +}; |
6 | 8 |
|
7 | 9 | use crate::{ |
8 | 10 | AssistContext, AssistId, Assists, |
9 | | - utils::{add_method_to_adt, find_struct_impl, is_selected}, |
| 11 | + utils::{find_struct_impl, generate_impl_with_item, is_selected}, |
10 | 12 | }; |
11 | 13 |
|
12 | 14 | // Assist: generate_enum_is_method |
@@ -64,27 +66,63 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext<'_> |
64 | 66 | target, |
65 | 67 | |builder| { |
66 | 68 | let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} ")); |
67 | | - let method = methods |
| 69 | + |
| 70 | + let fn_items: Vec<ast::AssocItem> = methods |
68 | 71 | .iter() |
69 | | - .map(|Method { pattern_suffix, fn_name, variant_name }| { |
70 | | - format!( |
71 | | - " \ |
72 | | - /// Returns `true` if the {enum_lowercase_name} is [`{variant_name}`]. |
73 | | - /// |
74 | | - /// [`{variant_name}`]: {enum_name}::{variant_name} |
75 | | - #[must_use] |
76 | | - {vis}fn {fn_name}(&self) -> bool {{ |
77 | | - matches!(self, Self::{variant_name}{pattern_suffix}) |
78 | | - }}", |
79 | | - ) |
80 | | - }) |
81 | | - .join("\n\n"); |
82 | | - |
83 | | - add_method_to_adt(builder, &parent_enum, impl_def, &method); |
| 72 | + .map(|method| build_fn_item(method, &enum_lowercase_name, &enum_name, &vis)) |
| 73 | + .collect(); |
| 74 | + |
| 75 | + if let Some(impl_def) = &impl_def { |
| 76 | + let editor = builder.make_editor(impl_def.syntax()); |
| 77 | + impl_def.assoc_item_list().unwrap().add_items(&editor, fn_items); |
| 78 | + builder.add_file_edits(ctx.vfs_file_id(), editor); |
| 79 | + return; |
| 80 | + } |
| 81 | + |
| 82 | + let editor = builder.make_editor(parent_enum.syntax()); |
| 83 | + let make = editor.make(); |
| 84 | + let indent = parent_enum.indent_level(); |
| 85 | + let assoc_list = make.assoc_item_list(fn_items); |
| 86 | + let new_impl = generate_impl_with_item(make, &parent_enum, Some(assoc_list)); |
| 87 | + editor.insert_all( |
| 88 | + Position::after(parent_enum.syntax()), |
| 89 | + vec![ |
| 90 | + make.whitespace(&format!("\n\n{indent}")).into(), |
| 91 | + new_impl.syntax().clone().into(), |
| 92 | + ], |
| 93 | + ); |
| 94 | + builder.add_file_edits(ctx.vfs_file_id(), editor); |
84 | 95 | }, |
85 | 96 | ) |
86 | 97 | } |
87 | 98 |
|
| 99 | +fn build_fn_item( |
| 100 | + method: &Method, |
| 101 | + enum_lowercase_name: &str, |
| 102 | + enum_name: &ast::Name, |
| 103 | + vis: &str, |
| 104 | +) -> ast::AssocItem { |
| 105 | + let Method { pattern_suffix, fn_name, variant_name } = method; |
| 106 | + let fn_text = format!( |
| 107 | + "/// Returns `true` if the {enum_lowercase_name} is [`{variant_name}`]. |
| 108 | +/// |
| 109 | +/// [`{variant_name}`]: {enum_name}::{variant_name} |
| 110 | +#[must_use] |
| 111 | +{vis}fn {fn_name}(&self) -> bool {{ |
| 112 | + matches!(self, Self::{variant_name}{pattern_suffix}) |
| 113 | +}}" |
| 114 | + ); |
| 115 | + let wrapped = format!("impl X {{ {fn_text} }}"); |
| 116 | + let parse = syntax::SourceFile::parse(&wrapped, Edition::CURRENT); |
| 117 | + let fn_ = parse |
| 118 | + .tree() |
| 119 | + .syntax() |
| 120 | + .descendants() |
| 121 | + .find_map(ast::Fn::cast) |
| 122 | + .expect("fn text must produce a valid fn node"); |
| 123 | + ast::AssocItem::Fn(fn_.indent(1.into())) |
| 124 | +} |
| 125 | + |
88 | 126 | struct Method { |
89 | 127 | pattern_suffix: &'static str, |
90 | 128 | fn_name: String, |
|
0 commit comments