Skip to content

Commit 643f393

Browse files
committed
Sort trait and impl methods
1 parent 29506b5 commit 643f393

File tree

4 files changed

+164
-13
lines changed

4 files changed

+164
-13
lines changed

crates/ide_assists/src/handlers/reorder_impl.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use syntax::{
88
ted, AstNode,
99
};
1010

11-
use crate::{AssistContext, AssistId, AssistKind, Assists};
11+
use crate::{AssistContext, AssistId, AssistKind, Assists, utils::get_methods};
1212

1313
// Assist: reorder_impl
1414
//
@@ -76,7 +76,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
7676
let target = items.syntax().text_range();
7777
acc.add(
7878
AssistId("reorder_impl", AssistKind::RefactorRewrite),
79-
"Sort methods",
79+
"Sort methods by trait definition",
8080
target,
8181
|builder| {
8282
let methods = methods.into_iter().map(|fn_| builder.make_mut(fn_)).collect::<Vec<_>>();
@@ -111,17 +111,6 @@ fn trait_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<
111111
}
112112
}
113113

114-
fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
115-
items
116-
.assoc_items()
117-
.flat_map(|i| match i {
118-
ast::AssocItem::Fn(f) => Some(f),
119-
_ => None,
120-
})
121-
.filter(|f| f.name().is_some())
122-
.collect()
123-
}
124-
125114
#[cfg(test)]
126115
mod tests {
127116
use crate::tests::{check_assist, check_assist_not_applicable};
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use std::cmp::Ordering;
2+
3+
use hir::known::Option;
4+
use itertools::Itertools;
5+
6+
use syntax::{
7+
ast::{self, NameOwner},
8+
ted, AstNode,
9+
};
10+
11+
use crate::{utils::get_methods, AssistContext, AssistId, AssistKind, Assists};
12+
13+
// Assist: sort_items
14+
//
15+
pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
16+
if let Some(trait_ast) = ctx.find_node_at_offset::<ast::Trait>() {
17+
sort_methods_assist(acc, trait_ast.assoc_item_list()?)
18+
} else if let Some(impl_ast) = ctx.find_node_at_offset::<ast::Impl>() {
19+
sort_methods_assist(acc, impl_ast.assoc_item_list()?)
20+
} else {
21+
None
22+
}
23+
}
24+
25+
fn sort_methods_assist(acc: &mut Assists, item_list: ast::AssocItemList) -> Option<()> {
26+
let methods = get_methods(&item_list);
27+
let sorted = sort_by_name(&methods);
28+
29+
if methods == sorted {
30+
cov_mark::hit!(not_applicable_if_sorted);
31+
return None;
32+
}
33+
34+
acc.add(
35+
AssistId("sort_items", AssistKind::RefactorRewrite),
36+
"Sort methods alphabetically",
37+
item_list.syntax().text_range(),
38+
|builder| {
39+
let methods = methods.into_iter().map(|fn_| builder.make_mut(fn_)).collect::<Vec<_>>();
40+
methods
41+
.into_iter()
42+
.zip(sorted)
43+
.for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax()));
44+
},
45+
)
46+
}
47+
48+
fn sort_by_name<T: NameOwner + Clone>(initial: &[T]) -> Vec<T> {
49+
initial
50+
.iter()
51+
.cloned()
52+
.sorted_by(|a, b| match (a.name(), b.name()) {
53+
(Some(a), Some(b)) => Ord::cmp(&a.to_string(), &b.to_string()),
54+
55+
// unexpected, but just in case
56+
(None, None) => Ordering::Equal,
57+
(None, Some(_)) => Ordering::Less,
58+
(Some(_), None) => Ordering::Greater,
59+
})
60+
.collect()
61+
}
62+
63+
#[cfg(test)]
64+
mod tests {
65+
use crate::tests::{check_assist, check_assist_not_applicable};
66+
67+
use super::*;
68+
69+
#[test]
70+
fn not_applicable_if_trait_sorted() {
71+
cov_mark::check!(not_applicable_if_sorted);
72+
73+
check_assist_not_applicable(
74+
sort_items,
75+
r#"
76+
t$0rait Bar {
77+
fn a() {}
78+
fn b() {}
79+
fn c() {}
80+
}
81+
"#,
82+
)
83+
}
84+
85+
#[test]
86+
fn not_applicable_if_impl_sorted() {
87+
cov_mark::check!(not_applicable_if_sorted);
88+
89+
check_assist_not_applicable(
90+
sort_items,
91+
r#"
92+
struct Bar;
93+
$0impl Bar {
94+
fn a() {}
95+
fn b() {}
96+
fn c() {}
97+
}
98+
"#,
99+
)
100+
}
101+
102+
#[test]
103+
fn sort_trait() {
104+
check_assist(
105+
sort_items,
106+
r#"
107+
$0trait Bar {
108+
fn a() {}
109+
fn c() {}
110+
fn z() {}
111+
fn b() {}
112+
}
113+
"#,
114+
r#"
115+
trait Bar {
116+
fn a() {}
117+
fn b() {}
118+
fn c() {}
119+
fn z() {}
120+
}
121+
"#,
122+
)
123+
}
124+
125+
#[test]
126+
fn sort_impl() {
127+
check_assist(
128+
sort_items,
129+
r#"
130+
struct Bar;
131+
$0impl Bar {
132+
fn c() {}
133+
fn a() {}
134+
fn z() {}
135+
fn d() {}
136+
}
137+
"#,
138+
r#"
139+
struct Bar;
140+
impl Bar {
141+
fn a() {}
142+
fn c() {}
143+
fn d() {}
144+
fn z() {}
145+
}
146+
"#,
147+
)
148+
}
149+
}

crates/ide_assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ mod handlers {
110110
mod replace_qualified_name_with_use;
111111
mod replace_string_with_char;
112112
mod split_import;
113+
mod sort_items;
113114
mod toggle_ignore;
114115
mod unmerge_use;
115116
mod unwrap_block;
@@ -181,6 +182,7 @@ mod handlers {
181182
replace_impl_trait_with_generic::replace_impl_trait_with_generic,
182183
replace_let_with_if_let::replace_let_with_if_let,
183184
replace_qualified_name_with_use::replace_qualified_name_with_use,
185+
sort_items::sort_items,
184186
split_import::split_import,
185187
toggle_ignore::toggle_ignore,
186188
unmerge_use::unmerge_use,

crates/ide_assists/src/utils.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,3 +516,14 @@ fn ty_ctor(ty: &String, ctor: &str) -> Option<String> {
516516
let res = ty.to_string().strip_prefix(ctor)?.strip_prefix('<')?.strip_suffix('>')?.to_string();
517517
Some(res)
518518
}
519+
520+
pub(crate) fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
521+
items
522+
.assoc_items()
523+
.flat_map(|i| match i {
524+
ast::AssocItem::Fn(f) => Some(f),
525+
_ => None,
526+
})
527+
.filter(|f| f.name().is_some())
528+
.collect()
529+
}

0 commit comments

Comments
 (0)