4
4
use std:: collections:: BTreeSet ;
5
5
6
6
use either:: Either ;
7
- use hir:: { AssocItem , GenericParam , HasAttrs , HirDisplay , Semantics , Trait } ;
8
- use ide_db:: { active_parameter:: callable_for_node, base_db:: FilePosition } ;
7
+ use hir:: {
8
+ AssocItem , GenericParam , HasAttrs , HirDisplay , ModuleDef , PathResolution , Semantics , Trait ,
9
+ } ;
10
+ use ide_db:: { active_parameter:: callable_for_node, base_db:: FilePosition , FxIndexMap } ;
9
11
use stdx:: format_to;
10
12
use syntax:: {
11
13
algo,
12
14
ast:: { self , HasArgList } ,
13
- match_ast, AstNode , Direction , SyntaxToken , TextRange , TextSize ,
15
+ match_ast, AstNode , Direction , SyntaxKind , SyntaxToken , TextRange , TextSize ,
14
16
} ;
15
17
16
18
use crate :: RootDatabase ;
@@ -37,14 +39,18 @@ impl SignatureHelp {
37
39
}
38
40
39
41
fn push_call_param ( & mut self , param : & str ) {
40
- self . push_param ( '(' , param) ;
42
+ self . push_param ( "(" , param) ;
41
43
}
42
44
43
45
fn push_generic_param ( & mut self , param : & str ) {
44
- self . push_param ( '<' , param) ;
46
+ self . push_param ( "<" , param) ;
47
+ }
48
+
49
+ fn push_record_field ( & mut self , param : & str ) {
50
+ self . push_param ( "{ " , param) ;
45
51
}
46
52
47
- fn push_param ( & mut self , opening_delim : char , param : & str ) {
53
+ fn push_param ( & mut self , opening_delim : & str , param : & str ) {
48
54
if !self . signature . ends_with ( opening_delim) {
49
55
self . signature . push_str ( ", " ) ;
50
56
}
@@ -85,14 +91,23 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
85
91
}
86
92
return signature_help_for_generics( & sema, garg_list, token) ;
87
93
} ,
94
+ ast:: RecordExpr ( record) => {
95
+ let cursor_outside = record. record_expr_field_list( ) . and_then( |list| list. r_curly_token( ) ) . as_ref( ) == Some ( & token) ;
96
+ if cursor_outside {
97
+ continue ;
98
+ }
99
+ return signature_help_for_record_lit( & sema, record, token) ;
100
+ } ,
88
101
_ => ( ) ,
89
102
}
90
103
}
91
104
92
105
// Stop at multi-line expressions, since the signature of the outer call is not very
93
106
// helpful inside them.
94
107
if let Some ( expr) = ast:: Expr :: cast ( node. clone ( ) ) {
95
- if expr. syntax ( ) . text ( ) . contains_char ( '\n' ) {
108
+ if expr. syntax ( ) . text ( ) . contains_char ( '\n' )
109
+ && expr. syntax ( ) . kind ( ) != SyntaxKind :: RECORD_EXPR
110
+ {
96
111
return None ;
97
112
}
98
113
}
@@ -368,6 +383,86 @@ fn add_assoc_type_bindings(
368
383
}
369
384
}
370
385
386
+ fn signature_help_for_record_lit (
387
+ sema : & Semantics < ' _ , RootDatabase > ,
388
+ record : ast:: RecordExpr ,
389
+ token : SyntaxToken ,
390
+ ) -> Option < SignatureHelp > {
391
+ let arg_list = record
392
+ . syntax ( )
393
+ . ancestors ( )
394
+ . filter_map ( ast:: RecordExpr :: cast)
395
+ . find ( |list| list. syntax ( ) . text_range ( ) . contains ( token. text_range ( ) . start ( ) ) ) ?;
396
+
397
+ let active_parameter = arg_list
398
+ . record_expr_field_list ( ) ?
399
+ . fields ( )
400
+ . take_while ( |arg| arg. syntax ( ) . text_range ( ) . end ( ) <= token. text_range ( ) . start ( ) )
401
+ . count ( ) ;
402
+
403
+ let mut res = SignatureHelp {
404
+ doc : None ,
405
+ signature : String :: new ( ) ,
406
+ parameters : vec ! [ ] ,
407
+ active_parameter : Some ( active_parameter) ,
408
+ } ;
409
+
410
+ let fields;
411
+
412
+ let db = sema. db ;
413
+ let path_res = sema. resolve_path ( & record. path ( ) ?) ?;
414
+ if let PathResolution :: Def ( ModuleDef :: Variant ( variant) ) = path_res {
415
+ fields = variant. fields ( db) ;
416
+ let en = variant. parent_enum ( db) ;
417
+
418
+ res. doc = en. docs ( db) . map ( |it| it. into ( ) ) ;
419
+ format_to ! ( res. signature, "enum {}::{} {{ " , en. name( db) , variant. name( db) ) ;
420
+ } else {
421
+ let adt = match path_res {
422
+ PathResolution :: SelfType ( imp) => imp. self_ty ( db) . as_adt ( ) ?,
423
+ PathResolution :: Def ( ModuleDef :: Adt ( adt) ) => adt,
424
+ _ => return None ,
425
+ } ;
426
+
427
+ match adt {
428
+ hir:: Adt :: Struct ( it) => {
429
+ fields = it. fields ( db) ;
430
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
431
+ format_to ! ( res. signature, "struct {} {{ " , it. name( db) ) ;
432
+ }
433
+ hir:: Adt :: Union ( it) => {
434
+ fields = it. fields ( db) ;
435
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
436
+ format_to ! ( res. signature, "union {} {{ " , it. name( db) ) ;
437
+ }
438
+ _ => return None ,
439
+ }
440
+ }
441
+
442
+ let mut fields =
443
+ fields. into_iter ( ) . map ( |field| ( field. name ( db) , Some ( field) ) ) . collect :: < FxIndexMap < _ , _ > > ( ) ;
444
+ let mut buf = String :: new ( ) ;
445
+ for field in record. record_expr_field_list ( ) ?. fields ( ) {
446
+ let Some ( ( field, _, ty) ) = sema. resolve_record_field ( & field) else { continue } ;
447
+ let name = field. name ( db) ;
448
+ format_to ! ( buf, "{name}: {}" , ty. display_truncated( db, Some ( 20 ) ) ) ;
449
+ res. push_record_field ( & buf) ;
450
+ buf. clear ( ) ;
451
+
452
+ if let Some ( field) = fields. get_mut ( & name) {
453
+ * field = None ;
454
+ }
455
+ }
456
+ for ( name, field) in fields {
457
+ let Some ( field) = field else { continue } ;
458
+ format_to ! ( buf, "{name}: {}" , field. ty( db) . display_truncated( db, Some ( 20 ) ) ) ;
459
+ res. push_record_field ( & buf) ;
460
+ buf. clear ( ) ;
461
+ }
462
+ res. signature . push_str ( " }" ) ;
463
+ Some ( res)
464
+ }
465
+
371
466
#[ cfg( test) ]
372
467
mod tests {
373
468
use std:: iter;
@@ -1405,4 +1500,98 @@ fn take<C, Error>(
1405
1500
"# ] ] ,
1406
1501
) ;
1407
1502
}
1503
+
1504
+ #[ test]
1505
+ fn record_literal ( ) {
1506
+ check (
1507
+ r#"
1508
+ struct Strukt<T, U = ()> {
1509
+ t: T,
1510
+ u: U,
1511
+ unit: (),
1512
+ }
1513
+ fn f() {
1514
+ Strukt {
1515
+ u: 0,
1516
+ $0
1517
+ }
1518
+ }
1519
+ "# ,
1520
+ expect ! [ [ r#"
1521
+ struct Strukt { u: i32, t: T, unit: () }
1522
+ ------ ^^^^ --------
1523
+ "# ] ] ,
1524
+ ) ;
1525
+ }
1526
+
1527
+ #[ test]
1528
+ fn record_literal_nonexistent_field ( ) {
1529
+ check (
1530
+ r#"
1531
+ struct Strukt {
1532
+ a: u8,
1533
+ }
1534
+ fn f() {
1535
+ Strukt {
1536
+ b: 8,
1537
+ $0
1538
+ }
1539
+ }
1540
+ "# ,
1541
+ expect ! [ [ r#"
1542
+ struct Strukt { a: u8 }
1543
+ -----
1544
+ "# ] ] ,
1545
+ ) ;
1546
+ }
1547
+
1548
+ #[ test]
1549
+ fn tuple_variant_record_literal ( ) {
1550
+ check (
1551
+ r#"
1552
+ enum Opt {
1553
+ Some(u8),
1554
+ }
1555
+ fn f() {
1556
+ Opt::Some {$0}
1557
+ }
1558
+ "# ,
1559
+ expect ! [ [ r#"
1560
+ enum Opt::Some { 0: u8 }
1561
+ ^^^^^
1562
+ "# ] ] ,
1563
+ ) ;
1564
+ check (
1565
+ r#"
1566
+ enum Opt {
1567
+ Some(u8),
1568
+ }
1569
+ fn f() {
1570
+ Opt::Some {0:0,$0}
1571
+ }
1572
+ "# ,
1573
+ expect ! [ [ r#"
1574
+ enum Opt::Some { 0: u8 }
1575
+ -----
1576
+ "# ] ] ,
1577
+ ) ;
1578
+ }
1579
+
1580
+ #[ test]
1581
+ fn record_literal_self ( ) {
1582
+ check (
1583
+ r#"
1584
+ struct S { t: u8 }
1585
+ impl S {
1586
+ fn new() -> Self {
1587
+ Self { $0 }
1588
+ }
1589
+ }
1590
+ "# ,
1591
+ expect ! [ [ r#"
1592
+ struct S { t: u8 }
1593
+ ^^^^^
1594
+ "# ] ] ,
1595
+ ) ;
1596
+ }
1408
1597
}
0 commit comments