@@ -14,14 +14,15 @@ pub(crate) mod macro_in_item_position;
14
14
pub ( crate ) mod trait_impl;
15
15
pub ( crate ) mod mod_;
16
16
17
- use hir:: { HasAttrs , HasSource , HirDisplay , ModPath , Mutability , ScopeDef , StructKind , Type } ;
18
- use itertools:: Itertools ;
17
+ use hir:: { HasAttrs , HasSource , HirDisplay , ModPath , Mutability , ScopeDef , Type } ;
19
18
use syntax:: { ast:: NameOwner , display:: * } ;
20
19
use test_utils:: mark;
21
20
22
21
use crate :: {
23
- item:: Builder , CompletionContext , CompletionItem , CompletionItemKind , CompletionKind ,
24
- CompletionScore , RootDatabase ,
22
+ item:: Builder ,
23
+ render:: { EnumVariantRender , FunctionRender , MacroRender } ,
24
+ CompletionContext , CompletionItem , CompletionItemKind , CompletionKind , CompletionScore ,
25
+ RootDatabase ,
25
26
} ;
26
27
27
28
/// Represents an in-progress set of completions being built.
@@ -189,50 +190,14 @@ impl Completions {
189
190
name : Option < String > ,
190
191
macro_ : hir:: MacroDef ,
191
192
) {
192
- // FIXME: Currently proc-macro do not have ast-node,
193
- // such that it does not have source
194
- if macro_. is_proc_macro ( ) {
195
- return ;
196
- }
197
-
198
193
let name = match name {
199
194
Some ( it) => it,
200
195
None => return ,
201
196
} ;
202
197
203
- let ast_node = macro_. source ( ctx. db ) . value ;
204
- let detail = macro_label ( & ast_node) ;
205
-
206
- let docs = macro_. docs ( ctx. db ) ;
207
-
208
- let mut builder = CompletionItem :: new (
209
- CompletionKind :: Reference ,
210
- ctx. source_range ( ) ,
211
- & format ! ( "{}!" , name) ,
212
- )
213
- . kind ( CompletionItemKind :: Macro )
214
- . set_documentation ( docs. clone ( ) )
215
- . set_deprecated ( is_deprecated ( macro_, ctx. db ) )
216
- . detail ( detail) ;
217
-
218
- let needs_bang = ctx. use_item_syntax . is_none ( ) && !ctx. is_macro_call ;
219
- builder = match ctx. config . snippet_cap {
220
- Some ( cap) if needs_bang => {
221
- let docs = docs. as_ref ( ) . map_or ( "" , |s| s. as_str ( ) ) ;
222
- let ( bra, ket) = guess_macro_braces ( & name, docs) ;
223
- builder
224
- . insert_snippet ( cap, format ! ( "{}!{}$0{}" , name, bra, ket) )
225
- . label ( format ! ( "{}!{}…{}" , name, bra, ket) )
226
- . lookup_by ( format ! ( "{}!" , name) )
227
- }
228
- None if needs_bang => builder. insert_text ( format ! ( "{}!" , name) ) ,
229
- _ => {
230
- mark:: hit!( dont_insert_macro_call_parens_unncessary) ;
231
- builder. insert_text ( name)
232
- }
233
- } ;
234
-
235
- self . add ( builder. build ( ) ) ;
198
+ if let Some ( item) = MacroRender :: new ( ctx. into ( ) , name, macro_) . render ( ) {
199
+ self . add ( item) ;
200
+ }
236
201
}
237
202
238
203
pub ( crate ) fn add_function (
@@ -241,50 +206,9 @@ impl Completions {
241
206
func : hir:: Function ,
242
207
local_name : Option < String > ,
243
208
) {
244
- fn add_arg ( arg : & str , ty : & Type , ctx : & CompletionContext ) -> String {
245
- if let Some ( derefed_ty) = ty. remove_ref ( ) {
246
- for ( name, local) in ctx. locals . iter ( ) {
247
- if name == arg && local. ty ( ctx. db ) == derefed_ty {
248
- return ( if ty. is_mutable_reference ( ) { "&mut " } else { "&" } ) . to_string ( )
249
- + & arg. to_string ( ) ;
250
- }
251
- }
252
- }
253
- arg. to_string ( )
254
- } ;
255
- let name = local_name. unwrap_or_else ( || func. name ( ctx. db ) . to_string ( ) ) ;
256
- let ast_node = func. source ( ctx. db ) . value ;
257
-
258
- let mut builder =
259
- CompletionItem :: new ( CompletionKind :: Reference , ctx. source_range ( ) , name. clone ( ) )
260
- . kind ( if func. self_param ( ctx. db ) . is_some ( ) {
261
- CompletionItemKind :: Method
262
- } else {
263
- CompletionItemKind :: Function
264
- } )
265
- . set_documentation ( func. docs ( ctx. db ) )
266
- . set_deprecated ( is_deprecated ( func, ctx. db ) )
267
- . detail ( function_declaration ( & ast_node) ) ;
268
-
269
- let params_ty = func. params ( ctx. db ) ;
270
- let params = ast_node
271
- . param_list ( )
272
- . into_iter ( )
273
- . flat_map ( |it| it. params ( ) )
274
- . zip ( params_ty)
275
- . flat_map ( |( it, param_ty) | {
276
- if let Some ( pat) = it. pat ( ) {
277
- let name = pat. to_string ( ) ;
278
- let arg = name. trim_start_matches ( "mut " ) . trim_start_matches ( '_' ) ;
279
- return Some ( add_arg ( arg, param_ty. ty ( ) , ctx) ) ;
280
- }
281
- None
282
- } )
283
- . collect ( ) ;
209
+ let item = FunctionRender :: new ( ctx. into ( ) , local_name, func) . render ( ) ;
284
210
285
- builder = builder. add_call_parens ( ctx, name, Params :: Named ( params) ) ;
286
-
287
- self . add ( builder. build ( ) )
211
+ self . add ( item)
288
212
}
289
213
290
214
pub ( crate ) fn add_const ( & mut self , ctx : & CompletionContext , constant : hir:: Const ) {
@@ -325,7 +249,8 @@ impl Completions {
325
249
variant : hir:: EnumVariant ,
326
250
path : ModPath ,
327
251
) {
328
- self . add_enum_variant_impl ( ctx, variant, None , Some ( path) )
252
+ let item = EnumVariantRender :: new ( ctx. into ( ) , None , variant, Some ( path) ) . render ( ) ;
253
+ self . add ( item) ;
329
254
}
330
255
331
256
pub ( crate ) fn add_enum_variant (
@@ -334,63 +259,8 @@ impl Completions {
334
259
variant : hir:: EnumVariant ,
335
260
local_name : Option < String > ,
336
261
) {
337
- self . add_enum_variant_impl ( ctx, variant, local_name, None )
338
- }
339
-
340
- fn add_enum_variant_impl (
341
- & mut self ,
342
- ctx : & CompletionContext ,
343
- variant : hir:: EnumVariant ,
344
- local_name : Option < String > ,
345
- path : Option < ModPath > ,
346
- ) {
347
- let is_deprecated = is_deprecated ( variant, ctx. db ) ;
348
- let name = local_name. unwrap_or_else ( || variant. name ( ctx. db ) . to_string ( ) ) ;
349
- let ( qualified_name, short_qualified_name) = match & path {
350
- Some ( path) => {
351
- let full = path. to_string ( ) ;
352
- let short =
353
- path. segments [ path. segments . len ( ) . saturating_sub ( 2 ) ..] . iter ( ) . join ( "::" ) ;
354
- ( full, short)
355
- }
356
- None => ( name. to_string ( ) , name. to_string ( ) ) ,
357
- } ;
358
- let detail_types = variant
359
- . fields ( ctx. db )
360
- . into_iter ( )
361
- . map ( |field| ( field. name ( ctx. db ) , field. signature_ty ( ctx. db ) ) ) ;
362
- let variant_kind = variant. kind ( ctx. db ) ;
363
- let detail = match variant_kind {
364
- StructKind :: Tuple | StructKind :: Unit => format ! (
365
- "({})" ,
366
- detail_types. map( |( _, t) | t. display( ctx. db) . to_string( ) ) . format( ", " )
367
- ) ,
368
- StructKind :: Record => format ! (
369
- "{{ {} }}" ,
370
- detail_types
371
- . map( |( n, t) | format!( "{}: {}" , n, t. display( ctx. db) . to_string( ) ) )
372
- . format( ", " )
373
- ) ,
374
- } ;
375
- let mut res = CompletionItem :: new (
376
- CompletionKind :: Reference ,
377
- ctx. source_range ( ) ,
378
- qualified_name. clone ( ) ,
379
- )
380
- . kind ( CompletionItemKind :: EnumVariant )
381
- . set_documentation ( variant. docs ( ctx. db ) )
382
- . set_deprecated ( is_deprecated)
383
- . detail ( detail) ;
384
-
385
- if variant_kind == StructKind :: Tuple {
386
- mark:: hit!( inserts_parens_for_tuple_enums) ;
387
- let params = Params :: Anonymous ( variant. fields ( ctx. db ) . len ( ) ) ;
388
- res = res. add_call_parens ( ctx, short_qualified_name, params)
389
- } else if path. is_some ( ) {
390
- res = res. lookup_by ( short_qualified_name) ;
391
- }
392
-
393
- res. add_to ( self ) ;
262
+ let item = EnumVariantRender :: new ( ctx. into ( ) , local_name, variant, None ) . render ( ) ;
263
+ self . add ( item) ;
394
264
}
395
265
}
396
266
@@ -434,112 +304,10 @@ fn compute_score(ctx: &CompletionContext, ty: &Type, name: &str) -> Option<Compl
434
304
compute_score_from_active ( & active_type, & active_name, ty, name)
435
305
}
436
306
437
- enum Params {
438
- Named ( Vec < String > ) ,
439
- Anonymous ( usize ) ,
440
- }
441
-
442
- impl Params {
443
- fn len ( & self ) -> usize {
444
- match self {
445
- Params :: Named ( xs) => xs. len ( ) ,
446
- Params :: Anonymous ( len) => * len,
447
- }
448
- }
449
-
450
- fn is_empty ( & self ) -> bool {
451
- self . len ( ) == 0
452
- }
453
- }
454
-
455
- impl Builder {
456
- fn add_call_parens ( mut self , ctx : & CompletionContext , name : String , params : Params ) -> Builder {
457
- if !ctx. config . add_call_parenthesis {
458
- return self ;
459
- }
460
- if ctx. use_item_syntax . is_some ( ) {
461
- mark:: hit!( no_parens_in_use_item) ;
462
- return self ;
463
- }
464
- if ctx. is_pattern_call {
465
- mark:: hit!( dont_duplicate_pattern_parens) ;
466
- return self ;
467
- }
468
- if ctx. is_call {
469
- return self ;
470
- }
471
-
472
- // Don't add parentheses if the expected type is some function reference.
473
- if let Some ( ty) = & ctx. expected_type {
474
- if ty. is_fn ( ) {
475
- mark:: hit!( no_call_parens_if_fn_ptr_needed) ;
476
- return self ;
477
- }
478
- }
479
-
480
- let cap = match ctx. config . snippet_cap {
481
- Some ( it) => it,
482
- None => return self ,
483
- } ;
484
- // If not an import, add parenthesis automatically.
485
- mark:: hit!( inserts_parens_for_function_calls) ;
486
-
487
- let ( snippet, label) = if params. is_empty ( ) {
488
- ( format ! ( "{}()$0" , name) , format ! ( "{}()" , name) )
489
- } else {
490
- self = self . trigger_call_info ( ) ;
491
- let snippet = match ( ctx. config . add_call_argument_snippets , params) {
492
- ( true , Params :: Named ( params) ) => {
493
- let function_params_snippet =
494
- params. iter ( ) . enumerate ( ) . format_with ( ", " , |( index, param_name) , f| {
495
- f ( & format_args ! ( "${{{}:{}}}" , index + 1 , param_name) )
496
- } ) ;
497
- format ! ( "{}({})$0" , name, function_params_snippet)
498
- }
499
- _ => {
500
- mark:: hit!( suppress_arg_snippets) ;
501
- format ! ( "{}($0)" , name)
502
- }
503
- } ;
504
-
505
- ( snippet, format ! ( "{}(…)" , name) )
506
- } ;
507
- self . lookup_by ( name) . label ( label) . insert_snippet ( cap, snippet)
508
- }
509
- }
510
-
511
307
fn is_deprecated ( node : impl HasAttrs , db : & RootDatabase ) -> bool {
512
308
node. attrs ( db) . by_key ( "deprecated" ) . exists ( )
513
309
}
514
310
515
- fn guess_macro_braces ( macro_name : & str , docs : & str ) -> ( & ' static str , & ' static str ) {
516
- let mut votes = [ 0 , 0 , 0 ] ;
517
- for ( idx, s) in docs. match_indices ( & macro_name) {
518
- let ( before, after) = ( & docs[ ..idx] , & docs[ idx + s. len ( ) ..] ) ;
519
- // Ensure to match the full word
520
- if after. starts_with ( '!' )
521
- && !before. ends_with ( |c : char | c == '_' || c. is_ascii_alphanumeric ( ) )
522
- {
523
- // It may have spaces before the braces like `foo! {}`
524
- match after[ 1 ..] . chars ( ) . find ( |& c| !c. is_whitespace ( ) ) {
525
- Some ( '{' ) => votes[ 0 ] += 1 ,
526
- Some ( '[' ) => votes[ 1 ] += 1 ,
527
- Some ( '(' ) => votes[ 2 ] += 1 ,
528
- _ => { }
529
- }
530
- }
531
- }
532
-
533
- // Insert a space before `{}`.
534
- // We prefer the last one when some votes equal.
535
- let ( _vote, ( bra, ket) ) = votes
536
- . iter ( )
537
- . zip ( & [ ( " {" , "}" ) , ( "[" , "]" ) , ( "(" , ")" ) ] )
538
- . max_by_key ( |& ( & vote, _) | vote)
539
- . unwrap ( ) ;
540
- ( * bra, * ket)
541
- }
542
-
543
311
#[ cfg( test) ]
544
312
mod tests {
545
313
use std:: cmp:: Reverse ;
0 commit comments