diff options
Diffstat (limited to 'crates/ra_assists/src/handlers')
-rw-r--r-- | crates/ra_assists/src/handlers/reorder_fields.rs | 86 |
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 | ||
5 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; | 5 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::ast::{Name, Pat}; | ||
8 | use ra_syntax::{ | 7 | use 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 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | assist_ctx::{Assist, AssistCtx}, | 14 | assist_ctx::{Assist, AssistCtx}, |
16 | AssistId, | 15 | AssistId, |
17 | }; | 16 | }; |
17 | use ra_syntax::ast::{Expr, NameRef}; | ||
18 | 18 | ||
19 | pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> { | 19 | pub(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 | ||
23 | fn reorder_struct(ctx: AssistCtx) -> Option<Assist> { | 23 | fn 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 | |||
28 | fn 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 | |||
35 | fn 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 | ||
40 | fn 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 | ||
44 | fn 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 | ||
67 | fn get_fields<R: AstNode, F: AstNode>(record: &R) -> Vec<F> { | 46 | fn 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 | |||
55 | fn 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 | |||
71 | fn 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 | ||
71 | fn sort_by_rank<F: AstNode + Clone>(fields: &[F], get_rank: impl FnMut(&F) -> usize) -> Vec<F> { | 76 | fn 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 | ||