Skip to content

Commit 7cbc5a3

Browse files
authored
Merge pull request #20270 from Hmikihiro/migrate_generate_new
Migrate `generate new` assist to use `SyntaxEditor`
2 parents 9a1ee18 + 9cc03e0 commit 7cbc5a3

File tree

6 files changed

+97
-58
lines changed

6 files changed

+97
-58
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
9494
})?;
9595
let _ = process_ref_mut(&fn_);
9696

97-
let assoc_list = make::assoc_item_list().clone_for_update();
97+
let assoc_list = make::assoc_item_list(None).clone_for_update();
9898
ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
9999
impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_));
100100

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

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use ide_db::{
44
};
55
use syntax::{
66
ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make},
7-
ted,
7+
syntax_editor::Position,
88
};
99

1010
use crate::{
1111
AssistContext, AssistId, Assists,
12-
utils::{find_struct_impl, generate_impl},
12+
utils::{find_struct_impl, generate_impl_with_item},
1313
};
1414

1515
// Assist: generate_new
@@ -149,7 +149,53 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
149149
.clone_for_update();
150150
fn_.indent(1.into());
151151

152-
if let Some(cap) = ctx.config.snippet_cap {
152+
let mut editor = builder.make_editor(strukt.syntax());
153+
154+
// Get the node for set annotation
155+
let contain_fn = if let Some(impl_def) = impl_def {
156+
fn_.indent(impl_def.indent_level());
157+
158+
if let Some(l_curly) = impl_def.assoc_item_list().and_then(|list| list.l_curly_token())
159+
{
160+
editor.insert_all(
161+
Position::after(l_curly),
162+
vec![
163+
make::tokens::whitespace(&format!("\n{}", impl_def.indent_level() + 1))
164+
.into(),
165+
fn_.syntax().clone().into(),
166+
make::tokens::whitespace("\n").into(),
167+
],
168+
);
169+
fn_.syntax().clone()
170+
} else {
171+
let items = vec![either::Either::Right(ast::AssocItem::Fn(fn_))];
172+
let list = make::assoc_item_list(Some(items));
173+
editor.insert(Position::after(impl_def.syntax()), list.syntax());
174+
list.syntax().clone()
175+
}
176+
} else {
177+
// Generate a new impl to add the method to
178+
let indent_level = strukt.indent_level();
179+
let body = vec![either::Either::Right(ast::AssocItem::Fn(fn_))];
180+
let list = make::assoc_item_list(Some(body));
181+
let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list));
182+
183+
impl_def.indent(strukt.indent_level());
184+
185+
// Insert it after the adt
186+
editor.insert_all(
187+
Position::after(strukt.syntax()),
188+
vec![
189+
make::tokens::whitespace(&format!("\n\n{indent_level}")).into(),
190+
impl_def.syntax().clone().into(),
191+
],
192+
);
193+
impl_def.syntax().clone()
194+
};
195+
196+
if let Some(fn_) = contain_fn.descendants().find_map(ast::Fn::cast)
197+
&& let Some(cap) = ctx.config.snippet_cap
198+
{
153199
match strukt.kind() {
154200
StructKind::Tuple(_) => {
155201
let struct_args = fn_
@@ -168,8 +214,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
168214
for (struct_arg, fn_param) in struct_args.zip(fn_params.params()) {
169215
if let Some(fn_pat) = fn_param.pat() {
170216
let fn_pat = fn_pat.syntax().clone();
171-
builder
172-
.add_placeholder_snippet_group(cap, vec![struct_arg, fn_pat]);
217+
let placeholder = builder.make_placeholder_snippet(cap);
218+
editor.add_annotation_all(vec![struct_arg, fn_pat], placeholder)
173219
}
174220
}
175221
}
@@ -179,36 +225,12 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
179225

180226
// Add a tabstop before the name
181227
if let Some(name) = fn_.name() {
182-
builder.add_tabstop_before(cap, name);
228+
let tabstop_before = builder.make_tabstop_before(cap);
229+
editor.add_annotation(name.syntax(), tabstop_before);
183230
}
184231
}
185232

186-
// Get the mutable version of the impl to modify
187-
let impl_def = if let Some(impl_def) = impl_def {
188-
fn_.indent(impl_def.indent_level());
189-
builder.make_mut(impl_def)
190-
} else {
191-
// Generate a new impl to add the method to
192-
let impl_def = generate_impl(&ast::Adt::Struct(strukt.clone()));
193-
let indent_level = strukt.indent_level();
194-
fn_.indent(indent_level);
195-
196-
// Insert it after the adt
197-
let strukt = builder.make_mut(strukt.clone());
198-
199-
ted::insert_all_raw(
200-
ted::Position::after(strukt.syntax()),
201-
vec![
202-
make::tokens::whitespace(&format!("\n\n{indent_level}")).into(),
203-
impl_def.syntax().clone().into(),
204-
],
205-
);
206-
207-
impl_def
208-
};
209-
210-
// Add the `new` method at the start of the impl
211-
impl_def.get_or_create_assoc_item_list().add_item_at_start(fn_.into());
233+
builder.add_file_edits(ctx.vfs_file_id(), editor);
212234
})
213235
}
214236

