From e4756cb4f6e66097638b9d101589358976be2ba8 Mon Sep 17 00:00:00 2001 From: Chetan Khilosiya Date: Tue, 23 Feb 2021 00:17:48 +0530 Subject: 7526: Rename crate assists to ide_assists. --- crates/ide_assists/src/handlers/reorder_fields.rs | 227 ++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 crates/ide_assists/src/handlers/reorder_fields.rs (limited to 'crates/ide_assists/src/handlers/reorder_fields.rs') diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs new file mode 100644 index 000000000..fba7d6ddb --- /dev/null +++ b/crates/ide_assists/src/handlers/reorder_fields.rs @@ -0,0 +1,227 @@ +use itertools::Itertools; +use rustc_hash::FxHashMap; + +use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; +use ide_db::RootDatabase; +use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; +use test_utils::mark; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: reorder_fields +// +// Reorder the fields of record literals and record patterns in the same order as in +// the definition. +// +// ``` +// struct Foo {foo: i32, bar: i32}; +// const test: Foo = $0Foo {bar: 0, foo: 1} +// ``` +// -> +// ``` +// struct Foo {foo: i32, bar: i32}; +// const test: Foo = Foo {foo: 1, bar: 0} +// ``` +// +pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + reorder::(acc, ctx).or_else(|| reorder::(acc, ctx)) +} + +fn reorder(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let record = ctx.find_node_at_offset::()?; + let path = record.syntax().children().find_map(ast::Path::cast)?; + + let ranks = compute_fields_ranks(&path, &ctx)?; + + let fields = get_fields(&record.syntax()); + let sorted_fields = sorted_by_rank(&fields, |node| { + *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value()) + }); + + if sorted_fields == fields { + mark::hit!(reorder_sorted_fields); + return None; + } + + let target = record.syntax().text_range(); + acc.add( + AssistId("reorder_fields", AssistKind::RefactorRewrite), + "Reorder record fields", + target, + |edit| { + let mut rewriter = algo::SyntaxRewriter::default(); + for (old, new) in fields.iter().zip(&sorted_fields) { + rewriter.replace(old, new); + } + edit.rewrite(rewriter); + }, + ) +} + +fn get_fields_kind(node: &SyntaxNode) -> Vec { + match node.kind() { + RECORD_EXPR => vec![RECORD_EXPR_FIELD], + RECORD_PAT => vec![RECORD_PAT_FIELD, IDENT_PAT], + _ => vec![], + } +} + +fn get_field_name(node: &SyntaxNode) -> String { + let res = match_ast! { + match node { + ast::RecordExprField(field) => field.field_name().map(|it| it.to_string()), + ast::RecordPatField(field) => field.field_name().map(|it| it.to_string()), + _ => None, + } + }; + res.unwrap_or_default() +} + +fn get_fields(record: &SyntaxNode) -> Vec { + let kinds = get_fields_kind(record); + record.children().flat_map(|n| n.children()).filter(|n| kinds.contains(&n.kind())).collect() +} + +fn sorted_by_rank( + fields: &[SyntaxNode], + get_rank: impl Fn(&SyntaxNode) -> usize, +) -> Vec { + fields.iter().cloned().sorted_by_key(get_rank).collect() +} + +fn struct_definition(path: &ast::Path, sema: &Semantics) -> Option { + match sema.resolve_path(path) { + Some(PathResolution::Def(ModuleDef::Adt(Adt::Struct(s)))) => Some(s), + _ => None, + } +} + +fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option> { + Some( + struct_definition(path, &ctx.sema)? + .fields(ctx.db()) + .iter() + .enumerate() + .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx)) + .collect(), + ) +} + +#[cfg(test)] +mod tests { + use test_utils::mark; + + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn reorder_sorted_fields() { + mark::check!(reorder_sorted_fields); + check_assist_not_applicable( + reorder_fields, + r#" +struct Foo { + foo: i32, + bar: i32, +} + +const test: Foo = $0Foo { foo: 0, bar: 0 }; +"#, + ) + } + + #[test] + fn trivial_empty_fields() { + check_assist_not_applicable( + reorder_fields, + r#" +struct Foo {}; +const test: Foo = $0Foo {} +"#, + ) + } + + #[test] + fn reorder_struct_fields() { + check_assist( + reorder_fields, + r#" +struct Foo {foo: i32, bar: i32}; +const test: Foo = $0Foo {bar: 0, foo: 1} +"#, + r#" +struct Foo {foo: i32, bar: i32}; +const test: Foo = Foo {foo: 1, bar: 0} +"#, + ) + } + + #[test] + fn reorder_struct_pattern() { + check_assist( + reorder_fields, + r#" +struct Foo { foo: i64, bar: i64, baz: i64 } + +fn f(f: Foo) -> { + match f { + $0Foo { baz: 0, ref mut bar, .. } => (), + _ => () + } +} +"#, + r#" +struct Foo { foo: i64, bar: i64, baz: i64 } + +fn f(f: Foo) -> { + match f { + Foo { ref mut bar, baz: 0, .. } => (), + _ => () + } +} +"#, + ) + } + + #[test] + fn reorder_with_extra_field() { + check_assist( + reorder_fields, + r#" +struct Foo { + foo: String, + bar: String, +} + +impl Foo { + fn new() -> Foo { + let foo = String::new(); + $0Foo { + bar: foo.clone(), + extra: "Extra field", + foo, + } + } +} +"#, + r#" +struct Foo { + foo: String, + bar: String, +} + +impl Foo { + fn new() -> Foo { + let foo = String::new(); + Foo { + foo, + bar: foo.clone(), + extra: "Extra field", + } + } +} +"#, + ) + } +} -- cgit v1.2.3