diff --git a/crates/assists/src/handlers/reorder_methods_sort.rs b/crates/assists/src/handlers/reorder_methods_sort.rs new file mode 100644 index 000000000000..bc4d6ef9dbff --- /dev/null +++ b/crates/assists/src/handlers/reorder_methods_sort.rs @@ -0,0 +1,242 @@ +use ast::{AssocItemList, Fn}; +use itertools::Itertools; +use rustc_hash::FxHashMap; + +use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; +use ide_db::RootDatabase; +use syntax::{ + algo, + ast::{self, AssocItem, NameOwner}, + match_ast, AstNode, SyntaxKind, + SyntaxKind::*, + SyntaxNode, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: reorder_methods_sort +// +// 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 = <|>Foo {bar: 0, foo: 1} +// ``` +// -> +// ``` +// struct Foo {foo: i32, bar: i32}; +// const test: Foo = Foo {foo: 1, bar: 0} +// ``` +// +pub(crate) fn reorder_methods_sort(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + reorder(acc, ctx) //.or_else(|| reorder::(acc, ctx)) +} + +fn reorder(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let impl_ast = ctx.find_node_at_offset::()?; + + let items = impl_ast.assoc_item_list()?; + + let methods = get_methods(&items); + let sorted: Vec<_> = + methods.iter().cloned().sorted_by_key(|f| f.name().unwrap().text().to_string()).collect(); + + let target = items.syntax().text_range(); + acc.add( + AssistId("reorder_methods_sort", AssistKind::RefactorRewrite), + "Sort impl methods", + target, + |edit| { + let mut rewriter = algo::SyntaxRewriter::default(); + for (old, new) in methods.iter().zip(&sorted) { + rewriter.replace(old.syntax(), new.syntax()); + } + edit.rewrite(rewriter); + }, + ) + + // println!("Fns: {:?}", fns); + // items.syntax().text_range(); + + // let path = record.syntax().children().find_map(ast::Path::cast)?; + + // println!("Path: {}", path); + + // 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 { + // return None; + // } + + // let target = record.syntax().text_range(); + // acc.add( + // AssistId("reorder_methods_sort", 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_methods(items: &AssocItemList) -> Vec { + items + .assoc_items() + .flat_map(|i| match i { + AssocItem::Fn(f) => Some(f), + _ => None, + }) + .filter(|f| f.name().is_some()) + .collect() +} + +struct Foo { + c: usize, +} +impl Foo { + fn ten() -> usize { + 10 + } + fn add(&mut self, b: usize) { + self.c += b + } + fn sub(&mut self, b: usize) { + self.c -= b + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn not_applicable_if_sorted() { + check_assist_not_applicable( + reorder_methods_sort, + r#" + struct Foo { + foo: i32, + bar: i32, + } + + const test: Foo = <|>Foo { foo: 0, bar: 0 }; + "#, + ) + } + + #[test] + fn trivial_empty_impl() { + check_assist_not_applicable( + reorder_methods_sort, + r#" + struct Foo {}; + <|>impl Foo {} + "#, + ) + } + + #[test] + fn reorder_impl_methods() { + check_assist( + reorder_methods_sort, + r#" + struct Foo {c: usize} + + <|>impl Foo { + fn sub(&mut self, b: usize) { self.c -= b } + fn ten() -> usize { 10 } + fn add(&mut self, b: usize) { self.c += b } + } + "#, + r#" + struct Foo {c: usize} + + impl Foo { + fn add(&mut self, b: usize) { self.c += b } + fn sub(&mut self, b: usize) { self.c -= b } + fn ten() -> usize { 10 } + } + "#, + ) + } + + #[test] + fn reorder_struct_pattern() { + check_assist( + reorder_methods_sort, + r#" + struct Foo { foo: i64, bar: i64, baz: i64 } + + fn f(f: Foo) -> { + match f { + <|>Foo { 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_methods_sort, + r#" + struct Foo { + foo: String, + bar: String, + } + + impl Foo { + fn new() -> Foo { + let foo = String::new(); + <|>Foo { + 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", + } + } + } + "#, + ) + } +} diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index b8ce7418d6f7..9a1b47937482 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs @@ -155,6 +155,7 @@ mod handlers { mod remove_mut; mod remove_unused_param; mod reorder_fields; + mod reorder_methods_sort; mod replace_derive_with_manual_impl; mod replace_if_let_with_match; mod replace_impl_trait_with_generic; @@ -207,6 +208,7 @@ mod handlers { remove_mut::remove_mut, remove_unused_param::remove_unused_param, reorder_fields::reorder_fields, + reorder_methods_sort::reorder_methods_sort, replace_derive_with_manual_impl::replace_derive_with_manual_impl, replace_if_let_with_match::replace_if_let_with_match, replace_if_let_with_match::replace_match_with_if_let,