crates/ide-assists/src/utils.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -664,30 +664,38 @@ fn generate_impl_text_inner(
664664

665665
/// Generates the corresponding `impl Type {}` including type and lifetime
666666
/// parameters.
667+
pub(crate) fn generate_impl_with_item(
668+
adt: &ast::Adt,
669+
body: Option<ast::AssocItemList>,
670+
) -> ast::Impl {
671+
generate_impl_inner(adt, None, true, body)
672+
}
673+
667674
pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl {
668-
generate_impl_inner(adt, None, true)
675+
generate_impl_inner(adt, None, true, None)
669676
}
670677

671678
/// Generates the corresponding `impl <trait> for Type {}` including type
672679
/// and lifetime parameters, with `<trait>` appended to `impl`'s generic parameters' bounds.
673680
///
674681
/// This is useful for traits like `PartialEq`, since `impl<T> PartialEq for U<T>` often requires `T: PartialEq`.
675682
pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl {
676-
generate_impl_inner(adt, Some(trait_), true)
683+
generate_impl_inner(adt, Some(trait_), true, None)
677684
}
678685

679686
/// Generates the corresponding `impl <trait> for Type {}` including type
680687
/// and lifetime parameters, with `impl`'s generic parameters' bounds kept as-is.
681688
///
682689
/// This is useful for traits like `From<T>`, since `impl<T> From<T> for U<T>` doesn't require `T: From<T>`.
683690
pub(crate) fn generate_trait_impl_intransitive(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl {
684-
generate_impl_inner(adt, Some(trait_), false)
691+
generate_impl_inner(adt, Some(trait_), false, None)
685692
}
686693

687694
fn generate_impl_inner(
688695
adt: &ast::Adt,
689696
trait_: Option<ast::Type>,
690697
trait_is_transitive: bool,
698+
body: Option<ast::AssocItemList>,
691699
) -> ast::Impl {
692700
// Ensure lifetime params are before type & const params
693701
let generic_params = adt.generic_param_list().map(|generic_params| {
@@ -737,9 +745,9 @@ fn generate_impl_inner(
737745
ty,
738746
None,
739747
adt.where_clause(),
740-
None,
748+
body,
741749
),
742-
None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), None),
750+
None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), body),
743751
}
744752
.clone_for_update();
745753

