aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs112
1 files changed, 45 insertions, 67 deletions
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index 794c89323..383ca6c47 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -1,9 +1,6 @@
1use itertools::Itertools;
2use rustc_hash::FxHashMap; 1use rustc_hash::FxHashMap;
3 2
4use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; 3use syntax::{algo, ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode};
5use ide_db::RootDatabase;
6use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
7 4
8use crate::{AssistContext, AssistId, AssistKind, Assists}; 5use crate::{AssistContext, AssistId, AssistKind, Assists};
9 6
@@ -23,26 +20,39 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
23// ``` 20// ```
24// 21//
25pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 22pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 reorder::<ast::RecordExpr>(acc, ctx).or_else(|| reorder::<ast::RecordPat>(acc, ctx)) 23 let record = ctx
27} 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 27
29fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 28 let path = record.children().find_map(ast::Path::cast)?;
30 let record = ctx.find_node_at_offset::<R>()?;
31 let path = record.syntax().children().find_map(ast::Path::cast)?;
32 29
33 let ranks = compute_fields_ranks(&path, &ctx)?; 30 let ranks = compute_fields_ranks(&path, &ctx)?;
34 31
35 let fields = get_fields(&record.syntax()); 32 let fields: Vec<SyntaxNode> = {
36 let sorted_fields = sorted_by_rank(&fields, |node| { 33 let field_kind = match record.kind() {
37 *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value()) 34 RECORD_EXPR => RECORD_EXPR_FIELD,
38 }); 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()
42 };
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
48 };
39 49
40 if sorted_fields == fields { 50 if sorted_fields == fields {
41 cov_mark::hit!(reorder_sorted_fields); 51 cov_mark::hit!(reorder_sorted_fields);
42 return None; 52 return None;
43 } 53 }
44 54
45 let target = record.syntax().text_range(); 55 let target = record.text_range();
46 acc.add( 56 acc.add(
47 AssistId("reorder_fields", AssistKind::RefactorRewrite), 57 AssistId("reorder_fields", AssistKind::RefactorRewrite),
48 "Reorder record fields", 58 "Reorder record fields",
@@ -57,14 +67,6 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
57 ) 67 )
58} 68}
59 69
60fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
61 match node.kind() {
62 RECORD_EXPR => vec![RECORD_EXPR_FIELD],
63 RECORD_PAT => vec![RECORD_PAT_FIELD, IDENT_PAT],
64 _ => vec![],
65 }
66}
67
68fn get_field_name(node: &SyntaxNode) -> String { 70fn get_field_name(node: &SyntaxNode) -> String {
69 let res = match_ast! { 71 let res = match_ast! {
70 match node { 72 match node {
@@ -76,34 +78,20 @@ fn get_field_name(node: &SyntaxNode) -> String {
76 res.unwrap_or_default() 78 res.unwrap_or_default()
77} 79}
78 80
79fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> { 81fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
80 let kinds = get_fields_kind(record); 82 let strukt = match ctx.sema.resolve_path(path) {
81 record.children().flat_map(|n| n.children()).filter(|n| kinds.contains(&n.kind())).collect() 83 Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(it)))) => it,
82} 84 _ => return None,
83 85 };
84fn sorted_by_rank(
85 fields: &[SyntaxNode],
86 get_rank: impl Fn(&SyntaxNode) -> usize,
87) -> Vec<SyntaxNode> {
88 fields.iter().cloned().sorted_by_key(get_rank).collect()
89}
90 86
91fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<Struct> { 87 let res = strukt
92 match sema.resolve_path(path) { 88 .fields(ctx.db())
93 Some(PathResolution::Def(ModuleDef::Adt(Adt::Struct(s)))) => Some(s), 89 .iter()
94 _ => None, 90 .enumerate()
95 } 91 .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
96} 92 .collect();
97 93
98fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> { 94 Some(res)
99 Some(
100 struct_definition(path, &ctx.sema)?
101 .fields(ctx.db())
102 .iter()
103 .enumerate()
104 .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
105 .collect(),
106 )
107} 95}
108 96
109#[cfg(test)] 97#[cfg(test)]
@@ -118,11 +106,7 @@ mod tests {
118 check_assist_not_applicable( 106 check_assist_not_applicable(
119 reorder_fields, 107 reorder_fields,
120 r#" 108 r#"
121struct Foo { 109struct Foo { foo: i32, bar: i32 }
122 foo: i32,
123 bar: i32,
124}
125
126const test: Foo = $0Foo { foo: 0, bar: 0 }; 110const test: Foo = $0Foo { foo: 0, bar: 0 };
127"#, 111"#,
128 ) 112 )
@@ -133,8 +117,8 @@ const test: Foo = $0Foo { foo: 0, bar: 0 };
133 check_assist_not_applicable( 117 check_assist_not_applicable(
134 reorder_fields, 118 reorder_fields,
135 r#" 119 r#"
136struct Foo {}; 120struct Foo {}
137const test: Foo = $0Foo {} 121const test: Foo = $0Foo {};
138"#, 122"#,
139 ) 123 )
140 } 124 }
@@ -144,12 +128,12 @@ const test: Foo = $0Foo {}
144 check_assist( 128 check_assist(
145 reorder_fields, 129 reorder_fields,
146 r#" 130 r#"
147struct Foo {foo: i32, bar: i32}; 131struct Foo { foo: i32, bar: i32 }
148const test: Foo = $0Foo {bar: 0, foo: 1} 132const test: Foo = $0Foo { bar: 0, foo: 1 };
149"#, 133"#,
150 r#" 134 r#"
151struct Foo {foo: i32, bar: i32}; 135struct Foo { foo: i32, bar: i32 }
152const test: Foo = Foo {foo: 1, bar: 0} 136const test: Foo = Foo { foo: 1, bar: 0 };
153"#, 137"#,
154 ) 138 )
155 } 139 }
@@ -186,10 +170,7 @@ fn f(f: Foo) -> {
186 check_assist( 170 check_assist(
187 reorder_fields, 171 reorder_fields,
188 r#" 172 r#"
189struct Foo { 173struct Foo { foo: String, bar: String }
190 foo: String,
191 bar: String,
192}
193 174
194impl Foo { 175impl Foo {
195 fn new() -> Foo { 176 fn new() -> Foo {
@@ -203,10 +184,7 @@ impl Foo {
203} 184}
204"#, 185"#,
205 r#" 186 r#"
206struct Foo { 187struct Foo { foo: String, bar: String }
207 foo: String,
208 bar: String,
209}
210 188
211impl Foo { 189impl Foo {
212 fn new() -> Foo { 190 fn new() -> Foo {