aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/reorder_fields.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/reorder_fields.rs')
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs86
1 files changed, 47 insertions, 39 deletions
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index 1da85fcec..100e1feb1 100644
--- a/crates/ra_assists/src/handlers/reorder_fields.rs
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -4,71 +4,79 @@ use itertools::Itertools;
4 4
5use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; 5use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
6use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
7use ra_syntax::ast::{Name, Pat};
8use ra_syntax::{ 7use ra_syntax::{
9 ast, 8 algo, ast,
10 ast::{Path, RecordField, RecordLit, RecordPat}, 9 ast::{Name, Path, RecordLit, RecordPat},
11 AstNode, 10 AstNode, SyntaxKind, SyntaxNode,
12}; 11};
13 12
14use crate::{ 13use crate::{
15 assist_ctx::{Assist, AssistCtx}, 14 assist_ctx::{Assist, AssistCtx},
16 AssistId, 15 AssistId,
17}; 16};
17use ra_syntax::ast::{Expr, NameRef};
18 18
19pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> { 19pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> {
20 reorder_struct(ctx.clone()).or_else(|| reorder_struct_pat(ctx)) 20 reorder::<RecordLit>(ctx.clone()).or_else(|| reorder::<RecordPat>(ctx))
21} 21}
22 22
23fn reorder_struct(ctx: AssistCtx) -> Option<Assist> { 23fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
24 let record: RecordLit = ctx.find_node_at_offset()?; 24 let record = ctx.find_node_at_offset::<R>()?;
25 reorder(ctx, &record, &record.path()?, field_name) 25 let path = record.syntax().children().find_map(Path::cast)?;
26}
27
28fn field_name(r: &RecordField) -> String {
29 r.name_ref()
30 .map(|name| name.syntax().text().to_string())
31 .or_else(|| r.expr().map(|e| e.syntax().text().to_string()))
32 .unwrap_or_default()
33}
34
35fn reorder_struct_pat(ctx: AssistCtx) -> Option<Assist> {
36 let record: RecordPat = ctx.find_node_at_offset()?;
37 reorder(ctx, &record, &record.path()?, field_pat_name)
38}
39 26
40fn field_pat_name(field: &Pat) -> String { 27 let ranks = compute_fields_ranks(&path, &ctx)?;
41 field.syntax().children().find_map(Name::cast).map(|n| n.to_string()).unwrap_or_default()
42}
43 28
44fn reorder<R: AstNode, F: AstNode + Eq + Clone>( 29 let fields = get_fields(&record.syntax());
45 ctx: AssistCtx, 30 let sorted_fields = sorted_by_rank(&fields, |node| {
46 record: &R, 31 *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value())
47 path: &Path, 32 });
48 field_name: fn(&F) -> String,
49) -> Option<Assist> {
50 let ranks = compute_fields_ranks(path, &ctx)?;
51 let fields: Vec<F> = get_fields(record);
52 let sorted_fields: Vec<F> =
53 sort_by_rank(&fields, |f| *ranks.get(&field_name(f)).unwrap_or(&usize::max_value()));
54 33
55 if sorted_fields == fields { 34 if sorted_fields == fields {
56 return None; 35 return None;
57 } 36 }
58 37
59 ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", |edit| { 38 ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", |edit| {
60 for (old, new) in fields.into_iter().zip(sorted_fields) { 39 for (old, new) in fields.iter().zip(&sorted_fields) {
61 edit.replace_ast(old, new); 40 algo::diff(old, new).into_text_edit(edit.text_edit_builder());
62 } 41 }
63 edit.target(record.syntax().text_range()) 42 edit.target(record.syntax().text_range())
64 }) 43 })
65} 44}
66 45
67fn get_fields<R: AstNode, F: AstNode>(record: &R) -> Vec<F> { 46fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
68 record.syntax().children().flat_map(|n1| n1.children()).filter_map(|n3| F::cast(n3)).collect() 47 use SyntaxKind::*;
48 match node.kind() {
49 RECORD_LIT => vec![RECORD_FIELD],
50 RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT],
51 _ => vec![],
52 }
53}
54
55fn get_field_name(node: &SyntaxNode) -> String {
56 use SyntaxKind::*;
57 match node.kind() {
58 RECORD_FIELD => {
59 if let Some(name) = node.children().find_map(NameRef::cast) {
60 return name.to_string();
61 }
62 node.children().find_map(Expr::cast).map(|expr| expr.to_string()).unwrap_or_default()
63 }
64 BIND_PAT | RECORD_FIELD_PAT => {
65 node.children().find_map(Name::cast).map(|n| n.to_string()).unwrap_or_default()
66 }
67 _ => String::new(),
68 }
69}
70
71fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> {
72 let kinds = get_fields_kind(record);
73 record.children().flat_map(|n| n.children()).filter(|n| kinds.contains(&n.kind())).collect()
69} 74}
70 75
71fn sort_by_rank<F: AstNode + Clone>(fields: &[F], get_rank: impl FnMut(&F) -> usize) -> Vec<F> { 76fn sorted_by_rank(
77 fields: &[SyntaxNode],
78 get_rank: impl Fn(&SyntaxNode) -> usize,
79) -> Vec<SyntaxNode> {
72 fields.iter().cloned().sorted_by_key(get_rank).collect() 80 fields.iter().cloned().sorted_by_key(get_rank).collect()
73} 81}
74 82