@@ -36,7 +36,7 @@ use ide_db::{traits::get_missing_assoc_items, SymbolKind};
3636use syntax:: {
3737 ast:: { self , edit, Impl } ,
3838 display:: function_declaration,
39- AstNode , SyntaxKind , SyntaxNode , TextRange , T ,
39+ AstNode , SyntaxElement , SyntaxKind , SyntaxNode , TextRange , T ,
4040} ;
4141use text_edit:: TextEdit ;
4242
@@ -154,8 +154,7 @@ fn add_function_impl(
154154 } else {
155155 CompletionItemKind :: SymbolKind ( SymbolKind :: Function )
156156 } ;
157- let range = TextRange :: new ( fn_def_node. text_range ( ) . start ( ) , ctx. source_range ( ) . end ( ) ) ;
158-
157+ let range = replacement_range ( ctx, fn_def_node) ;
159158 if let Some ( src) = func. source ( ctx. db ) {
160159 let function_decl = function_declaration ( & src. value ) ;
161160 match ctx. config . snippet_cap {
@@ -183,8 +182,7 @@ fn add_type_alias_impl(
183182
184183 let snippet = format ! ( "type {} = " , alias_name) ;
185184
186- let range = TextRange :: new ( type_def_node. text_range ( ) . start ( ) , ctx. source_range ( ) . end ( ) ) ;
187-
185+ let range = replacement_range ( ctx, type_def_node) ;
188186 let mut item = CompletionItem :: new ( CompletionKind :: Magic , ctx. source_range ( ) , snippet. clone ( ) ) ;
189187 item. text_edit ( TextEdit :: replace ( range, snippet) )
190188 . lookup_by ( alias_name)
@@ -205,9 +203,7 @@ fn add_const_impl(
205203 if let Some ( source) = const_. source ( ctx. db ) {
206204 let snippet = make_const_compl_syntax ( & source. value ) ;
207205
208- let range =
209- TextRange :: new ( const_def_node. text_range ( ) . start ( ) , ctx. source_range ( ) . end ( ) ) ;
210-
206+ let range = replacement_range ( ctx, const_def_node) ;
211207 let mut item =
212208 CompletionItem :: new ( CompletionKind :: Magic , ctx. source_range ( ) , snippet. clone ( ) ) ;
213209 item. text_edit ( TextEdit :: replace ( range, snippet) )
@@ -242,6 +238,21 @@ fn make_const_compl_syntax(const_: &ast::Const) -> String {
242238 format ! ( "{} = " , syntax. trim_end( ) )
243239}
244240
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+
245256#[ cfg( test) ]
246257mod tests {
247258 use expect_test:: { expect, Expect } ;
@@ -734,4 +745,50 @@ impl Test for T {{
734745 test ( "CONST" , "const $0" , "const CONST: u16 = " , next_sibling) ;
735746 }
736747 }
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+ }
737794}
0 commit comments