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,81 @@ 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+ 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+
371461#[ cfg( test) ]
372462mod tests {
373463 use std:: iter;
@@ -1405,4 +1495,80 @@ fn take<C, Error>(
14051495 "# ] ] ,
14061496 ) ;
14071497 }
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+ }
14081574}
0 commit comments