Skip to content

Commit 0c04450

Browse files
committed
migrate generate_enum_projection_method to syntax editor
1 parent 1f49c24 commit 0c04450

1 file changed

Lines changed: 61 additions & 31 deletions

File tree

crates/ide-assists/src/handlers/generate_enum_projection_method.rs

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use ide_db::assists::GroupLabel;
2-
use itertools::Itertools;
32
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+
};
68

79
use crate::{
810
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},
1012
};
1113

1214
// Assist: generate_enum_try_into_method
@@ -116,15 +118,6 @@ fn generate_enum_projection_method(
116118
assist_description: &str,
117119
props: ProjectionProps,
118120
) -> Option<()> {
119-
let ProjectionProps {
120-
fn_name_prefix,
121-
self_param,
122-
return_prefix,
123-
return_suffix,
124-
happy_case,
125-
sad_case,
126-
} = props;
127-
128121
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
129122
let parent_enum = ast::Adt::Enum(variant.parent_enum());
130123
let variants = variant
@@ -135,7 +128,7 @@ fn generate_enum_projection_method(
135128
.collect::<Vec<_>>();
136129
let methods = variants
137130
.iter()
138-
.map(|variant| Method::new(variant, fn_name_prefix))
131+
.map(|variant| Method::new(variant, props.fn_name_prefix))
139132
.collect::<Option<Vec<_>>>()?;
140133
let fn_names = methods.iter().map(|it| it.fn_name.clone()).collect::<Vec<_>>();
141134
stdx::never!(variants.is_empty());
@@ -151,30 +144,66 @@ fn generate_enum_projection_method(
151144
target,
152145
|builder| {
153146
let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} "));
147+
let must_use = if ctx.config.assist_emit_must_use { "#[must_use]\n" } else { "" };
154148

155-
let must_use = if ctx.config.assist_emit_must_use { "#[must_use]\n " } else { "" };
156-
157-
let method = methods
149+
let fn_items: Vec<ast::AssocItem> = methods
158150
.iter()
159-
.map(|Method { pattern_suffix, field_type, bound_name, fn_name, variant_name }| {
160-
format!(
161-
" \
162-
{must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
163-
if let Self::{variant_name}{pattern_suffix} = self {{
164-
{happy_case}({bound_name})
165-
}} else {{
166-
{sad_case}
167-
}}
168-
}}"
169-
)
170-
})
171-
.join("\n\n");
151+
.map(|method| build_fn_item(method, &vis, must_use, &props))
152+
.collect();
153+
154+
if let Some(impl_def) = &impl_def {
155+
let editor = builder.make_editor(impl_def.syntax());
156+
impl_def.assoc_item_list().unwrap().add_items(&editor, fn_items);
157+
builder.add_file_edits(ctx.vfs_file_id(), editor);
158+
return;
159+
}
172160

173-
add_method_to_adt(builder, &parent_enum, impl_def, &method);
161+
let editor = builder.make_editor(parent_enum.syntax());
162+
let make = editor.make();
163+
let indent = parent_enum.indent_level();
164+
let assoc_list = make.assoc_item_list(fn_items);
165+
let new_impl = generate_impl_with_item(make, &parent_enum, Some(assoc_list));
166+
editor.insert_all(
167+
Position::after(parent_enum.syntax()),
168+
vec![
169+
make.whitespace(&format!("\n\n{indent}")).into(),
170+
new_impl.syntax().clone().into(),
171+
],
172+
);
173+
builder.add_file_edits(ctx.vfs_file_id(), editor);
174174
},
175175
)
176176
}
177177

178+
fn build_fn_item(
179+
method: &Method,
180+
vis: &str,
181+
must_use: &str,
182+
props: &ProjectionProps,
183+
) -> ast::AssocItem {
184+
let Method { pattern_suffix, field_type, bound_name, fn_name, variant_name } = method;
185+
let ProjectionProps { self_param, return_prefix, return_suffix, happy_case, sad_case, .. } =
186+
props;
187+
let fn_text = format!(
188+
"{must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
189+
if let Self::{variant_name}{pattern_suffix} = self {{
190+
{happy_case}({bound_name})
191+
}} else {{
192+
{sad_case}
193+
}}
194+
}}"
195+
);
196+
let wrapped = format!("impl X {{ {fn_text} }}");
197+
let parse = syntax::SourceFile::parse(&wrapped, Edition::CURRENT);
198+
let fn_ = parse
199+
.tree()
200+
.syntax()
201+
.descendants()
202+
.find_map(ast::Fn::cast)
203+
.expect("fn text must produce a valid fn node");
204+
ast::AssocItem::Fn(fn_.indent(1.into()))
205+
}
206+
178207
struct Method {
179208
pattern_suffix: String,
180209
field_type: ast::Type,
@@ -185,6 +214,7 @@ struct Method {
185214

186215
impl Method {
187216
fn new(variant: &ast::Variant, fn_name_prefix: &str) -> Option<Self> {
217+
use itertools::Itertools as _;
188218
let variant_name = variant.name()?;
189219
let fn_name = format!("{fn_name_prefix}_{}", &to_lower_snake_case(&variant_name.text()));
190220

0 commit comments

Comments
 (0)