1 use std::cmp::Ordering;
3 use itertools::Itertools;
7 ted, AstNode, TextRange,
10 use crate::{utils::get_methods, AssistContext, AssistId, AssistKind, Assists};
14 // Sorts item members alphabetically: fields, enum variants and methods.
17 // struct $0Foo$0 { second: u32, first: String }
21 // struct Foo { first: String, second: u32 }
26 // fn second(&self) -> u32;
27 // fn first(&self) -> String;
33 // fn first(&self) -> String;
34 // fn second(&self) -> u32;
41 // fn second(&self) -> u32;
42 // fn first(&self) -> String;
49 // fn first(&self) -> String;
50 // fn second(&self) -> u32;
54 // There is a difference between sorting enum variants:
59 // Cat { weight: f64, name: String },
65 // Cat { weight: f64, name: String },
69 // and sorting a single enum struct variant:
74 // Cat $0{ weight: f64, name: String }$0,
81 // Cat { name: String, weight: f64 },
84 pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
85 if ctx.frange.range.is_empty() {
86 cov_mark::hit!(not_applicable_if_no_selection);
90 if let Some(trait_ast) = ctx.find_node_at_offset::<ast::Trait>() {
91 add_sort_methods_assist(acc, trait_ast.assoc_item_list()?)
92 } else if let Some(impl_ast) = ctx.find_node_at_offset::<ast::Impl>() {
93 add_sort_methods_assist(acc, impl_ast.assoc_item_list()?)
94 } else if let Some(struct_ast) = ctx.find_node_at_offset::<ast::Struct>() {
95 add_sort_field_list_assist(acc, struct_ast.field_list())
96 } else if let Some(union_ast) = ctx.find_node_at_offset::<ast::Union>() {
97 add_sort_fields_assist(acc, union_ast.record_field_list()?)
98 } else if let Some(variant_ast) = ctx.find_node_at_offset::<ast::Variant>() {
99 add_sort_field_list_assist(acc, variant_ast.field_list())
100 } else if let Some(enum_struct_variant_ast) = ctx.find_node_at_offset::<ast::RecordFieldList>()
102 // should be above enum and below struct
103 add_sort_fields_assist(acc, enum_struct_variant_ast)
104 } else if let Some(enum_ast) = ctx.find_node_at_offset::<ast::Enum>() {
105 add_sort_variants_assist(acc, enum_ast.variant_list()?)
112 fn add_rewrite<T: AstNode>(
121 impl AddRewrite for Assists {
122 fn add_rewrite<T: AstNode>(
129 self.add(AssistId("sort_items", AssistKind::RefactorRewrite), label, target, |builder| {
130 let mutable: Vec<T> = old.into_iter().map(|it| builder.make_mut(it)).collect();
134 .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax()));
139 fn add_sort_field_list_assist(acc: &mut Assists, field_list: Option<ast::FieldList>) -> Option<()> {
141 Some(ast::FieldList::RecordFieldList(it)) => add_sort_fields_assist(acc, it),
143 cov_mark::hit!(not_applicable_if_sorted_or_empty_or_single);
149 fn add_sort_methods_assist(acc: &mut Assists, item_list: ast::AssocItemList) -> Option<()> {
150 let methods = get_methods(&item_list);
151 let sorted = sort_by_name(&methods);
153 if methods == sorted {
154 cov_mark::hit!(not_applicable_if_sorted_or_empty_or_single);
158 acc.add_rewrite("Sort methods alphabetically", methods, sorted, item_list.syntax().text_range())
161 fn add_sort_fields_assist(
163 record_field_list: ast::RecordFieldList,
165 let fields: Vec<_> = record_field_list.fields().collect();
166 let sorted = sort_by_name(&fields);
168 if fields == sorted {
169 cov_mark::hit!(not_applicable_if_sorted_or_empty_or_single);
174 "Sort fields alphabetically",
177 record_field_list.syntax().text_range(),
181 fn add_sort_variants_assist(acc: &mut Assists, variant_list: ast::VariantList) -> Option<()> {
182 let variants: Vec<_> = variant_list.variants().collect();
183 let sorted = sort_by_name(&variants);
185 if variants == sorted {
186 cov_mark::hit!(not_applicable_if_sorted_or_empty_or_single);
191 "Sort variants alphabetically",
194 variant_list.syntax().text_range(),
198 fn sort_by_name<T: HasName + Clone>(initial: &[T]) -> Vec<T> {
202 .sorted_by(|a, b| match (a.name(), b.name()) {
203 (Some(a), Some(b)) => Ord::cmp(&a.to_string(), &b.to_string()),
205 // unexpected, but just in case
206 (None, None) => Ordering::Equal,
207 (None, Some(_)) => Ordering::Less,
208 (Some(_), None) => Ordering::Greater,
215 use crate::tests::{check_assist, check_assist_not_applicable};
220 fn not_applicable_if_no_selection() {
221 cov_mark::check!(not_applicable_if_no_selection);
223 check_assist_not_applicable(
235 fn not_applicable_if_trait_empty() {
236 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
238 check_assist_not_applicable(
248 fn not_applicable_if_impl_empty() {
249 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
251 check_assist_not_applicable(
262 fn not_applicable_if_struct_empty() {
263 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
265 check_assist_not_applicable(
274 fn not_applicable_if_struct_empty2() {
275 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
277 check_assist_not_applicable(
286 fn not_applicable_if_enum_empty() {
287 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
289 check_assist_not_applicable(
292 $0enum ZeroVariants$0 {};
298 fn not_applicable_if_trait_sorted() {
299 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
301 check_assist_not_applicable(
314 fn not_applicable_if_impl_sorted() {
315 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
317 check_assist_not_applicable(
331 fn not_applicable_if_struct_sorted() {
332 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
334 check_assist_not_applicable(
347 fn not_applicable_if_union_sorted() {
348 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
350 check_assist_not_applicable(
363 fn not_applicable_if_enum_sorted() {
364 cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
366 check_assist_not_applicable(
462 fn sort_generic_struct_with_lifetime() {
466 $0struct Bar<'a,$0 T> {
485 fn sort_struct_fields_diff_len() {
532 d{ first: u32, second: usize},
543 d{ first: u32, second: usize},
550 fn sort_struct_enum_variant_fields() {
555 d$0{ second: usize, first: u32 }$0,
563 d{ first: u32, second: usize },
573 fn sort_struct_enum_variant() {
578 $0d$0{ second: usize, first: u32 },
583 d{ first: u32, second: usize },