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,81 @@ 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
+ match sema. resolve_path ( & record. path ( ) ?) ? {
414
+ PathResolution :: Def ( ModuleDef :: Adt ( adt) ) => match adt {
415
+ hir:: Adt :: Struct ( it) => {
416
+ fields = it. fields ( db) ;
417
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
418
+ format_to ! ( res. signature, "struct {} {{ " , it. name( db) ) ;
419
+ }
420
+ hir:: Adt :: Union ( it) => {
421
+ fields = it. fields ( db) ;
422
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
423
+ format_to ! ( res. signature, "union {} {{ " , it. name( db) ) ;
424
+ }
425
+ _ => return None ,
426
+ } ,
427
+ PathResolution :: Def ( ModuleDef :: Variant ( variant) ) => {
428
+ fields = variant. fields ( db) ;
429
+ let en = variant. parent_enum ( db) ;
430
+
431
+ res. doc = en. docs ( db) . map ( |it| it. into ( ) ) ;
432
+ format_to ! ( res. signature, "enum {}::{} {{ " , en. name( db) , variant. name( db) ) ;
433
+ }
434
+ _ => return None ,
435
+ }
436
+
437
+ let mut fields =
438
+ fields. into_iter ( ) . map ( |field| ( field. name ( db) , Some ( field) ) ) . collect :: < FxIndexMap < _ , _ > > ( ) ;
439
+ let mut buf = String :: new ( ) ;
440
+ for field in record. record_expr_field_list ( ) ?. fields ( ) {
441
+ let Some ( ( field, _, ty) ) = sema. resolve_record_field ( & field) else { continue } ;
442
+ let name = field. name ( db) ;
443
+ format_to ! ( buf, "{name}: {}" , ty. display_truncated( db, Some ( 20 ) ) ) ;
444
+ res. push_record_field ( & buf) ;
445
+ buf. clear ( ) ;
446
+
447
+ if let Some ( field) = fields. get_mut ( & name) {
448
+ * field = None ;
449
+ }
450
+ }
451
+ for ( name, field) in fields {
452
+ let Some ( field) = field else { continue } ;
453
+ format_to ! ( buf, "{name}: {}" , field. ty( db) . display_truncated( db, Some ( 20 ) ) ) ;
454
+ res. push_record_field ( & buf) ;
455
+ buf. clear ( ) ;
456
+ }
457
+ res. signature . push_str ( " }" ) ;
458
+ Some ( res)
459
+ }
460
+
371
461
#[ cfg( test) ]
372
462
mod tests {
373
463
use std:: iter;
@@ -1405,4 +1495,80 @@ fn take<C, Error>(
1405
1495
"# ] ] ,
1406
1496
) ;
1407
1497
}
1498
+
1499
+ #[ test]
1500
+ fn record_literal ( ) {
1501
+ check (
1502
+ r#"
1503
+ struct Strukt<T, U = ()> {
1504
+ t: T,
1505
+ u: U,
1506
+ unit: (),
1507
+ }
1508
+ fn f() {
1509
+ Strukt {
1510
+ u: 0,
1511
+ $0
1512
+ }
1513
+ }
1514
+ "# ,
1515
+ expect ! [ [ r#"
1516
+ struct Strukt { u: i32, t: T, unit: () }
1517
+ ------ ^^^^ --------
1518
+ "# ] ] ,
1519
+ ) ;
1520
+ }
1521
+
1522
+ #[ test]
1523
+ fn record_literal_nonexistent_field ( ) {
1524
+ check (
1525
+ r#"
1526
+ struct Strukt {
1527
+ a: u8,
1528
+ }
1529
+ fn f() {
1530
+ Strukt {
1531
+ b: 8,
1532
+ $0
1533
+ }
1534
+ }
1535
+ "# ,
1536
+ expect ! [ [ r#"
1537
+ struct Strukt { a: u8 }
1538
+ -----
1539
+ "# ] ] ,
1540
+ ) ;
1541
+ }
1542
+
1543
+ #[ test]
1544
+ fn tuple_variant_record_literal ( ) {
1545
+ check (
1546
+ r#"
1547
+ enum Opt {
1548
+ Some(u8),
1549
+ }
1550
+ fn f() {
1551
+ Opt::Some {$0}
1552
+ }
1553
+ "# ,
1554
+ expect ! [ [ r#"
1555
+ enum Opt::Some { 0: u8 }
1556
+ ^^^^^
1557
+ "# ] ] ,
1558
+ ) ;
1559
+ check (
1560
+ r#"
1561
+ enum Opt {
1562
+ Some(u8),
1563
+ }
1564
+ fn f() {
1565
+ Opt::Some {0:0,$0}
1566
+ }
1567
+ "# ,
1568
+ expect ! [ [ r#"
1569
+ enum Opt::Some { 0: u8 }
1570
+ -----
1571
+ "# ] ] ,
1572
+ ) ;
1573
+ }
1408
1574
}
0 commit comments