diff options
author | Jesse Bakker <[email protected]> | 2021-05-06 14:56:48 +0100 |
---|---|---|
committer | Jesse Bakker <[email protected]> | 2021-05-06 15:16:38 +0100 |
commit | 10254b5d953c2031edeb6a5fd78dbaed867c2268 (patch) | |
tree | e38abb2e9c7399a258896c39395cd6fbef08fac2 /crates | |
parent | c3596371d841ebb949dc8b99acc3bc902e4d808a (diff) |
Fix: Do not overwrite comments and attrs in trait impl completion
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide_completion/src/completions/trait_impl.rs | 73 |
1 files changed, 65 insertions, 8 deletions
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs index a26fe7c6c..78fbfcd97 100644 --- a/crates/ide_completion/src/completions/trait_impl.rs +++ b/crates/ide_completion/src/completions/trait_impl.rs | |||
@@ -36,7 +36,7 @@ use ide_db::{traits::get_missing_assoc_items, SymbolKind}; | |||
36 | use syntax::{ | 36 | use syntax::{ |
37 | ast::{self, edit, Impl}, | 37 | ast::{self, edit, Impl}, |
38 | display::function_declaration, | 38 | display::function_declaration, |
39 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, | 39 | AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T, |
40 | }; | 40 | }; |
41 | use text_edit::TextEdit; | 41 | use text_edit::TextEdit; |
42 | 42 | ||
@@ -154,8 +154,7 @@ fn add_function_impl( | |||
154 | } else { | 154 | } else { |
155 | CompletionItemKind::SymbolKind(SymbolKind::Function) | 155 | CompletionItemKind::SymbolKind(SymbolKind::Function) |
156 | }; | 156 | }; |
157 | let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); | 157 | let range = replacement_range(ctx, fn_def_node); |
158 | |||
159 | if let Some(src) = func.source(ctx.db) { | 158 | if let Some(src) = func.source(ctx.db) { |
160 | let function_decl = function_declaration(&src.value); | 159 | let function_decl = function_declaration(&src.value); |
161 | match ctx.config.snippet_cap { | 160 | match ctx.config.snippet_cap { |
@@ -183,8 +182,7 @@ fn add_type_alias_impl( | |||
183 | 182 | ||
184 | let snippet = format!("type {} = ", alias_name); | 183 | let snippet = format!("type {} = ", alias_name); |
185 | 184 | ||
186 | let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); | 185 | let range = replacement_range(ctx, type_def_node); |
187 | |||
188 | let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); | 186 | let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); |
189 | item.text_edit(TextEdit::replace(range, snippet)) | 187 | item.text_edit(TextEdit::replace(range, snippet)) |
190 | .lookup_by(alias_name) | 188 | .lookup_by(alias_name) |
@@ -205,9 +203,7 @@ fn add_const_impl( | |||
205 | if let Some(source) = const_.source(ctx.db) { | 203 | if let Some(source) = const_.source(ctx.db) { |
206 | let snippet = make_const_compl_syntax(&source.value); | 204 | let snippet = make_const_compl_syntax(&source.value); |
207 | 205 | ||
208 | let range = | 206 | let range = replacement_range(ctx, const_def_node); |
209 | TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); | ||
210 | |||
211 | let mut item = | 207 | let mut item = |
212 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); | 208 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); |
213 | item.text_edit(TextEdit::replace(range, snippet)) | 209 | item.text_edit(TextEdit::replace(range, snippet)) |
@@ -242,6 +238,21 @@ fn make_const_compl_syntax(const_: &ast::Const) -> String { | |||
242 | format!("{} = ", syntax.trim_end()) | 238 | format!("{} = ", syntax.trim_end()) |
243 | } | 239 | } |
244 | 240 | ||
241 | fn replacement_range(ctx: &CompletionContext, item: &SyntaxNode) -> TextRange { | ||
242 | let first_child = item | ||
243 | .children_with_tokens() | ||
244 | .find(|child| { | ||
245 | let kind = child.kind(); | ||
246 | match kind { | ||
247 | SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR => false, | ||
248 | _ => true, | ||
249 | } | ||
250 | }) | ||
251 | .unwrap_or(SyntaxElement::Node(item.clone())); | ||
252 | |||
253 | TextRange::new(first_child.text_range().start(), ctx.source_range().end()) | ||
254 | } | ||
255 | |||
245 | #[cfg(test)] | 256 | #[cfg(test)] |
246 | mod tests { | 257 | mod tests { |
247 | use expect_test::{expect, Expect}; | 258 | use expect_test::{expect, Expect}; |
@@ -734,4 +745,50 @@ impl Test for T {{ | |||
734 | test("CONST", "const $0", "const CONST: u16 = ", next_sibling); | 745 | test("CONST", "const $0", "const CONST: u16 = ", next_sibling); |
735 | } | 746 | } |
736 | } | 747 | } |
748 | |||
749 | #[test] | ||
750 | fn snippet_does_not_overwrite_comment_or_attr() { | ||
751 | let test = |completion: &str, hint: &str, completed: &str| { | ||
752 | check_edit( | ||
753 | completion, | ||
754 | &format!( | ||
755 | r#" | ||
756 | trait Foo {{ | ||
757 | type Type; | ||
758 | fn function(); | ||
759 | const CONST: i32 = 0; | ||
760 | }} | ||
761 | struct T; | ||
762 | |||
763 | impl Foo for T {{ | ||
764 | // Comment | ||
765 | #[bar] | ||
766 | {} | ||
767 | }} | ||
768 | "#, | ||
769 | hint | ||
770 | ), | ||
771 | &format!( | ||
772 | r#" | ||
773 | trait Foo {{ | ||
774 | type Type; | ||
775 | fn function(); | ||
776 | const CONST: i32 = 0; | ||
777 | }} | ||
778 | struct T; | ||
779 | |||
780 | impl Foo for T {{ | ||
781 | // Comment | ||
782 | #[bar] | ||
783 | {} | ||
784 | }} | ||
785 | "#, | ||
786 | completed | ||
787 | ), | ||
788 | ) | ||
789 | }; | ||
790 | test("function", "fn f$0", "fn function() {\n $0\n}"); | ||
791 | test("Type", "type T$0", "type Type = "); | ||
792 | test("CONST", "const C$0", "const CONST: i32 = "); | ||
793 | } | ||
737 | } | 794 | } |