crates/syntax/src/ast/edit_in_place.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ impl Removable for ast::Use {
644644
impl ast::Impl {
645645
pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList {
646646
if self.assoc_item_list().is_none() {
647-
let assoc_item_list = make::assoc_item_list().clone_for_update();
647+
let assoc_item_list = make::assoc_item_list(None).clone_for_update();
648648
ted::append_child(self.syntax(), assoc_item_list.syntax());
649649
}
650650
self.assoc_item_list().unwrap()

crates/syntax/src/ast/make.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,18 @@ pub fn ty_fn_ptr<I: Iterator<Item = Param>>(
229229
}
230230
}
231231

232-
pub fn assoc_item_list() -> ast::AssocItemList {
233-
ast_from_text("impl C for D {}")
232+
pub fn assoc_item_list(
233+
body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
234+
) -> ast::AssocItemList {
235+
let is_break_braces = body.is_some();
236+
let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() };
237+
let body_indent = if is_break_braces { " ".to_owned() } else { String::new() };
238+
239+
let body = match body {
240+
Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
241+
None => String::new(),
242+
};
243+
ast_from_text(&format!("impl C for D {{{body_newline}{body_indent}{body}{body_newline}}}"))
234244
}
235245

236246
fn merge_gen_params(
@@ -273,28 +283,21 @@ pub fn impl_(
273283
generic_args: Option<ast::GenericArgList>,
274284
path_type: ast::Type,
275285
where_clause: Option<ast::WhereClause>,
276-
body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
286+
body: Option<ast::AssocItemList>,
277287
) -> ast::Impl {
278288
let gen_args = generic_args.map_or_else(String::new, |it| it.to_string());
279289

280290
let gen_params = generic_params.map_or_else(String::new, |it| it.to_string());
281291

282292
let body_newline =
283293
if where_clause.is_some() && body.is_none() { "\n".to_owned() } else { String::new() };
284-
285294
let where_clause = match where_clause {
286295
Some(pr) => format!("\n{pr}\n"),
287296
None => " ".to_owned(),
288297
};
289298

290-
let body = match body {
291-
Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
292-
None => String::new(),
293-
};
294-
295-
ast_from_text(&format!(
296-
"impl{gen_params} {path_type}{gen_args}{where_clause}{{{body_newline}{body}}}"
297-
))
299+
let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string());
300+
ast_from_text(&format!("impl{gen_params} {path_type}{gen_args}{where_clause}{body}"))
298301
}
299302

300303
pub fn impl_trait(
@@ -308,7 +311,7 @@ pub fn impl_trait(
308311
ty: ast::Type,
309312
trait_where_clause: Option<ast::WhereClause>,
310313
ty_where_clause: Option<ast::WhereClause>,
311-
body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
314+
body: Option<ast::AssocItemList>,
312315
) -> ast::Impl {
313316
let is_unsafe = if is_unsafe { "unsafe " } else { "" };
314317

@@ -330,13 +333,10 @@ pub fn impl_trait(
330333
let where_clause = merge_where_clause(ty_where_clause, trait_where_clause)
331334
.map_or_else(|| " ".to_owned(), |wc| format!("\n{wc}\n"));
332335

333-
let body = match body {
334-
Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
335-
None => String::new(),
336-
};
336+
let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string());
337337

338338
ast_from_text(&format!(
339-
"{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{{{body_newline}{body}}}"
339+
"{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}"
340340
))
341341
}
342342

crates/syntax/src/syntax_editor.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs
66
77
use std::{
8-
fmt,
8+
fmt, iter,
99
num::NonZeroU32,
1010
ops::RangeInclusive,
1111
sync::atomic::{AtomicU32, Ordering},
@@ -41,6 +41,15 @@ impl SyntaxEditor {
4141
self.annotations.push((element.syntax_element(), annotation))
4242
}
4343

44+
pub fn add_annotation_all(
45+
&mut self,
46+
elements: Vec<impl Element>,
47+
annotation: SyntaxAnnotation,
48+
) {
49+
self.annotations
50+
.extend(elements.into_iter().map(|e| e.syntax_element()).zip(iter::repeat(annotation)));
51+
}
52+
4453
pub fn merge(&mut self, mut other: SyntaxEditor) {
4554
debug_assert!(
4655
self.root == other.root || other.root.ancestors().any(|node| node == self.root),

0 commit comments

Comments
 (0)