aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
authorIgor Matuszewski <[email protected]>2019-03-07 12:21:56 +0000
committerIgor Matuszewski <[email protected]>2019-03-16 21:41:13 +0000
commit713975b1c1584bb141dfca84818bf5a7ca91a8ee (patch)
tree54a7769270157fffdb5ce2bd86a321b4f3079277 /crates/ra_assists
parent38eece97ecc801811a8847cfd230e97d838398cd (diff)
Properly support the case when the cursor is inside an empty block or outside
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs59
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
5use hir::Resolver; 5use hir::Resolver;
6use hir::db::HirDatabase; 6use hir::db::HirDatabase;
7use ra_syntax::{SmolStr, SyntaxKind, SyntaxNode, TextUnit, TreeArc}; 7use ra_syntax::{SmolStr, SyntaxKind, TextRange, 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}; 10use ra_fmt::{leading_indent, reindent};
@@ -42,17 +42,13 @@ pub(crate) fn build_func_body(def: &ast::FnDef) -> String {
42} 42}
43 43
44pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 44pub(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)]
123mod tests { 124mod 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 "
167trait Foo { fn foo(&self); }
168struct S;
169impl Foo for S {<|>}",
170 "
171trait Foo { fn foo(&self); }
172struct S;
173impl 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 "
184trait Foo { fn foo(&self); }
185struct S;
186impl Foo for S {}<|>",
187 )
188 }
160} 189}