Merge pull request #20371 from Hmikihiro/migrate_generate_trait_from_impl

Migrate `generate_trait_from_impl` assist to use `SyntaxEditor`
This commit is contained in:
Lukas Wirth 2025-08-03 07:23:07 +00:00 committed by GitHub
commit 531a02c824
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2,12 +2,8 @@ use crate::assist_context::{AssistContext, Assists};
use ide_db::assists::AssistId; use ide_db::assists::AssistId;
use syntax::{ use syntax::{
AstNode, SyntaxKind, T, AstNode, SyntaxKind, T,
ast::{ ast::{self, HasGenericParams, HasName, HasVisibility, edit_in_place::Indent, make},
self, HasGenericParams, HasName, HasVisibility, syntax_editor::{Position, SyntaxEditor},
edit_in_place::{HasVisibilityEdit, Indent},
make,
},
ted::{self, Position},
}; };
// NOTES : // NOTES :
@ -88,8 +84,8 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
return None; return None;
} }
let assoc_items = impl_ast.assoc_item_list()?; let impl_assoc_items = impl_ast.assoc_item_list()?;
let first_element = assoc_items.assoc_items().next(); let first_element = impl_assoc_items.assoc_items().next();
first_element.as_ref()?; first_element.as_ref()?;
let impl_name = impl_ast.self_ty()?; let impl_name = impl_ast.self_ty()?;
@ -99,20 +95,16 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
"Generate trait from impl", "Generate trait from impl",
impl_ast.syntax().text_range(), impl_ast.syntax().text_range(),
|builder| { |builder| {
let impl_ast = builder.make_mut(impl_ast); let trait_items: ast::AssocItemList = {
let trait_items = assoc_items.clone_for_update(); let trait_items = impl_assoc_items.clone_subtree();
let impl_items = builder.make_mut(assoc_items); let mut trait_items_editor = SyntaxEditor::new(trait_items.syntax().clone());
let impl_name = builder.make_mut(impl_name);
trait_items.assoc_items().for_each(|item| { trait_items.assoc_items().for_each(|item| {
strip_body(&item); strip_body(&mut trait_items_editor, &item);
remove_items_visibility(&item); remove_items_visibility(&mut trait_items_editor, &item);
}); });
ast::AssocItemList::cast(trait_items_editor.finish().new_root().clone()).unwrap()
impl_items.assoc_items().for_each(|item| { };
remove_items_visibility(&item);
});
let trait_ast = make::trait_( let trait_ast = make::trait_(
false, false,
"NewTrait", "NewTrait",
@ -130,6 +122,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
trait_name_ref.syntax().clone().into(), trait_name_ref.syntax().clone().into(),
make::tokens::single_space().into(), make::tokens::single_space().into(),
make::token(T![for]).into(), make::token(T![for]).into(),
make::tokens::single_space().into(),
]; ];
if let Some(params) = impl_ast.generic_param_list() { if let Some(params) = impl_ast.generic_param_list() {
@ -137,10 +130,15 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
elements.insert(1, gen_args.syntax().clone().into()); elements.insert(1, gen_args.syntax().clone().into());
} }
ted::insert_all(Position::before(impl_name.syntax()), elements); let mut editor = builder.make_editor(impl_ast.syntax());
impl_assoc_items.assoc_items().for_each(|item| {
remove_items_visibility(&mut editor, &item);
});
editor.insert_all(Position::before(impl_name.syntax()), elements);
// Insert trait before TraitImpl // Insert trait before TraitImpl
ted::insert_all_raw( editor.insert_all(
Position::before(impl_ast.syntax()), Position::before(impl_ast.syntax()),
vec![ vec![
trait_ast.syntax().clone().into(), trait_ast.syntax().clone().into(),
@ -150,11 +148,12 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
// Link the trait name & trait ref names together as a placeholder snippet group // Link the trait name & trait ref names together as a placeholder snippet group
if let Some(cap) = ctx.config.snippet_cap { if let Some(cap) = ctx.config.snippet_cap {
builder.add_placeholder_snippet_group( let placeholder = builder.make_placeholder_snippet(cap);
cap, editor.add_annotation(trait_name.syntax(), placeholder);
vec![trait_name.syntax().clone(), trait_name_ref.syntax().clone()], editor.add_annotation(trait_name_ref.syntax(), placeholder);
);
} }
builder.add_file_edits(ctx.vfs_file_id(), editor);
}, },
); );
@ -162,19 +161,21 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
} }
/// `E0449` Trait items always share the visibility of their trait /// `E0449` Trait items always share the visibility of their trait
fn remove_items_visibility(item: &ast::AssocItem) { fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) { if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) {
if let Some(vis) = has_vis.visibility() if let Some(vis) = has_vis.visibility()
&& let Some(token) = vis.syntax().next_sibling_or_token() && let Some(token) = vis.syntax().next_sibling_or_token()
&& token.kind() == SyntaxKind::WHITESPACE && token.kind() == SyntaxKind::WHITESPACE
{ {
ted::remove(token); editor.delete(token);
}
if let Some(vis) = has_vis.visibility() {
editor.delete(vis.syntax());
} }
has_vis.set_visibility(None);
} }
} }
fn strip_body(item: &ast::AssocItem) { fn strip_body(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
if let ast::AssocItem::Fn(f) = item if let ast::AssocItem::Fn(f) = item
&& let Some(body) = f.body() && let Some(body) = f.body()
{ {
@ -183,10 +184,10 @@ fn strip_body(item: &ast::AssocItem) {
if let Some(prev) = body.syntax().prev_sibling_or_token() if let Some(prev) = body.syntax().prev_sibling_or_token()
&& prev.kind() == SyntaxKind::WHITESPACE && prev.kind() == SyntaxKind::WHITESPACE
{ {
ted::remove(prev); editor.delete(prev);
} }
ted::replace(body.syntax(), make::tokens::semicolon()); editor.replace(body.syntax(), make::tokens::semicolon());
}; };
} }