1 use rustc_hash::FxHashMap;
3 use syntax::{algo, ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode};
5 use crate::{AssistContext, AssistId, AssistKind, Assists};
7 // Assist: reorder_fields
9 // Reorder the fields of record literals and record patterns in the same order as in
13 // struct Foo {foo: i32, bar: i32};
14 // const test: Foo = $0Foo {bar: 0, foo: 1}
18 // struct Foo {foo: i32, bar: i32};
19 // const test: Foo = Foo {foo: 1, bar: 0}
22 pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
24 .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()))?;
28 let path = record.children().find_map(ast::Path::cast)?;
30 let ranks = compute_fields_ranks(&path, &ctx)?;
32 let fields: Vec<SyntaxNode> = {
33 let field_kind = match record.kind() {
34 RECORD_EXPR => RECORD_EXPR_FIELD,
35 RECORD_PAT => RECORD_PAT_FIELD,
41 record.children().flat_map(|n| n.children()).filter(|n| n.kind() == field_kind).collect()
45 let mut fields = fields.clone();
46 fields.sort_by_key(|node| *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value()));
50 if sorted_fields == fields {
51 cov_mark::hit!(reorder_sorted_fields);
55 let target = record.text_range();
57 AssistId("reorder_fields", AssistKind::RefactorRewrite),
58 "Reorder record fields",
61 let mut rewriter = algo::SyntaxRewriter::default();
62 for (old, new) in fields.iter().zip(&sorted_fields) {
63 rewriter.replace(old, new);
65 edit.rewrite(rewriter);
70 fn get_field_name(node: &SyntaxNode) -> String {
71 let res = match_ast! {
73 ast::RecordExprField(field) => field.field_name().map(|it| it.to_string()),
74 ast::RecordPatField(field) => field.field_name().map(|it| it.to_string()),
78 res.unwrap_or_default()
81 fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
82 let strukt = match ctx.sema.resolve_path(path) {
83 Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(it)))) => it,
91 .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
99 use crate::tests::{check_assist, check_assist_not_applicable};
104 fn reorder_sorted_fields() {
105 cov_mark::check!(reorder_sorted_fields);
106 check_assist_not_applicable(
109 struct Foo { foo: i32, bar: i32 }
110 const test: Foo = $0Foo { foo: 0, bar: 0 };
116 fn trivial_empty_fields() {
117 check_assist_not_applicable(
121 const test: Foo = $0Foo {};
127 fn reorder_struct_fields() {
131 struct Foo { foo: i32, bar: i32 }
132 const test: Foo = $0Foo { bar: 0, foo: 1 };
135 struct Foo { foo: i32, bar: i32 }
136 const test: Foo = Foo { foo: 1, bar: 0 };
142 fn reorder_struct_pattern() {
146 struct Foo { foo: i64, bar: i64, baz: i64 }
150 $0Foo { baz: 0, ref mut bar, .. } => (),
156 struct Foo { foo: i64, bar: i64, baz: i64 }
160 Foo { ref mut bar, baz: 0, .. } => (),
169 fn reorder_with_extra_field() {
173 struct Foo { foo: String, bar: String }
177 let foo = String::new();
180 extra: "Extra field",
187 struct Foo { foo: String, bar: String }
191 let foo = String::new();
195 extra: "Extra field",