]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/reorder_impl.rs
Merge #11481
[rust.git] / crates / ide_assists / src / handlers / reorder_impl.rs
1 use itertools::Itertools;
2 use rustc_hash::FxHashMap;
3
4 use hir::{PathResolution, Semantics};
5 use ide_db::RootDatabase;
6 use syntax::{
7     ast::{self, HasName},
8     ted, AstNode,
9 };
10
11 use crate::{utils::get_methods, AssistContext, AssistId, AssistKind, Assists};
12
13 // Assist: reorder_impl
14 //
15 // Reorder the methods of an `impl Trait`. The methods will be ordered
16 // in the same order as in the trait definition.
17 //
18 // ```
19 // trait Foo {
20 //     fn a() {}
21 //     fn b() {}
22 //     fn c() {}
23 // }
24 //
25 // struct Bar;
26 // $0impl Foo for Bar {
27 //     fn b() {}
28 //     fn c() {}
29 //     fn a() {}
30 // }
31 // ```
32 // ->
33 // ```
34 // trait Foo {
35 //     fn a() {}
36 //     fn b() {}
37 //     fn c() {}
38 // }
39 //
40 // struct Bar;
41 // impl Foo for Bar {
42 //     fn a() {}
43 //     fn b() {}
44 //     fn c() {}
45 // }
46 // ```
47 pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
48     let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
49     let items = impl_ast.assoc_item_list()?;
50     let methods = get_methods(&items);
51
52     let path = impl_ast
53         .trait_()
54         .and_then(|t| match t {
55             ast::Type::PathType(path) => Some(path),
56             _ => None,
57         })?
58         .path()?;
59
60     let ranks = compute_method_ranks(&path, ctx)?;
61     let sorted: Vec<_> = methods
62         .iter()
63         .cloned()
64         .sorted_by_key(|f| {
65             f.name().and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value())
66         })
67         .collect();
68
69     // Don't edit already sorted methods:
70     if methods == sorted {
71         cov_mark::hit!(not_applicable_if_sorted);
72         return None;
73     }
74
75     let target = items.syntax().text_range();
76     acc.add(
77         AssistId("reorder_impl", AssistKind::RefactorRewrite),
78         "Sort methods by trait definition",
79         target,
80         |builder| {
81             let methods = methods.into_iter().map(|fn_| builder.make_mut(fn_)).collect::<Vec<_>>();
82             methods
83                 .into_iter()
84                 .zip(sorted)
85                 .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax()));
86         },
87     )
88 }
89
90 fn compute_method_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
91     let td = trait_definition(path, &ctx.sema)?;
92
93     Some(
94         td.items(ctx.db())
95             .iter()
96             .flat_map(|i| match i {
97                 hir::AssocItem::Function(f) => Some(f),
98                 _ => None,
99             })
100             .enumerate()
101             .map(|(idx, func)| (func.name(ctx.db()).to_string(), idx))
102             .collect(),
103     )
104 }
105
106 fn trait_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<hir::Trait> {
107     match sema.resolve_path(path)? {
108         PathResolution::Def(hir::ModuleDef::Trait(trait_)) => Some(trait_),
109         _ => None,
110     }
111 }
112
113 #[cfg(test)]
114 mod tests {
115     use crate::tests::{check_assist, check_assist_not_applicable};
116
117     use super::*;
118
119     #[test]
120     fn not_applicable_if_sorted() {
121         cov_mark::check!(not_applicable_if_sorted);
122         check_assist_not_applicable(
123             reorder_impl,
124             r#"
125 trait Bar {
126     fn a() {}
127     fn z() {}
128     fn b() {}
129 }
130 struct Foo;
131 $0impl Bar for Foo {
132     fn a() {}
133     fn z() {}
134     fn b() {}
135 }
136         "#,
137         )
138     }
139
140     #[test]
141     fn not_applicable_if_empty() {
142         check_assist_not_applicable(
143             reorder_impl,
144             r#"
145 trait Bar {};
146 struct Foo;
147 $0impl Bar for Foo {}
148         "#,
149         )
150     }
151
152     #[test]
153     fn reorder_impl_trait_functions() {
154         check_assist(
155             reorder_impl,
156             r#"
157 trait Bar {
158     fn a() {}
159     fn c() {}
160     fn b() {}
161     fn d() {}
162 }
163
164 struct Foo;
165 $0impl Bar for Foo {
166     fn d() {}
167     fn b() {}
168     fn c() {}
169     fn a() {}
170 }
171         "#,
172             r#"
173 trait Bar {
174     fn a() {}
175     fn c() {}
176     fn b() {}
177     fn d() {}
178 }
179
180 struct Foo;
181 impl Bar for Foo {
182     fn a() {}
183     fn c() {}
184     fn b() {}
185     fn d() {}
186 }
187         "#,
188         )
189     }
190
191     #[test]
192     fn reorder_impl_trait_methods_uneven_ident_lengths() {
193         check_assist(
194             reorder_impl,
195             r#"
196 trait Bar {
197     fn foo(&mut self) {}
198     fn fooo(&mut self) {}
199 }
200
201 struct Foo;
202 impl Bar for Foo {
203     fn fooo(&mut self) {}
204     fn foo(&mut self) {$0}
205 }"#,
206             r#"
207 trait Bar {
208     fn foo(&mut self) {}
209     fn fooo(&mut self) {}
210 }
211
212 struct Foo;
213 impl Bar for Foo {
214     fn foo(&mut self) {}
215     fn fooo(&mut self) {}
216 }"#,
217         )
218     }
219 }