Skip to content

Commit 6ef7f8e

Browse files
authored
Merge pull request rust-lang#18483 from tareknaser/syntax_factory_introduce_named_generic
Migrate `introduce_named_generic` Assist to Use `SyntaxFactory`
2 parents f31547d + 0a99a9f commit 6ef7f8e

File tree

4 files changed

+123
-27
lines changed

4 files changed

+123
-27
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
use ide_db::syntax_helpers::suggest_name;
22
use itertools::Itertools;
3-
use syntax::{
4-
ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams, HasName},
5-
ted,
6-
};
3+
use syntax::ast::{self, syntax_factory::SyntaxFactory, AstNode, HasGenericParams, HasName};
74

85
use crate::{AssistContext, AssistId, AssistKind, Assists};
96

@@ -25,42 +22,42 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>
2522

2623
let type_bound_list = impl_trait_type.type_bound_list()?;
2724

25+
let make = SyntaxFactory::new();
2826
let target = fn_.syntax().text_range();
2927
acc.add(
3028
AssistId("introduce_named_generic", AssistKind::RefactorRewrite),
3129
"Replace impl trait with generic",
3230
target,
33-
|edit| {
34-
let impl_trait_type = edit.make_mut(impl_trait_type);
35-
let fn_ = edit.make_mut(fn_);
36-
let fn_generic_param_list = fn_.get_or_create_generic_param_list();
37-
38-
let existing_names = fn_generic_param_list
39-
.generic_params()
40-
.flat_map(|param| match param {
41-
ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()),
42-
p => Some(p.to_string()),
43-
})
44-
.collect_vec();
31+
|builder| {
32+
let mut editor = builder.make_editor(fn_.syntax());
33+
34+
let existing_names = match fn_.generic_param_list() {
35+
Some(generic_param_list) => generic_param_list
36+
.generic_params()
37+
.flat_map(|param| match param {
38+
ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()),
39+
p => Some(p.to_string()),
40+
})
41+
.collect_vec(),
42+
None => Vec::new(),
43+
};
4544
let type_param_name = suggest_name::NameGenerator::new_with_names(
4645
existing_names.iter().map(|s| s.as_str()),
4746
)
4847
.for_impl_trait_as_generic(&impl_trait_type);
4948

50-
let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
51-
.clone_for_update();
52-
let new_ty = make::ty(&type_param_name).clone_for_update();
49+
let type_param = make.type_param(make.name(&type_param_name), Some(type_bound_list));
50+
let new_ty = make.ty(&type_param_name);
5351

54-
ted::replace(impl_trait_type.syntax(), new_ty.syntax());
55-
fn_generic_param_list.add_generic_param(type_param.into());
52+
editor.replace(impl_trait_type.syntax(), new_ty.syntax());
53+
editor.add_generic_param(&fn_, type_param.clone().into());
5654

5755
if let Some(cap) = ctx.config.snippet_cap {
58-
if let Some(generic_param) =
59-
fn_.generic_param_list().and_then(|it| it.generic_params().last())
60-
{
61-
edit.add_tabstop_before(cap, generic_param);
62-
}
56+
editor.add_annotation(type_param.syntax(), builder.make_tabstop_before(cap));
6357
}
58+
59+
editor.add_mappings(make.finish_with_mappings());
60+
builder.add_file_edits(ctx.file_id(), editor);
6461
},
6562
)
6663
}

src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use itertools::Itertools;
33

44
use crate::{
5-
ast::{self, make, HasName},
5+
ast::{self, make, HasName, HasTypeBounds},
66
syntax_editor::SyntaxMappingBuilder,
77
AstNode,
88
};
@@ -14,6 +14,32 @@ impl SyntaxFactory {
1414
make::name(name).clone_for_update()
1515
}
1616

17+
pub fn ty(&self, text: &str) -> ast::Type {
18+
make::ty(text).clone_for_update()
19+
}
20+
21+
pub fn type_param(
22+
&self,
23+
name: ast::Name,
24+
bounds: Option<ast::TypeBoundList>,
25+
) -> ast::TypeParam {
26+
let ast = make::type_param(name.clone(), bounds.clone()).clone_for_update();
27+
28+
if let Some(mut mapping) = self.mappings() {
29+
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
30+
builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
31+
if let Some(input) = bounds {
32+
builder.map_node(
33+
input.syntax().clone(),
34+
ast.type_bound_list().unwrap().syntax().clone(),
35+
);
36+
}
37+
builder.finish(&mut mapping);
38+
}
39+
40+
ast
41+
}
42+
1743
pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
1844
let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update();
1945

src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_hash::FxHashMap;
1616
use crate::{SyntaxElement, SyntaxNode, SyntaxToken};
1717

1818
mod edit_algo;
19+
mod edits;
1920
mod mapping;
2021

2122
pub use mapping::{SyntaxMapping, SyntaxMappingBuilder};
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//! Structural editing for ast using `SyntaxEditor`
2+
3+
use crate::{
4+
ast::make, ast::AstNode, ast::Fn, ast::GenericParam, ast::HasGenericParams, ast::HasName,
5+
syntax_editor::Position, syntax_editor::SyntaxEditor, SyntaxKind,
6+
};
7+
8+
impl SyntaxEditor {
9+
/// Adds a new generic param to the function using `SyntaxEditor`
10+
pub fn add_generic_param(&mut self, function: &Fn, new_param: GenericParam) {
11+
match function.generic_param_list() {
12+
Some(generic_param_list) => match generic_param_list.generic_params().last() {
13+
Some(last_param) => {
14+
// There exists a generic param list and it's not empty
15+
let position = generic_param_list.r_angle_token().map_or_else(
16+
|| Position::last_child_of(function.syntax()),
17+
Position::before,
18+
);
19+
20+
if last_param
21+
.syntax()
22+
.next_sibling_or_token()
23+
.map_or(false, |it| it.kind() == SyntaxKind::COMMA)
24+
{
25+
self.insert(
26+
Position::after(last_param.syntax()),
27+
new_param.syntax().clone(),
28+
);
29+
self.insert(
30+
Position::after(last_param.syntax()),
31+
make::token(SyntaxKind::WHITESPACE),
32+
);
33+
self.insert(
34+
Position::after(last_param.syntax()),
35+
make::token(SyntaxKind::COMMA),
36+
);
37+
} else {
38+
let elements = vec![
39+
make::token(SyntaxKind::COMMA).into(),
40+
make::token(SyntaxKind::WHITESPACE).into(),
41+
new_param.syntax().clone().into(),
42+
];
43+
self.insert_all(position, elements);
44+
}
45+
}
46+
None => {
47+
// There exists a generic param list but it's empty
48+
let position = Position::after(generic_param_list.l_angle_token().unwrap());
49+
self.insert(position, new_param.syntax());
50+
}
51+
},
52+
None => {
53+
// There was no generic param list
54+
let position = if let Some(name) = function.name() {
55+
Position::after(name.syntax)
56+
} else if let Some(fn_token) = function.fn_token() {
57+
Position::after(fn_token)
58+
} else if let Some(param_list) = function.param_list() {
59+
Position::before(param_list.syntax)
60+
} else {
61+
Position::last_child_of(function.syntax())
62+
};
63+
let elements = vec![
64+
make::token(SyntaxKind::L_ANGLE).into(),
65+
new_param.syntax().clone().into(),
66+
make::token(SyntaxKind::R_ANGLE).into(),
67+
];
68+
self.insert_all(position, elements);
69+
}
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)