Skip to content

Commit a3b0349

Browse files
bors[bot]matklad
andauthored
Merge #8781
8781: internal: rewrite **Repalce impl Trait** assist to mutable syntax trees r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
2 parents 0900bee + 5342800 commit a3b0349

File tree

7 files changed

+100
-100
lines changed

7 files changed

+100
-100
lines changed

crates/ide/src/join_lines.rs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::convert::TryFrom;
2+
13
use ide_assists::utils::extract_trivial_expression;
24
use itertools::Itertools;
35
use syntax::{
@@ -65,6 +67,14 @@ fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextR
6567

6668
fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) {
6769
if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 {
70+
let n_spaces_after_line_break = {
71+
let suff = &token.text()[TextRange::new(
72+
offset - token.text_range().start() + TextSize::of('\n'),
73+
TextSize::of(token.text()),
74+
)];
75+
suff.bytes().take_while(|&b| b == b' ').count()
76+
};
77+
6878
let mut no_space = false;
6979
if let Some(string) = ast::String::cast(token.clone()) {
7080
if let Some(range) = string.open_quote_text_range() {
@@ -73,18 +83,13 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
7383
}
7484
if let Some(range) = string.close_quote_text_range() {
7585
cov_mark::hit!(join_string_literal_close_quote);
76-
no_space |= range.start() == offset + TextSize::of('\n');
86+
no_space |= range.start()
87+
== offset
88+
+ TextSize::of('\n')
89+
+ TextSize::try_from(n_spaces_after_line_break).unwrap();
7790
}
7891
}
7992

80-
let n_spaces_after_line_break = {
81-
let suff = &token.text()[TextRange::new(
82-
offset - token.text_range().start() + TextSize::of('\n'),
83-
TextSize::of(token.text()),
84-
)];
85-
suff.bytes().take_while(|&b| b == b' ').count()
86-
};
87-
8893
let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into());
8994
let replace_with = if no_space { "" } else { " " };
9095
edit.replace(range, replace_with.to_string());
@@ -833,6 +838,19 @@ fn main() {
833838
fn main() {
834839
$0"hello";
835840
}
841+
"#,
842+
);
843+
check_join_lines(
844+
r#"
845+
fn main() {
846+
$0r"hello
847+
";
848+
}
849+
"#,
850+
r#"
851+
fn main() {
852+
$0r"hello";
853+
}
836854
"#,
837855
);
838856
}

crates/ide_assists/src/handlers/extract_variable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
5454

5555
let var_name = match &field_shorthand {
5656
Some(it) => it.to_string(),
57-
None => suggest_name::variable(&to_extract, &ctx.sema),
57+
None => suggest_name::for_variable(&to_extract, &ctx.sema),
5858
};
5959
let expr_range = match &field_shorthand {
6060
Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()),

crates/ide_assists/src/handlers/introduce_named_lifetime.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,12 @@ fn generate_unique_lifetime_param_name(
139139

140140
fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) {
141141
let generic_param =
142-
make::generic_param(format!("'{}", new_lifetime_param), None).clone_for_update();
142+
make::generic_param(&format!("'{}", new_lifetime_param), None).clone_for_update();
143143
type_params.add_generic_param(generic_param);
144144
}
145145

146146
fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime {
147-
make::generic_param(format!("'{}", new_lifetime_param), None)
147+
make::generic_param(&format!("'{}", new_lifetime_param), None)
148148
.syntax()
149149
.descendants()
150150
.find_map(ast::Lifetime::cast)
Lines changed: 49 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner};
1+
use syntax::{
2+
ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode},
3+
ted,
4+
};
25

3-
use crate::{AssistContext, AssistId, AssistKind, Assists};
6+
use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
47

58
// Assist: replace_impl_trait_with_generic
69
//
@@ -17,30 +20,29 @@ pub(crate) fn replace_impl_trait_with_generic(
1720
acc: &mut Assists,
1821
ctx: &AssistContext,
1922
) -> Option<()> {
20-
let type_impl_trait = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
21-
let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?;
22-
let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?;
23+
let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
24+
let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?;
25+
let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?;
2326

24-
let impl_trait_ty = type_impl_trait.type_bound_list()?;
27+
let type_bound_list = impl_trait_type.type_bound_list()?;
2528

26-
let target = type_fn.syntax().text_range();
29+
let target = fn_.syntax().text_range();
2730
acc.add(
2831
AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite),
2932
"Replace impl trait with generic",
3033
target,
3134
|edit| {
32-
let generic_letter = impl_trait_ty.to_string().chars().next().unwrap().to_string();
35+
let impl_trait_type = edit.make_ast_mut(impl_trait_type);
36+
let fn_ = edit.make_ast_mut(fn_);
3337

34-
let generic_param_list = type_fn
35-
.generic_param_list()
36-
.unwrap_or_else(|| make::generic_param_list(None))
37-
.append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty)));
38+
let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type);
3839

