44use std:: collections:: BTreeSet ;
55
66use 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 } ;
911use stdx:: format_to;
1012use syntax:: {
1113 algo,
1214 ast:: { self , HasArgList } ,
13- match_ast, AstNode , Direction , SyntaxToken , TextRange , TextSize ,
15+ match_ast, AstNode , Direction , SyntaxKind , SyntaxToken , TextRange , TextSize ,
1416} ;
1517
1618use crate :: RootDatabase ;
@@ -37,14 +39,18 @@ impl SignatureHelp {
3739 }
3840
3941 fn push_call_param ( & mut self , param : & str ) {
40- self . push_param ( '(' , param) ;
42+ self . push_param ( "(" , param) ;
4143 }
4244
4345 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) ;
4551 }
4652
47- fn push_param ( & mut self , opening_delim : char , param : & str ) {
53+ fn push_param ( & mut self , opening_delim : & str , param : & str ) {
4854 if !self . signature . ends_with ( opening_delim) {
4955 self . signature . push_str ( ", " ) ;
5056 }
@@ -85,14 +91,23 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
8591 }
8692 return signature_help_for_generics( & sema, garg_list, token) ;
8793 } ,
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+ } ,
88101 _ => ( ) ,
89102 }
90103 }
91104
92105 // Stop at multi-line expressions, since the signature of the outer call is not very
93106 // helpful inside them.
94107 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+ {
96111 return None ;
97112 }
98113 }
@@ -368,6 +383,86 @@ fn add_assoc_type_bindings(
368383 }
369384}
370385
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+
371466#[ cfg( test) ]
372467mod tests {
373468 use std:: iter;
@@ -1405,4 +1500,98 @@ fn take<C, Error>(
14051500 "# ] ] ,
14061501 ) ;
14071502 }
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+ }
14081597}
0 commit comments