Skip to content

Commit 4a589b1

Browse files
bors[bot]Veykril
andauthored
Merge #8326
8326: Rewrite reorder fields assist to use mutable syntax trees r=matklad a=Veykril This also instead uses `Either` to use the typed `RecordPat` and `RecordExpr` nodes, this unfortunately gives a bit of code duplication Co-authored-by: Lukas Wirth <[email protected]>
2 parents 9bf7ca5 + df1320d commit 4a589b1

File tree

1 file changed

+50
-39
lines changed

1 file changed

+50
-39
lines changed

crates/ide_assists/src/handlers/reorder_fields.rs

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
use either::Either;
2+
use itertools::Itertools;
13
use rustc_hash::FxHashMap;
24

3-
use syntax::{algo, ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode};
5+
use syntax::{ast, ted, AstNode};
46

57
use crate::{AssistContext, AssistId, AssistKind, Assists};
68

@@ -22,60 +24,70 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
2224
pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
2325
let record = ctx
2426
.find_node_at_offset::<ast::RecordExpr>()
25-
.map(|it| it.syntax().clone())
26-
.or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(|it| it.syntax().clone()))?;
27-
28-
let path = record.children().find_map(ast::Path::cast)?;
27+
.map(Either::Left)
28+
.or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?;
2929

30+
let path = record.as_ref().either(|it| it.path(), |it| it.path())?;
3031
let ranks = compute_fields_ranks(&path, &ctx)?;
32+
let get_rank_of_field =
33+
|of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX);
3134

32-
let fields: Vec<SyntaxNode> = {
33-
let field_kind = match record.kind() {
34-
RECORD_EXPR => RECORD_EXPR_FIELD,
35-
RECORD_PAT => RECORD_PAT_FIELD,
36-
_ => {
37-
stdx::never!();
38-
return None;
39-
}
40-
};
41-
record.children().flat_map(|n| n.children()).filter(|n| n.kind() == field_kind).collect()
35+
let field_list = match &record {
36+
Either::Left(it) => Either::Left(it.record_expr_field_list()?),
37+
Either::Right(it) => Either::Right(it.record_pat_field_list()?),
4238
};
43-
44-
let sorted_fields = {
45-
let mut fields = fields.clone();
46-
fields.sort_by_key(|node| *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value()));
47-
fields
39+
let fields = match field_list {
40+
Either::Left(it) => Either::Left((
41+
it.fields()
42+
.sorted_unstable_by_key(|field| {
43+
get_rank_of_field(field.field_name().map(|it| it.to_string()))
44+
})
45+
.collect::<Vec<_>>(),
46+
it,
47+
)),
48+
Either::Right(it) => Either::Right((
49+
it.fields()
50+
.sorted_unstable_by_key(|field| {
51+
get_rank_of_field(field.field_name().map(|it| it.to_string()))
52+
})
53+
.collect::<Vec<_>>(),
54+
it,
55+
)),
4856
};
4957

50-
if sorted_fields == fields {
58+
let is_sorted = fields.as_ref().either(
59+
|(sorted, field_list)| field_list.fields().zip(sorted).all(|(a, b)| a == *b),
60+
|(sorted, field_list)| field_list.fields().zip(sorted).all(|(a, b)| a == *b),
61+
);
62+
if is_sorted {
5163
cov_mark::hit!(reorder_sorted_fields);
5264
return None;
5365
}
54-
55-
let target = record.text_range();
66+
let target = record.as_ref().either(AstNode::syntax, AstNode::syntax).text_range();
5667
acc.add(
5768
AssistId("reorder_fields", AssistKind::RefactorRewrite),
5869
"Reorder record fields",
5970
target,
60-
|edit| {
61-
let mut rewriter = algo::SyntaxRewriter::default();
62-
for (old, new) in fields.iter().zip(&sorted_fields) {
63-
rewriter.replace(old, new);
71+
|builder| match fields {
72+
Either::Left((sorted, field_list)) => {
73+
replace(builder.make_ast_mut(field_list).fields(), sorted)
74+
}
75+
Either::Right((sorted, field_list)) => {
76+
replace(builder.make_ast_mut(field_list).fields(), sorted)
6477
}
65-
edit.rewrite(rewriter);
6678
},
6779
)
6880
}
6981

70-
fn get_field_name(node: &SyntaxNode) -> String {
71-
let res = match_ast! {
72-
match node {
73-
ast::RecordExprField(field) => field.field_name().map(|it| it.to_string()),
74-
ast::RecordPatField(field) => field.field_name().map(|it| it.to_string()),
75-
_ => None,
76-
}
77-
};
78-
res.unwrap_or_default()
82+
fn replace<T: AstNode + PartialEq>(
83+
fields: impl Iterator<Item = T>,
84+
sorted_fields: impl IntoIterator<Item = T>,
85+
) {
86+
fields.zip(sorted_fields).filter(|(field, sorted)| field != sorted).for_each(
87+
|(field, sorted_field)| {
88+
ted::replace(field.syntax(), sorted_field.syntax().clone_for_update());
89+
},
90+
);
7991
}
8092

8193
fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
@@ -86,7 +98,7 @@ fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashM
8698

8799
let res = strukt
88100
.fields(ctx.db())
89-
.iter()
101+
.into_iter()
90102
.enumerate()
91103
.map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
92104
.collect();
@@ -137,7 +149,6 @@ const test: Foo = Foo { foo: 1, bar: 0 };
137149
"#,
138150
)
139151
}
140-
141152
#[test]
142153
fn reorder_struct_pattern() {
143154
check_assist(

0 commit comments

Comments
 (0)