]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
Rollup merge of #101420 - kraktus:doc_hir_local, r=cjgillot
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / reorder_impl_items.rs
1 use hir::{PathResolution, Semantics};
2 use ide_db::{FxHashMap, RootDatabase};
3 use itertools::Itertools;
4 use syntax::{
5     ast::{self, HasName},
6     ted, AstNode,
7 };
8
9 use crate::{AssistContext, AssistId, AssistKind, Assists};
10
11 // Assist: reorder_impl_items
12 //
13 // Reorder the items of an `impl Trait`. The items will be ordered
14 // in the same order as in the trait definition.
15 //
16 // ```
17 // trait Foo {
18 //     type A;
19 //     const B: u8;
20 //     fn c();
21 // }
22 //
23 // struct Bar;
24 // $0impl Foo for Bar {
25 //     const B: u8 = 17;
26 //     fn c() {}
27 //     type A = String;
28 // }
29 // ```
30 // ->
31 // ```
32 // trait Foo {
33 //     type A;
34 //     const B: u8;
35 //     fn c();
36 // }
37 //
38 // struct Bar;
39 // impl Foo for Bar {
40 //     type A = String;
41 //     const B: u8 = 17;
42 //     fn c() {}
43 // }
44 // ```
45 pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
46     let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
47     let items = impl_ast.assoc_item_list()?;
48     let assoc_items = items.assoc_items().collect::<Vec<_>>();
49
50     let path = impl_ast
51         .trait_()
52         .and_then(|t| match t {
53             ast::Type::PathType(path) => Some(path),
54             _ => None,
55         })?
56         .path()?;
57
58     let ranks = compute_item_ranks(&path, ctx)?;
59     let sorted: Vec<_> = assoc_items
60         .iter()
61         .cloned()
62         .sorted_by_key(|i| {
63             let name = match i {
64                 ast::AssocItem::Const(c) => c.name(),
65                 ast::AssocItem::Fn(f) => f.name(),
66                 ast::AssocItem::TypeAlias(t) => t.name(),
67                 ast::AssocItem::MacroCall(_) => None,
68             };
69
70             name.and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value())
71         })
72         .collect();
73
74     // Don't edit already sorted methods:
75     if assoc_items == sorted {
76         cov_mark::hit!(not_applicable_if_sorted);
77         return None;
78     }
79
80     let target = items.syntax().text_range();
81     acc.add(
82         AssistId("reorder_impl_items", AssistKind::RefactorRewrite),
83         "Sort items by trait definition",
84         target,
85         |builder| {
86             let assoc_items =
87                 assoc_items.into_iter().map(|item| builder.make_mut(item)).collect::<Vec<_>>();
88             assoc_items
89                 .into_iter()
90                 .zip(sorted)
91                 .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax()));
92         },
93     )
94 }
95
96 fn compute_item_ranks(
97     path: &ast::Path,
98     ctx: &AssistContext<'_>,
99 ) -> Option<FxHashMap<String, usize>> {
100     let td = trait_definition(path, &ctx.sema)?;
101
102     Some(
103         td.items(ctx.db())
104             .iter()
105             .flat_map(|i| i.name(ctx.db()))
106             .enumerate()
107             .map(|(idx, name)| (name.to_string(), idx))
108             .collect(),
109     )
110 }
111
112 fn trait_definition(path: &ast::Path, sema: &Semantics<'_, RootDatabase>) -> Option<hir::Trait> {
113     match sema.resolve_path(path)? {
114         PathResolution::Def(hir::ModuleDef::Trait(trait_)) => Some(trait_),
115         _ => None,
116     }
117 }
118
119 #[cfg(test)]
120 mod tests {
121     use crate::tests::{check_assist, check_assist_not_applicable};
122
123     use super::*;
124
125     #[test]
126     fn not_applicable_if_sorted() {
127         cov_mark::check!(not_applicable_if_sorted);
128         check_assist_not_applicable(
129             reorder_impl_items,
130             r#"
131 trait Bar {
132     type T;
133     const C: ();
134     fn a() {}
135     fn z() {}
136     fn b() {}
137 }
138 struct Foo;
139 $0impl Bar for Foo {
140     type T = ();
141     const C: () = ();
142     fn a() {}
143     fn z() {}
144     fn b() {}
145 }
146         "#,
147         )
148     }
149
150     #[test]
151     fn reorder_impl_trait_functions() {
152         check_assist(
153             reorder_impl_items,
154             r#"
155 trait Bar {
156     fn a() {}
157     fn c() {}
158     fn b() {}
159     fn d() {}
160 }
161
162 struct Foo;
163 $0impl Bar for Foo {
164     fn d() {}
165     fn b() {}
166     fn c() {}
167     fn a() {}
168 }
169 "#,
170             r#"
171 trait Bar {
172     fn a() {}
173     fn c() {}
174     fn b() {}
175     fn d() {}
176 }
177
178 struct Foo;
179 impl Bar for Foo {
180     fn a() {}
181     fn c() {}
182     fn b() {}
183     fn d() {}
184 }
185 "#,
186         )
187     }
188
189     #[test]
190     fn not_applicable_if_empty() {
191         check_assist_not_applicable(
192             reorder_impl_items,
193             r#"
194 trait Bar {};
195 struct Foo;
196 $0impl Bar for Foo {}
197         "#,
198         )
199     }
200
201     #[test]
202     fn reorder_impl_trait_items() {
203         check_assist(
204             reorder_impl_items,
205             r#"
206 trait Bar {
207     fn a() {}
208     type T0;
209     fn c() {}
210     const C1: ();
211     fn b() {}
212     type T1;
213     fn d() {}
214     const C0: ();
215 }
216
217 struct Foo;
218 $0impl Bar for Foo {
219     type T1 = ();
220     fn d() {}
221     fn b() {}
222     fn c() {}
223     const C1: () = ();
224     fn a() {}
225     type T0 = ();
226     const C0: () = ();
227 }
228         "#,
229             r#"
230 trait Bar {
231     fn a() {}
232     type T0;
233     fn c() {}
234     const C1: ();
235     fn b() {}
236     type T1;
237     fn d() {}
238     const C0: ();
239 }
240
241 struct Foo;
242 impl Bar for Foo {
243     fn a() {}
244     type T0 = ();
245     fn c() {}
246     const C1: () = ();
247     fn b() {}
248     type T1 = ();
249     fn d() {}
250     const C0: () = ();
251 }
252         "#,
253         )
254     }
255
256     #[test]
257     fn reorder_impl_trait_items_uneven_ident_lengths() {
258         check_assist(
259             reorder_impl_items,
260             r#"
261 trait Bar {
262     type Foo;
263     type Fooo;
264 }
265
266 struct Foo;
267 impl Bar for Foo {
268     type Fooo = ();
269     type Foo = ();$0
270 }"#,
271             r#"
272 trait Bar {
273     type Foo;
274     type Fooo;
275 }
276
277 struct Foo;
278 impl Bar for Foo {
279     type Foo = ();
280     type Fooo = ();
281 }"#,
282         )
283     }
284 }