aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/reorder_impl.rs201
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs35
3 files changed, 238 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/reorder_impl.rs b/crates/assists/src/handlers/reorder_impl.rs
new file mode 100644
index 000000000..309f291c8
--- /dev/null
+++ b/crates/assists/src/handlers/reorder_impl.rs
@@ -0,0 +1,201 @@
1use itertools::Itertools;
2use rustc_hash::FxHashMap;
3
4use hir::{PathResolution, Semantics};
5use ide_db::RootDatabase;
6use syntax::{
7 algo,
8 ast::{self, NameOwner},
9 AstNode,
10};
11use test_utils::mark;
12
13use crate::{AssistContext, AssistId, AssistKind, Assists};
14
15// Assist: reorder_impl
16//
17// Reorder the methods of an `impl Trait`. The methods will be ordered
18// in the same order as in the trait definition.
19//
20// ```
21// trait Foo {
22// fn a() {}
23// fn b() {}
24// fn c() {}
25// }
26//
27// struct Bar;
28// $0impl Foo for Bar {
29// fn b() {}
30// fn c() {}
31// fn a() {}
32// }
33// ```
34// ->
35// ```
36// trait Foo {
37// fn a() {}
38// fn b() {}
39// fn c() {}
40// }
41//
42// struct Bar;
43// impl Foo for Bar {
44// fn a() {}
45// fn b() {}
46// fn c() {}
47// }
48// ```
49//
50pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
51 let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
52 let items = impl_ast.assoc_item_list()?;
53 let methods = get_methods(&items);
54
55 let path = impl_ast
56 .trait_()
57 .and_then(|t| match t {
58 ast::Type::PathType(path) => Some(path),
59 _ => None,
60 })?
61 .path()?;
62
63 let ranks = compute_method_ranks(&path, ctx)?;
64 let sorted: Vec<_> = methods
65 .iter()
66 .cloned()
67 .sorted_by_key(|f| {
68 f.name().and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value())
69 })
70 .collect();
71
72 // Don't edit already sorted methods:
73 if methods == sorted {
74 mark::hit!(not_applicable_if_sorted);
75 return None;
76 }
77
78 let target = items.syntax().text_range();
79 acc.add(AssistId("reorder_impl", AssistKind::RefactorRewrite), "Sort methods", target, |edit| {
80 let mut rewriter = algo::SyntaxRewriter::default();
81 for (old, new) in methods.iter().zip(&sorted) {
82 rewriter.replace(old.syntax(), new.syntax());
83 }
84 edit.rewrite(rewriter);
85 })
86}
87
88fn compute_method_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
89 let td = trait_definition(path, &ctx.sema)?;
90
91 Some(
92 td.items(ctx.db())
93 .iter()
94 .flat_map(|i| match i {
95 hir::AssocItem::Function(f) => Some(f),
96 _ => None,
97 })
98 .enumerate()
99 .map(|(idx, func)| ((func.name(ctx.db()).to_string(), idx)))
100 .collect(),
101 )
102}
103
104fn trait_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<hir::Trait> {
105 match sema.resolve_path(path)? {
106 PathResolution::Def(hir::ModuleDef::Trait(trait_)) => Some(trait_),
107 _ => None,
108 }
109}
110
111fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
112 items
113 .assoc_items()
114 .flat_map(|i| match i {
115 ast::AssocItem::Fn(f) => Some(f),
116 _ => None,
117 })
118 .filter(|f| f.name().is_some())
119 .collect()
120}
121
122#[cfg(test)]
123mod tests {
124 use test_utils::mark;
125
126 use crate::tests::{check_assist, check_assist_not_applicable};
127
128 use super::*;
129
130 #[test]
131 fn not_applicable_if_sorted() {
132 mark::check!(not_applicable_if_sorted);
133 check_assist_not_applicable(
134 reorder_impl,
135 r#"
136trait Bar {
137 fn a() {}
138 fn z() {}
139 fn b() {}
140}
141struct Foo;
142$0impl Bar for Foo {
143 fn a() {}
144 fn z() {}
145 fn b() {}
146}
147 "#,
148 )
149 }
150
151 #[test]
152 fn not_applicable_if_empty() {
153 check_assist_not_applicable(
154 reorder_impl,
155 r#"
156trait Bar {};
157struct Foo;
158$0impl Bar for Foo {}
159 "#,
160 )
161 }
162
163 #[test]
164 fn reorder_impl_trait_methods() {
165 check_assist(
166 reorder_impl,
167 r#"
168trait Bar {
169 fn a() {}
170 fn c() {}
171 fn b() {}
172 fn d() {}
173}
174
175struct Foo;
176$0impl Bar for Foo {
177 fn d() {}
178 fn b() {}
179 fn c() {}
180 fn a() {}
181}
182 "#,
183 r#"
184trait Bar {
185 fn a() {}
186 fn c() {}
187 fn b() {}
188 fn d() {}
189}
190
191struct Foo;
192impl Bar for Foo {
193 fn a() {}
194 fn c() {}
195 fn b() {}
196 fn d() {}
197}
198 "#,
199 )
200 }
201}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 90009c55a..1080294ab 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -146,6 +146,7 @@ mod handlers {
146 mod remove_mut; 146 mod remove_mut;
147 mod remove_unused_param; 147 mod remove_unused_param;
148 mod reorder_fields; 148 mod reorder_fields;
149 mod reorder_impl;
149 mod replace_derive_with_manual_impl; 150 mod replace_derive_with_manual_impl;
150 mod replace_if_let_with_match; 151 mod replace_if_let_with_match;
151 mod replace_impl_trait_with_generic; 152 mod replace_impl_trait_with_generic;
@@ -202,6 +203,7 @@ mod handlers {
202 remove_mut::remove_mut, 203 remove_mut::remove_mut,
203 remove_unused_param::remove_unused_param, 204 remove_unused_param::remove_unused_param,
204 reorder_fields::reorder_fields, 205 reorder_fields::reorder_fields,
206 reorder_impl::reorder_impl,
205 replace_derive_with_manual_impl::replace_derive_with_manual_impl, 207 replace_derive_with_manual_impl::replace_derive_with_manual_impl,
206 replace_if_let_with_match::replace_if_let_with_match, 208 replace_if_let_with_match::replace_if_let_with_match,
207 replace_if_let_with_match::replace_match_with_if_let, 209 replace_if_let_with_match::replace_match_with_if_let,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index e28837b53..217f577eb 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -896,6 +896,41 @@ const test: Foo = Foo {foo: 1, bar: 0}
896} 896}
897 897
898#[test] 898#[test]
899fn doctest_reorder_impl() {
900 check_doc_test(
901 "reorder_impl",
902 r#####"
903trait Foo {
904 fn a() {}
905 fn b() {}
906 fn c() {}
907}
908
909struct Bar;
910$0impl Foo for Bar {
911 fn b() {}
912 fn c() {}
913 fn a() {}
914}
915"#####,
916 r#####"
917trait Foo {
918 fn a() {}
919 fn b() {}
920 fn c() {}
921}
922
923struct Bar;
924impl Foo for Bar {
925 fn a() {}
926 fn b() {}
927 fn c() {}
928}
929"#####,
930 )
931}
932
933#[test]
899fn doctest_replace_derive_with_manual_impl() { 934fn doctest_replace_derive_with_manual_impl() {
900 check_doc_test( 935 check_doc_test(
901 "replace_derive_with_manual_impl", 936 "replace_derive_with_manual_impl",