39-
let new_type_fn = type_fn
40-
.replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter))
41-
.with_generic_param_list(generic_param_list);
40+
let type_param =
41+
make::generic_param(&type_param_name, Some(type_bound_list)).clone_for_update();
42+
let new_ty = make::ty(&type_param_name).clone_for_update();
4243

43-
edit.replace_ast(type_fn.clone(), new_type_fn);
44+
ted::replace(impl_trait_type.syntax(), new_ty.syntax());
45+
fn_.get_or_create_generic_param_list().add_generic_param(type_param)
4446
},
4547
)
4648
}
@@ -55,51 +57,35 @@ mod tests {
5557
fn replace_impl_trait_with_generic_params() {
5658
check_assist(
5759
replace_impl_trait_with_generic,
58-
r#"
59-
fn foo<G>(bar: $0impl Bar) {}
60-
"#,
61-
r#"
62-
fn foo<G, B: Bar>(bar: B) {}
63-
"#,
60+
r#"fn foo<G>(bar: $0impl Bar) {}"#,
61+
r#"fn foo<G, B: Bar>(bar: B) {}"#,
6462
);
6563
}
6664

6765
#[test]
6866
fn replace_impl_trait_without_generic_params() {
6967
check_assist(
7068
replace_impl_trait_with_generic,
71-
r#"
72-
fn foo(bar: $0impl Bar) {}
73-
"#,
74-
r#"
75-
fn foo<B: Bar>(bar: B) {}
76-
"#,
69+
r#"fn foo(bar: $0impl Bar) {}"#,
70+
r#"fn foo<B: Bar>(bar: B) {}"#,
7771
);
7872
}
7973

8074
#[test]
8175
fn replace_two_impl_trait_with_generic_params() {
8276
check_assist(
8377
replace_impl_trait_with_generic,
84-
r#"
85-
fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}
86-
"#,
87-
r#"
88-
fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}
89-
"#,
78+
r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
79+
r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#,
9080
);
9181
}
9282

9383
#[test]
9484
fn replace_impl_trait_with_empty_generic_params() {
9585
check_assist(
9686
replace_impl_trait_with_generic,
97-
r#"
98-
fn foo<>(bar: $0impl Bar) {}
99-
"#,
100-
r#"
101-
fn foo<B: Bar>(bar: B) {}
102-
"#,
87+
r#"fn foo<>(bar: $0impl Bar) {}"#,
88+
r#"fn foo<B: Bar>(bar: B) {}"#,
10389
);
10490
}
10591

@@ -108,13 +94,13 @@ mod tests {
10894
check_assist(
10995
replace_impl_trait_with_generic,
11096
r#"
111-
fn foo<
112-
>(bar: $0impl Bar) {}
113-
"#,
97+
fn foo<
98+
>(bar: $0impl Bar) {}
99+
"#,
114100
r#"
115-
fn foo<B: Bar
116-
>(bar: B) {}
117-
"#,
101+
fn foo<B: Bar
102+
>(bar: B) {}
103+
"#,
118104
);
119105
}
120106

