Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit eb6d6ba

Browse files
committed
Migrate generate_trait_from_impl to mutable ast
1 parent 115646d commit eb6d6ba

File tree

3 files changed

+49
-61
lines changed

3 files changed

+49
-61
lines changed

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

Lines changed: 46 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
use crate::assist_context::{AssistContext, Assists};
22
use ide_db::assists::AssistId;
33
use syntax::{
4-
ast::{self, edit::IndentLevel, make, HasGenericParams, HasVisibility},
5-
ted, AstNode, SyntaxKind,
4+
ast::{
5+
self,
6+
edit_in_place::{HasVisibilityEdit, Indent},
7+
make, HasGenericParams, HasName,
8+
},
9+
ted::{self, Position},
10+
AstNode, SyntaxKind, T,
611
};
712

813
// NOTES :
@@ -44,7 +49,7 @@ use syntax::{
4449
// };
4550
// }
4651
//
47-
// trait ${0:TraitName}<const N: usize> {
52+
// trait ${0:NewTrait}<const N: usize> {
4853
// // Used as an associated constant.
4954
// const CONST_ASSOC: usize = N * 4;
5055
//
@@ -53,7 +58,7 @@ use syntax::{
5358
// const_maker! {i32, 7}
5459
// }
5560
//
56-
// impl<const N: usize> ${0:TraitName}<N> for Foo<N> {
61+
// impl<const N: usize> ${0:NewTrait}<N> for Foo<N> {
5762
// // Used as an associated constant.
5863
// const CONST_ASSOC: usize = N * 4;
5964
//
@@ -94,8 +99,10 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
9499
"Generate trait from impl",
95100
impl_ast.syntax().text_range(),
96101
|builder| {
102+
let impl_ast = builder.make_mut(impl_ast);
97103
let trait_items = assoc_items.clone_for_update();
98-
let impl_items = assoc_items.clone_for_update();
104+
let impl_items = builder.make_mut(assoc_items);
105+
let impl_name = builder.make_mut(impl_name);
99106

100107
trait_items.assoc_items().for_each(|item| {
101108
strip_body(&item);
@@ -112,46 +119,42 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
112119
impl_ast.generic_param_list(),
113120
impl_ast.where_clause(),
114121
trait_items,
115-
);
122+
)
123+
.clone_for_update();
124+
125+
let trait_name = trait_ast.name().expect("new trait should have a name");
126+
let trait_name_ref = make::name_ref(&trait_name.to_string()).clone_for_update();
116127

117128
// Change `impl Foo` to `impl NewTrait for Foo`
118-
let arg_list = if let Some(genpars) = impl_ast.generic_param_list() {
119-
genpars.to_generic_args().to_string()
120-
} else {
121-
"".to_owned()
122-
};
123-
124-
if let Some(snippet_cap) = ctx.config.snippet_cap {
125-
builder.replace_snippet(
126-
snippet_cap,
127-
impl_name.syntax().text_range(),
128-
format!("${{0:TraitName}}{} for {}", arg_list, impl_name),
129-
);
129+
let mut elements = vec![
130+
trait_name_ref.syntax().clone().into(),
131+
make::tokens::single_space().into(),
132+
make::token(T![for]).into(),
133+
];
134+
135+
if let Some(params) = impl_ast.generic_param_list() {
136+
let gen_args = &params.to_generic_args().clone_for_update();
137+
elements.insert(1, gen_args.syntax().clone().into());
138+
}
130139

131-
// Insert trait before TraitImpl
132-
builder.insert_snippet(
133-
snippet_cap,
134-
impl_ast.syntax().text_range().start(),
135-
format!(
136-
"{}\n\n{}",
137-
trait_ast.to_string().replace("NewTrait", "${0:TraitName}"),
138-
IndentLevel::from_node(impl_ast.syntax())
139-
),
140-
);
141-
} else {
142-
builder.replace(
143-
impl_name.syntax().text_range(),
144-
format!("NewTrait{} for {}", arg_list, impl_name),
145-
);
140+
ted::insert_all(Position::before(impl_name.syntax()), elements);
141+
142+
// Insert trait before TraitImpl
143+
ted::insert_all_raw(
144+
Position::before(impl_ast.syntax()),
145+
vec![
146+
trait_ast.syntax().clone().into(),
147+
make::tokens::whitespace(&format!("\n\n{}", impl_ast.indent_level())).into(),
148+
],
149+
);
146150

147-
// Insert trait before TraitImpl
148-
builder.insert(
149-
impl_ast.syntax().text_range().start(),
150-
format!("{}\n\n{}", trait_ast, IndentLevel::from_node(impl_ast.syntax())),
151+
// Link the trait name & trait ref names together as a placeholder snippet group
152+
if let Some(cap) = ctx.config.snippet_cap {
153+
builder.add_placeholder_snippet_group(
154+
cap,
155+
vec![trait_name.syntax().clone(), trait_name_ref.syntax().clone()],
151156
);
152157
}
153-
154-
builder.replace(assoc_items.syntax().text_range(), impl_items.to_string());
155158
},
156159
);
157160

@@ -160,23 +163,8 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
160163

161164
/// `E0449` Trait items always share the visibility of their trait
162165
fn remove_items_visibility(item: &ast::AssocItem) {
163-
match item {
164-
ast::AssocItem::Const(c) => {
165-
if let Some(vis) = c.visibility() {
166-
ted::remove(vis.syntax());
167-
}
168-
}
169-
ast::AssocItem::Fn(f) => {
170-
if let Some(vis) = f.visibility() {
171-
ted::remove(vis.syntax());
172-
}
173-
}
174-
ast::AssocItem::TypeAlias(t) => {
175-
if let Some(vis) = t.visibility() {
176-
ted::remove(vis.syntax());
177-
}
178-
}
179-
_ => (),
166+
if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) {
167+
has_vis.set_visibility(None);
180168
}
181169
}
182170

@@ -404,12 +392,12 @@ impl<const N: usize> F$0oo<N> {
404392
r#"
405393
struct Foo<const N: usize>([i32; N]);
406394
407-
trait ${0:TraitName}<const N: usize> {
395+
trait ${0:NewTrait}<const N: usize> {
408396
// Used as an associated constant.
409397
const CONST: usize = N * 4;
410398
}
411399
412-
impl<const N: usize> ${0:TraitName}<N> for Foo<N> {
400+
impl<const N: usize> ${0:NewTrait}<N> for Foo<N> {
413401
// Used as an associated constant.
414402
const CONST: usize = N * 4;
415403
}

crates/ide-assists/src/tests/generated.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,7 +1665,7 @@ macro_rules! const_maker {
16651665
};
16661666
}
16671667
1668-
trait ${0:TraitName}<const N: usize> {
1668+
trait ${0:NewTrait}<const N: usize> {
16691669
// Used as an associated constant.
16701670
const CONST_ASSOC: usize = N * 4;
16711671
@@ -1674,7 +1674,7 @@ trait ${0:TraitName}<const N: usize> {
16741674
const_maker! {i32, 7}
16751675
}
16761676
1677-
impl<const N: usize> ${0:TraitName}<N> for Foo<N> {
1677+
impl<const N: usize> ${0:NewTrait}<N> for Foo<N> {
16781678
// Used as an associated constant.
16791679
const CONST_ASSOC: usize = N * 4;
16801680

crates/syntax/src/ast/make.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ pub mod tokens {
11471147

11481148
pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
11491149
SourceFile::parse(
1150-
"const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let a @ [] })\n;\n\n",
1150+
"const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let a @ [] })\n;\n\nimpl A for B where: {}",
11511151
)
11521152
});
11531153

0 commit comments

Comments
 (0)