diff options
-rw-r--r-- | crates/ra_assists/src/add_missing_impl_members.rs | 59 |
1 files changed, 44 insertions, 15 deletions
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs index 0888268e4..f121dafb2 100644 --- a/crates/ra_assists/src/add_missing_impl_members.rs +++ b/crates/ra_assists/src/add_missing_impl_members.rs | |||
@@ -4,7 +4,7 @@ use crate::{Assist, AssistId, AssistCtx}; | |||
4 | 4 | ||
5 | use hir::Resolver; | 5 | use hir::Resolver; |
6 | use hir::db::HirDatabase; | 6 | use hir::db::HirDatabase; |
7 | use ra_syntax::{SmolStr, SyntaxKind, SyntaxNode, TextUnit, TreeArc}; | 7 | use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; |
8 | use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; | 8 | use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; |
9 | use ra_db::FilePosition; | 9 | use ra_db::FilePosition; |
10 | use ra_fmt::{leading_indent, reindent}; | 10 | use ra_fmt::{leading_indent, reindent}; |
@@ -42,17 +42,13 @@ pub(crate) fn build_func_body(def: &ast::FnDef) -> String { | |||
42 | } | 42 | } |
43 | 43 | ||
44 | pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 44 | pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
45 | use SyntaxKind::{IMPL_BLOCK, ITEM_LIST, WHITESPACE}; | ||
46 | |||
47 | let node = ctx.covering_node(); | 45 | let node = ctx.covering_node(); |
48 | let kinds = node.ancestors().take(3).map(SyntaxNode::kind); | ||
49 | // Only suggest this in `impl Foo for S { [Item...] <|> }` cursor position | ||
50 | if !Iterator::eq(kinds, [WHITESPACE, ITEM_LIST, IMPL_BLOCK].iter().cloned()) { | ||
51 | return None; | ||
52 | } | ||
53 | |||
54 | let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?; | 46 | let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?; |
55 | let impl_item_list = impl_node.item_list()?; | 47 | let impl_item_list = impl_node.item_list()?; |
48 | // Don't offer the assist when cursor is at the end, outside the block itself. | ||
49 | if node.range().end() == impl_node.syntax().range().end() { | ||
50 | return None; | ||
51 | } | ||
56 | 52 | ||
57 | let trait_def = { | 53 | let trait_def = { |
58 | let db = ctx.db; | 54 | let db = ctx.db; |
@@ -82,14 +78,10 @@ pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> | |||
82 | .cloned() | 78 | .cloned() |
83 | .filter(|t| def_name(t).map(|n| missing_fn_names.contains(&n)).unwrap_or(false)) | 79 | .filter(|t| def_name(t).map(|n| missing_fn_names.contains(&n)).unwrap_or(false)) |
84 | .collect(); | 80 | .collect(); |
85 | |||
86 | if missing_fns.is_empty() { | 81 | if missing_fns.is_empty() { |
87 | return None; | 82 | return None; |
88 | } | 83 | } |
89 | 84 | ||
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 missing impl members", |edit| { | 85 | ctx.add_action(AssistId("add_impl_missing_members"), "add missing impl members", |edit| { |
94 | let indent = { | 86 | let indent = { |
95 | // FIXME: Find a way to get the indent already used in the file. | 87 | // FIXME: Find a way to get the indent already used in the file. |
@@ -109,7 +101,16 @@ pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> | |||
109 | let func_bodies = String::from("\n") + &func_bodies; | 101 | let func_bodies = String::from("\n") + &func_bodies; |
110 | let func_bodies = reindent(&func_bodies, &indent) + "\n"; | 102 | let func_bodies = reindent(&func_bodies, &indent) + "\n"; |
111 | 103 | ||
112 | let changed_range = last_whitespace_node.range(); | 104 | let changed_range = { |
105 | let last_whitespace = impl_item_list.syntax().children(); | ||
106 | let last_whitespace = last_whitespace.filter_map(ast::Whitespace::cast).last(); | ||
107 | let last_whitespace = last_whitespace.map(|w| w.syntax()); | ||
108 | |||
109 | let cursor_range = TextRange::from_to(node.range().end(), node.range().end()); | ||
110 | |||
111 | last_whitespace.map(|x| x.range()).unwrap_or(cursor_range) | ||
112 | }; | ||
113 | |||
113 | let replaced_text_range = TextUnit::of_str(&func_bodies); | 114 | let replaced_text_range = TextUnit::of_str(&func_bodies); |
114 | 115 | ||
115 | edit.replace(changed_range, func_bodies); | 116 | edit.replace(changed_range, func_bodies); |
@@ -122,7 +123,7 @@ pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> | |||
122 | #[cfg(test)] | 123 | #[cfg(test)] |
123 | mod tests { | 124 | mod tests { |
124 | use super::*; | 125 | use super::*; |
125 | use crate::helpers::{ check_assist }; | 126 | use crate::helpers::{check_assist, check_assist_not_applicable}; |
126 | 127 | ||
127 | #[test] | 128 | #[test] |
128 | fn test_add_missing_impl_members() { | 129 | fn test_add_missing_impl_members() { |
@@ -157,4 +158,32 @@ impl Foo for S { | |||
157 | }", | 158 | }", |
158 | ); | 159 | ); |
159 | } | 160 | } |
161 | |||
162 | #[test] | ||
163 | fn test_empty_impl_block() { | ||
164 | check_assist( | ||
165 | add_missing_impl_members, | ||
166 | " | ||
167 | trait Foo { fn foo(&self); } | ||
168 | struct S; | ||
169 | impl Foo for S {<|>}", | ||
170 | " | ||
171 | trait Foo { fn foo(&self); } | ||
172 | struct S; | ||
173 | impl Foo for S { | ||
174 | fn foo(&self) { unimplemented!() }<|> | ||
175 | }", | ||
176 | ); | ||
177 | } | ||
178 | |||
179 | #[test] | ||
180 | fn test_cursor_after_empty_impl_block() { | ||
181 | check_assist_not_applicable( | ||
182 | add_missing_impl_members, | ||
183 | " | ||
184 | trait Foo { fn foo(&self); } | ||
185 | struct S; | ||
186 | impl Foo for S {}<|>", | ||
187 | ) | ||
188 | } | ||
160 | } | 189 | } |