@@ -123,12 +109,8 @@ mod tests {
123109
fn replace_impl_trait_with_exist_generic_letter() {
124110
check_assist(
125111
replace_impl_trait_with_generic,
126-
r#"
127-
fn foo<B>(bar: $0impl Bar) {}
128-
"#,
129-
r#"
130-
fn foo<B, C: Bar>(bar: C) {}
131-
"#,
112+
r#"fn foo<B>(bar: $0impl Bar) {}"#,
113+
r#"fn foo<B, C: Bar>(bar: C) {}"#,
132114
);
133115
}
134116

@@ -137,32 +119,28 @@ mod tests {
137119
check_assist(
138120
replace_impl_trait_with_generic,
139121
r#"
140-
fn foo<
141-
G: Foo,
142-
F,
143-
H,
144-
>(bar: $0impl Bar) {}
145-
"#,
146-
r#"
147-
fn foo<
148-
G: Foo,
149-
F,
150-
H, B: Bar
151-
>(bar: B) {}
152-
"#,
122+
fn foo<
123+
G: Foo,
124+
F,
125+
H,
126+
>(bar: $0impl Bar) {}
127+
"#,
128+
r#"
129+
fn foo<
130+
G: Foo,
131+
F,
132+
H, B: Bar,
133+
>(bar: B) {}
134+
"#,
153135
);
154136
}
155137

156138
#[test]
157139
fn replace_impl_trait_multiple() {
158140
check_assist(
159141
replace_impl_trait_with_generic,
160-
r#"
161-
fn foo(bar: $0impl Foo + Bar) {}
162-
"#,
163-
r#"
164-
fn foo<F: Foo + Bar>(bar: F) {}
165-
"#,
142+
r#"fn foo(bar: $0impl Foo + Bar) {}"#,
143+
r#"fn foo<F: Foo + Bar>(bar: F) {}"#,
166144
);
167145
}
168146
}

crates/ide_assists/src/utils/suggest_name.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use itertools::Itertools;
66
use stdx::to_lower_snake_case;
77
use syntax::{
88
ast::{self, NameOwner},
9-
match_ast, AstNode,
9+
match_ast, AstNode, SmolStr,
1010
};
1111

1212
/// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
@@ -57,6 +57,14 @@ const USELESS_METHODS: &[&str] = &[
5757
"iter_mut",
5858
];
5959

60+
pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr {
61+
let c = ty
62+
.type_bound_list()
63+
.and_then(|bounds| bounds.syntax().text().char_at(0.into()))
64+
.unwrap_or('T');
65+
c.encode_utf8(&mut [0; 4]).into()
66+
}
67+
6068
/// Suggest name of variable for given expression
6169
///
6270
/// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name.
@@ -75,7 +83,8 @@ const USELESS_METHODS: &[&str] = &[
7583
/// It also applies heuristics to filter out less informative names
7684
///
7785
/// Currently it sticks to the first name found.
78-
pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
86+
// FIXME: Microoptimize and return a `SmolStr` here.
87+
pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
7988
// `from_param` does not benifit from stripping
8089
// it need the largest context possible
8190
// so we check firstmost
@@ -276,7 +285,7 @@ mod tests {
276285
frange.range,
277286
"selection is not an expression(yet contained in one)"
278287
);
279-
let name = variable(&expr, &sema);
288+
let name = for_variable(&expr, &sema);
280289
assert_eq!(&name, expected);
281290
}
282291

crates/syntax/src/ast/edit_in_place.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -195,18 +195,13 @@ impl ast::GenericParamList {
195195
pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
196196
match self.generic_params().last() {
197197
Some(last_param) => {
198-
let mut elems = Vec::new();
199-
if !last_param
200-
.syntax()
201-
.siblings_with_tokens(Direction::Next)
202-
.any(|it| it.kind() == T![,])
203-
{
204-
elems.push(make::token(T![,]).into());
205-
elems.push(make::tokens::single_space().into());
206-
};
207-
elems.push(generic_param.syntax().clone().into());
208-
let after_last_param = Position::after(last_param.syntax());
209-
ted::insert_all(after_last_param, elems);
198+
let position = Position::after(last_param.syntax());
199+
let elements = vec![
200+
make::token(T![,]).into(),
201+
make::tokens::single_space().into(),
202+
generic_param.syntax().clone().into(),
203+
];
204+
ted::insert_all(position, elements);
210205
}
211206
None => {
212207
let after_l_angle = Position::after(self.l_angle_token().unwrap());

crates/syntax/src/ast/make.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,8 +475,8 @@ pub fn param_list(
475475
};
476476
ast_from_text(&list)
477477
}
478-
479-
pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam {
478+
// FIXME: s/&str/ast:Name
479+
pub fn generic_param(name: &str, ty: Option<ast::TypeBoundList>) -> ast::GenericParam {
480480
let bound = match ty {
481481
Some(it) => format!(": {}", it),
482482
None => String::new(),

0 commit comments

Comments
 (0)