@@ -36,11 +36,13 @@ use ide_db::{path_transform::PathTransform, traits::get_missing_assoc_items, Sym
36
36
use syntax:: {
37
37
ast:: { self , edit_in_place:: AttrsOwnerEdit } ,
38
38
display:: function_declaration,
39
- AstNode , SyntaxElement , SyntaxKind , SyntaxNode , SyntaxToken , TextRange , T ,
39
+ AstNode , SyntaxElement , SyntaxKind , SyntaxNode , TextRange , T ,
40
40
} ;
41
41
use text_edit:: TextEdit ;
42
42
43
- use crate :: { CompletionContext , CompletionItem , CompletionItemKind , Completions } ;
43
+ use crate :: {
44
+ CompletionContext , CompletionItem , CompletionItemKind , CompletionRelevance , Completions ,
45
+ } ;
44
46
45
47
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
46
48
enum ImplCompletionKind {
@@ -51,84 +53,102 @@ enum ImplCompletionKind {
51
53
}
52
54
53
55
pub ( crate ) fn complete_trait_impl ( acc : & mut Completions , ctx : & CompletionContext ) {
54
- if let Some ( ( kind, trigger , impl_def) ) = completion_match ( ctx. token . clone ( ) ) {
56
+ if let Some ( ( kind, replacement_range , impl_def) ) = completion_match ( ctx) {
55
57
if let Some ( hir_impl) = ctx. sema . to_def ( & impl_def) {
56
58
get_missing_assoc_items ( & ctx. sema , & impl_def) . into_iter ( ) . for_each ( |item| {
57
59
match ( item, kind) {
58
60
(
59
61
hir:: AssocItem :: Function ( fn_item) ,
60
62
ImplCompletionKind :: All | ImplCompletionKind :: Fn ,
61
- ) => add_function_impl ( acc, ctx, & trigger , fn_item, hir_impl) ,
63
+ ) => add_function_impl ( acc, ctx, replacement_range , fn_item, hir_impl) ,
62
64
(
63
65
hir:: AssocItem :: TypeAlias ( type_item) ,
64
66
ImplCompletionKind :: All | ImplCompletionKind :: TypeAlias ,
65
- ) => add_type_alias_impl ( acc, ctx, & trigger , type_item) ,
67
+ ) => add_type_alias_impl ( acc, ctx, replacement_range , type_item) ,
66
68
(
67
69
hir:: AssocItem :: Const ( const_item) ,
68
70
ImplCompletionKind :: All | ImplCompletionKind :: Const ,
69
- ) => add_const_impl ( acc, ctx, & trigger , const_item, hir_impl) ,
71
+ ) => add_const_impl ( acc, ctx, replacement_range , const_item, hir_impl) ,
70
72
_ => { }
71
73
}
72
74
} ) ;
73
75
}
74
76
}
75
77
}
76
78
77
- fn completion_match ( mut token : SyntaxToken ) -> Option < ( ImplCompletionKind , SyntaxNode , ast:: Impl ) > {
79
+ fn completion_match ( ctx : & CompletionContext ) -> Option < ( ImplCompletionKind , TextRange , ast:: Impl ) > {
80
+ let token = ctx. token . clone ( ) ;
81
+
78
82
// For keyword without name like `impl .. { fn $0 }`, the current position is inside
79
83
// the whitespace token, which is outside `FN` syntax node.
80
84
// We need to follow the previous token in this case.
85
+ let mut token_before_ws = token. clone ( ) ;
81
86
if token. kind ( ) == SyntaxKind :: WHITESPACE {
82
- token = token. prev_token ( ) ?;
87
+ token_before_ws = token. prev_token ( ) ?;
83
88
}
84
89
85
- let parent_kind = token. parent ( ) . map_or ( SyntaxKind :: EOF , |it| it. kind ( ) ) ;
86
- let impl_item_offset = match token. kind ( ) {
87
- // `impl .. { const $0 }`
88
- // ERROR 0
89
- // CONST_KW <- *
90
- T ! [ const ] => 0 ,
91
- // `impl .. { fn/type $0 }`
92
- // FN/TYPE_ALIAS 0
93
- // FN_KW <- *
94
- T ! [ fn ] | T ! [ type ] => 0 ,
95
- // `impl .. { fn/type/const foo$0 }`
96
- // FN/TYPE_ALIAS/CONST 1
97
- // NAME 0
98
- // IDENT <- *
99
- SyntaxKind :: IDENT if parent_kind == SyntaxKind :: NAME => 1 ,
100
- // `impl .. { foo$0 }`
101
- // MACRO_CALL 3
102
- // PATH 2
103
- // PATH_SEGMENT 1
104
- // NAME_REF 0
105
- // IDENT <- *
106
- SyntaxKind :: IDENT if parent_kind == SyntaxKind :: NAME_REF => 3 ,
107
- _ => return None ,
108
- } ;
90
+ let parent_kind = token_before_ws. parent ( ) . map_or ( SyntaxKind :: EOF , |it| it. kind ( ) ) ;
91
+ if token. parent ( ) . map ( |n| n. kind ( ) ) == Some ( SyntaxKind :: ASSOC_ITEM_LIST )
92
+ && matches ! (
93
+ token_before_ws. kind( ) ,
94
+ SyntaxKind :: SEMICOLON | SyntaxKind :: R_CURLY | SyntaxKind :: L_CURLY
95
+ )
96
+ {
97
+ let impl_def = ast:: Impl :: cast ( token. parent ( ) ?. parent ( ) ?) ?;
98
+ let kind = ImplCompletionKind :: All ;
99
+ let replacement_range = TextRange :: empty ( ctx. position . offset ) ;
100
+ Some ( ( kind, replacement_range, impl_def) )
101
+ } else {
102
+ let impl_item_offset = match token_before_ws. kind ( ) {
103
+ // `impl .. { const $0 }`
104
+ // ERROR 0
105
+ // CONST_KW <- *
106
+ T ! [ const ] => 0 ,
107
+ // `impl .. { fn/type $0 }`
108
+ // FN/TYPE_ALIAS 0
109
+ // FN_KW <- *
110
+ T ! [ fn ] | T ! [ type ] => 0 ,
111
+ // `impl .. { fn/type/const foo$0 }`
112
+ // FN/TYPE_ALIAS/CONST 1
113
+ // NAME 0
114
+ // IDENT <- *
115
+ SyntaxKind :: IDENT if parent_kind == SyntaxKind :: NAME => 1 ,
116
+ // `impl .. { foo$0 }`
117
+ // MACRO_CALL 3
118
+ // PATH 2
119
+ // PATH_SEGMENT 1
120
+ // NAME_REF 0
121
+ // IDENT <- *
122
+ SyntaxKind :: IDENT if parent_kind == SyntaxKind :: NAME_REF => 3 ,
123
+ _ => return None ,
124
+ } ;
109
125
110
- let impl_item = token. ancestors ( ) . nth ( impl_item_offset) ?;
111
- // Must directly belong to an impl block.
112
- // IMPL
113
- // ASSOC_ITEM_LIST
114
- // <item>
115
- let impl_def = ast:: Impl :: cast ( impl_item. parent ( ) ?. parent ( ) ?) ?;
116
- let kind = match impl_item. kind ( ) {
117
- // `impl ... { const $0 fn/type/const }`
118
- _ if token. kind ( ) == T ! [ const ] => ImplCompletionKind :: Const ,
119
- SyntaxKind :: CONST | SyntaxKind :: ERROR => ImplCompletionKind :: Const ,
120
- SyntaxKind :: TYPE_ALIAS => ImplCompletionKind :: TypeAlias ,
121
- SyntaxKind :: FN => ImplCompletionKind :: Fn ,
122
- SyntaxKind :: MACRO_CALL => ImplCompletionKind :: All ,
123
- _ => return None ,
124
- } ;
125
- Some ( ( kind, impl_item, impl_def) )
126
+ let impl_item = token_before_ws. ancestors ( ) . nth ( impl_item_offset) ?;
127
+ // Must directly belong to an impl block.
128
+ // IMPL
129
+ // ASSOC_ITEM_LIST
130
+ // <item>
131
+ let impl_def = ast:: Impl :: cast ( impl_item. parent ( ) ?. parent ( ) ?) ?;
132
+ let kind = match impl_item. kind ( ) {
133
+ // `impl ... { const $0 fn/type/const }`
134
+ _ if token_before_ws. kind ( ) == T ! [ const ] => ImplCompletionKind :: Const ,
135
+ SyntaxKind :: CONST | SyntaxKind :: ERROR => ImplCompletionKind :: Const ,
136
+ SyntaxKind :: TYPE_ALIAS => ImplCompletionKind :: TypeAlias ,
137
+ SyntaxKind :: FN => ImplCompletionKind :: Fn ,
138
+ SyntaxKind :: MACRO_CALL => ImplCompletionKind :: All ,
139
+ _ => return None ,
140
+ } ;
141
+
142
+ let replacement_range = replacement_range ( ctx, & impl_item) ;
143
+
144
+ Some ( ( kind, replacement_range, impl_def) )
145
+ }
126
146
}
127
147
128
148
fn add_function_impl (
129
149
acc : & mut Completions ,
130
150
ctx : & CompletionContext ,
131
- fn_def_node : & SyntaxNode ,
151
+ replacement_range : TextRange ,
132
152
func : hir:: Function ,
133
153
impl_def : hir:: Impl ,
134
154
) {
@@ -146,9 +166,10 @@ fn add_function_impl(
146
166
CompletionItemKind :: SymbolKind ( SymbolKind :: Function )
147
167
} ;
148
168
149
- let range = replacement_range ( ctx, fn_def_node) ;
150
- let mut item = CompletionItem :: new ( completion_kind, range, label) ;
151
- item. lookup_by ( fn_name) . set_documentation ( func. docs ( ctx. db ) ) ;
169
+ let mut item = CompletionItem :: new ( completion_kind, replacement_range, label) ;
170
+ item. lookup_by ( fn_name)
171
+ . set_documentation ( func. docs ( ctx. db ) )
172
+ . set_relevance ( CompletionRelevance { is_item_from_trait : true , ..Default :: default ( ) } ) ;
152
173
153
174
if let Some ( source) = ctx. sema . source ( func) {
154
175
let assoc_item = ast:: AssocItem :: Fn ( source. value ) ;
@@ -162,11 +183,11 @@ fn add_function_impl(
162
183
match ctx. config . snippet_cap {
163
184
Some ( cap) => {
164
185
let snippet = format ! ( "{} {{\n $0\n }}" , function_decl) ;
165
- item. snippet_edit ( cap, TextEdit :: replace ( range , snippet) ) ;
186
+ item. snippet_edit ( cap, TextEdit :: replace ( replacement_range , snippet) ) ;
166
187
}
167
188
None => {
168
189
let header = format ! ( "{} {{" , function_decl) ;
169
- item. text_edit ( TextEdit :: replace ( range , header) ) ;
190
+ item. text_edit ( TextEdit :: replace ( replacement_range , header) ) ;
170
191
}
171
192
} ;
172
193
item. add_to ( acc) ;
@@ -201,25 +222,26 @@ fn get_transformed_assoc_item(
201
222
fn add_type_alias_impl (
202
223
acc : & mut Completions ,
203
224
ctx : & CompletionContext ,
204
- type_def_node : & SyntaxNode ,
225
+ replacement_range : TextRange ,
205
226
type_alias : hir:: TypeAlias ,
206
227
) {
207
228
let alias_name = type_alias. name ( ctx. db ) . to_smol_str ( ) ;
208
229
230
+ let label = format ! ( "type {} =" , alias_name) ;
209
231
let snippet = format ! ( "type {} = " , alias_name) ;
210
232
211
- let range = replacement_range ( ctx, type_def_node) ;
212
- let mut item = CompletionItem :: new ( SymbolKind :: TypeAlias , range, & snippet) ;
213
- item. text_edit ( TextEdit :: replace ( range, snippet) )
233
+ let mut item = CompletionItem :: new ( SymbolKind :: TypeAlias , replacement_range, label) ;
234
+ item. text_edit ( TextEdit :: replace ( replacement_range, snippet) )
214
235
. lookup_by ( alias_name)
215
- . set_documentation ( type_alias. docs ( ctx. db ) ) ;
236
+ . set_documentation ( type_alias. docs ( ctx. db ) )
237
+ . set_relevance ( CompletionRelevance { is_item_from_trait : true , ..Default :: default ( ) } ) ;
216
238
item. add_to ( acc) ;
217
239
}
218
240
219
241
fn add_const_impl (
220
242
acc : & mut Completions ,
221
243
ctx : & CompletionContext ,
222
- const_def_node : & SyntaxNode ,
244
+ replacement_range : TextRange ,
223
245
const_ : hir:: Const ,
224
246
impl_def : hir:: Impl ,
225
247
) {
@@ -234,13 +256,17 @@ fn add_const_impl(
234
256
_ => unreachable ! ( ) ,
235
257
} ;
236
258
237
- let snippet = make_const_compl_syntax ( & transformed_const) ;
259
+ let label = make_const_compl_syntax ( & transformed_const) ;
260
+ let snippet = format ! ( "{} " , label) ;
238
261
239
- let range = replacement_range ( ctx, const_def_node) ;
240
- let mut item = CompletionItem :: new ( SymbolKind :: Const , range, & snippet) ;
241
- item. text_edit ( TextEdit :: replace ( range, snippet) )
262
+ let mut item = CompletionItem :: new ( SymbolKind :: Const , replacement_range, label) ;
263
+ item. text_edit ( TextEdit :: replace ( replacement_range, snippet) )
242
264
. lookup_by ( const_name)
243
- . set_documentation ( const_. docs ( ctx. db ) ) ;
265
+ . set_documentation ( const_. docs ( ctx. db ) )
266
+ . set_relevance ( CompletionRelevance {
267
+ is_item_from_trait : true ,
268
+ ..Default :: default ( )
269
+ } ) ;
244
270
item. add_to ( acc) ;
245
271
}
246
272
}
@@ -267,7 +293,7 @@ fn make_const_compl_syntax(const_: &ast::Const) -> String {
267
293
268
294
let syntax = const_. syntax ( ) . text ( ) . slice ( range) . to_string ( ) ;
269
295
270
- format ! ( "{} = " , syntax. trim_end( ) )
296
+ format ! ( "{} =" , syntax. trim_end( ) )
271
297
}
272
298
273
299
fn replacement_range ( ctx : & CompletionContext , item : & SyntaxNode ) -> TextRange {
@@ -987,4 +1013,38 @@ where Self: SomeTrait<u32> {
987
1013
"# ,
988
1014
)
989
1015
}
1016
+
1017
+ #[ test]
1018
+ fn works_directly_in_impl ( ) {
1019
+ check (
1020
+ r#"
1021
+ trait Tr {
1022
+ fn required();
1023
+ }
1024
+
1025
+ impl Tr for () {
1026
+ $0
1027
+ }
1028
+ "# ,
1029
+ expect ! [ [ r#"
1030
+ fn fn required()
1031
+ "# ] ] ,
1032
+ ) ;
1033
+ check (
1034
+ r#"
1035
+ trait Tr {
1036
+ fn provided() {}
1037
+ fn required();
1038
+ }
1039
+
1040
+ impl Tr for () {
1041
+ fn provided() {}
1042
+ $0
1043
+ }
1044
+ "# ,
1045
+ expect ! [ [ r#"
1046
+ fn fn required()
1047
+ "# ] ] ,
1048
+ ) ;
1049
+ }
990
1050
}
0 commit comments