aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs72
1 files changed, 59 insertions, 13 deletions
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs
index 120109d4b..4926a9b24 100644
--- a/crates/ra_assists/src/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/add_missing_impl_members.rs
@@ -1,12 +1,15 @@
1use std::collections::HashSet; 1use std::collections::HashSet;
2 2
3use crate::assist_ctx::{Assist, AssistCtx}; 3use crate::{Assist, AssistId, AssistCtx};
4 4
5use hir::Resolver; 5use hir::Resolver;
6use hir::db::HirDatabase; 6use hir::db::HirDatabase;
7use ra_syntax::{SmolStr, SyntaxKind, SyntaxNode, TreeArc}; 7use ra_syntax::{SmolStr, SyntaxKind, SyntaxNode, TextUnit, TreeArc};
8use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; 8use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner};
9use ra_db::FilePosition; 9use ra_db::FilePosition;
10use ra_fmt::{leading_indent, reindent};
11
12use itertools::Itertools;
10 13
11/// Given an `ast::ImplBlock`, resolves the target trait (the one being 14/// Given an `ast::ImplBlock`, resolves the target trait (the one being
12/// implemented) to a `ast::TraitDef`. 15/// implemented) to a `ast::TraitDef`.
@@ -24,7 +27,21 @@ pub(crate) fn resolve_target_trait_def(
24 } 27 }
25} 28}
26 29
27pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 30pub(crate) fn build_func_body(def: &ast::FnDef) -> String {
31 let mut buf = String::new();
32
33 for child in def.syntax().children() {
34 if child.kind() == SyntaxKind::SEMI {
35 buf.push_str(" { unimplemented!() }")
36 } else {
37 child.text().push_to(&mut buf);
38 }
39 }
40
41 buf.trim_end().to_string()
42}
43
44pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
28 use SyntaxKind::{IMPL_BLOCK, ITEM_LIST, WHITESPACE}; 45 use SyntaxKind::{IMPL_BLOCK, ITEM_LIST, WHITESPACE};
29 46
30 let node = ctx.covering_node(); 47 let node = ctx.covering_node();
@@ -35,6 +52,7 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti
35 } 52 }
36 53
37 let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?; 54 let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?;
55 let impl_item_list = impl_node.item_list()?;
38 56
39 let trait_def = { 57 let trait_def = {
40 let db = ctx.db; 58 let db = ctx.db;
@@ -47,23 +65,49 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti
47 }; 65 };
48 66
49 let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None }; 67 let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None };
50 let def_name = |&def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) }; 68 let def_name = |def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) };
51 69
52 let trait_items = trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items(); 70 let trait_items = trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items();
53 let impl_items = impl_node.item_list()?.impl_items(); 71 let impl_items = impl_item_list.impl_items();
54 72
55 let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>(); 73 let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
56 let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>(); 74 let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
57 75
58 let trait_fn_names = trait_fns.iter().filter_map(def_name).collect::<HashSet<_>>(); 76 let trait_fn_names = trait_fns.iter().cloned().filter_map(def_name).collect::<HashSet<_>>();
59 let impl_fn_names = impl_fns.iter().filter_map(def_name).collect::<HashSet<_>>(); 77 let impl_fn_names = impl_fns.iter().cloned().filter_map(def_name).collect::<HashSet<_>>();
60 78
61 let missing_fn_names = trait_fn_names.difference(&impl_fn_names).collect::<HashSet<_>>(); 79 let missing_fn_names = trait_fn_names.difference(&impl_fn_names).collect::<HashSet<_>>();
62 let missing_fns = trait_fns 80 let missing_fns: Vec<_> = trait_fns
63 .iter() 81 .iter()
64 .filter(|&t| def_name(t).map(|n| missing_fn_names.contains(&n)).unwrap_or(false)); 82 .cloned()
83 .filter(|t| def_name(t).map(|n| missing_fn_names.contains(&n)).unwrap_or(false))
84 .collect();
65 85
66 unimplemented!() 86 if missing_fns.is_empty() {
87 return None;
88 }
89
90 let last_whitespace_node =
91 impl_item_list.syntax().children().filter_map(ast::Whitespace::cast).last()?.syntax();
92
93 ctx.add_action(AssistId("add_impl_missing_members"), "add impl missing members", |edit| {
94 let func_bodies = missing_fns.into_iter().map(build_func_body).join("\n");
95 let func_bodies = String::from("\n") + &func_bodies;
96
97 let first_impl_item = impl_item_list.impl_items().next();
98 // FIXME: We should respect the indent of the first item from the item list or the indent of leading block + some default indent (4?)
99 // Another approach is to not indent at all if there are no items here
100 let indent = first_impl_item.and_then(|i| leading_indent(i.syntax())).unwrap_or_default();
101 let func_bodies = reindent(&func_bodies, indent) + "\n";
102
103 let changed_range = last_whitespace_node.range();
104 let replaced_text_range = TextUnit::of_str(&func_bodies);
105
106 edit.replace(changed_range, func_bodies);
107 edit.set_cursor(changed_range.start() + replaced_text_range - TextUnit::of_str("\n"));
108 });
109
110 ctx.build()
67} 111}
68 112
69#[cfg(test)] 113#[cfg(test)]
@@ -78,24 +122,26 @@ mod tests {
78 " 122 "
79trait Foo { 123trait Foo {
80 fn foo(&self); 124 fn foo(&self);
125 fn bar(&self);
81} 126}
82 127
83struct S; 128struct S;
84 129
85impl Foo for S { 130impl Foo for S {
131 fn bar(&self) {}
86 <|> 132 <|>
87}", 133}",
88 " 134 "
89trait Foo { 135trait Foo {
90 fn foo(&self); 136 fn foo(&self);
137 fn bar(&self);
91} 138}
92 139
93struct S; 140struct S;
94 141
95impl Foo for S { 142impl Foo for S {
96 fn foo(&self) { 143 fn bar(&self) {}
97 <|> 144 fn foo(&self) { unimplemented!() }<|>
98 }
99}", 145}",
100 ); 146 );
101 } 147 }