aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs69
-rw-r--r--crates/ide_completion/src/context.rs2
2 files changed, 62 insertions, 9 deletions
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index a26fe7c6c..968c0254d 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};
36use syntax::{ 36use 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};
41use text_edit::TextEdit; 41use 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,17 @@ fn make_const_compl_syntax(const_: &ast::Const) -> String {
242 format!("{} = ", syntax.trim_end()) 238 format!("{} = ", syntax.trim_end())
243} 239}
244 240
241fn replacement_range(ctx: &CompletionContext, item: &SyntaxNode) -> TextRange {
242 let first_child = item
243 .children_with_tokens()
244 .find(|child| {
245 !matches!(child.kind(), SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR)
246 })
247 .unwrap_or_else(|| SyntaxElement::Node(item.clone()));
248
249 TextRange::new(first_child.text_range().start(), ctx.source_range().end())
250}
251
245#[cfg(test)] 252#[cfg(test)]
246mod tests { 253mod tests {
247 use expect_test::{expect, Expect}; 254 use expect_test::{expect, Expect};
@@ -734,4 +741,50 @@ impl Test for T {{
734 test("CONST", "const $0", "const CONST: u16 = ", next_sibling); 741 test("CONST", "const $0", "const CONST: u16 = ", next_sibling);
735 } 742 }
736 } 743 }
744
745 #[test]
746 fn snippet_does_not_overwrite_comment_or_attr() {
747 let test = |completion: &str, hint: &str, completed: &str| {
748 check_edit(
749 completion,
750 &format!(
751 r#"
752trait Foo {{
753 type Type;
754 fn function();
755 const CONST: i32 = 0;
756}}
757struct T;
758
759impl Foo for T {{
760 // Comment
761 #[bar]
762 {}
763}}
764"#,
765 hint
766 ),
767 &format!(
768 r#"
769trait Foo {{
770 type Type;
771 fn function();
772 const CONST: i32 = 0;
773}}
774struct T;
775
776impl Foo for T {{
777 // Comment
778 #[bar]
779 {}
780}}
781"#,
782 completed
783 ),
784 )
785 };
786 test("function", "fn f$0", "fn function() {\n $0\n}");
787 test("Type", "type T$0", "type Type = ");
788 test("CONST", "const C$0", "const CONST: i32 = ");
789 }
737} 790}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index f3fcb712c..62ef40818 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -381,7 +381,7 @@ impl<'a> CompletionContext<'a> {
381 let def = self.sema.to_def(&it); 381 let def = self.sema.to_def(&it);
382 (def.map(|def| def.ret_type(self.db)), None) 382 (def.map(|def| def.ret_type(self.db)), None)
383 }, 383 },
384 ast::Stmt(it) => (None, None), 384 ast::Stmt(_it) => (None, None),
385 _ => { 385 _ => {
386 match node.parent() { 386 match node.parent() {
387 Some(n) => { 387 Some(n) => {