11use hir:: { HasSource , HirDisplay , InRealFile } ;
22use ide_db:: assists:: { AssistId , AssistKind } ;
33use syntax:: {
4- ast:: { self , make , HasArgList } ,
4+ ast:: { self , syntax_factory :: SyntaxFactory , HasArgList } ,
55 match_ast, AstNode , SyntaxNode ,
66} ;
77
@@ -33,7 +33,7 @@ use crate::assist_context::{AssistContext, Assists};
3333// ```
3434pub ( crate ) fn generate_enum_variant ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
3535 let path: ast:: Path = ctx. find_node_at_offset ( ) ?;
36- let parent = path_parent ( & path) ?;
36+ let parent = PathParent :: new ( & path) ?;
3737
3838 if ctx. sema . resolve_path ( & path) . is_some ( ) {
3939 // No need to generate anything if the path resolves
@@ -46,14 +46,32 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>)
4646 return None ;
4747 }
4848
49- if let Some ( hir:: PathResolution :: Def ( hir:: ModuleDef :: Adt ( hir:: Adt :: Enum ( e) ) ) ) =
49+ let Some ( hir:: PathResolution :: Def ( hir:: ModuleDef :: Adt ( hir:: Adt :: Enum ( e) ) ) ) =
5050 ctx. sema . resolve_path ( & path. qualifier ( ) ?)
51- {
52- let target = path. syntax ( ) . text_range ( ) ;
53- return add_variant_to_accumulator ( acc, ctx, target, e, & name_ref, parent) ;
54- }
51+ else {
52+ return None ;
53+ } ;
5554
56- None
55+ let target = path. syntax ( ) . text_range ( ) ;
56+ let name_ref: & ast:: NameRef = & name_ref;
57+ let db = ctx. db ( ) ;
58+ let InRealFile { file_id, value : enum_node } = e. source ( db) ?. original_ast_node_rooted ( db) ?;
59+
60+ acc. add (
61+ AssistId ( "generate_enum_variant" , AssistKind :: Generate ) ,
62+ "Generate variant" ,
63+ target,
64+ |builder| {
65+ let mut editor = builder. make_editor ( enum_node. syntax ( ) ) ;
66+ let make = SyntaxFactory :: new ( ) ;
67+ let field_list = parent. make_field_list ( ctx, & make) ;
68+ let variant = make. variant ( None , make. name ( & name_ref. text ( ) ) , field_list, None ) ;
69+ if let Some ( it) = enum_node. variant_list ( ) {
70+ it. add_variant ( & mut editor, & variant) ;
71+ }
72+ builder. add_file_edits ( file_id, editor) ;
73+ } ,
74+ )
5775}
5876
5977#[ derive( Debug ) ]
@@ -65,6 +83,20 @@ enum PathParent {
6583}
6684
6785impl PathParent {
86+ fn new ( path : & ast:: Path ) -> Option < Self > {
87+ let parent = path. syntax ( ) . parent ( ) ?;
88+
89+ match_ast ! {
90+ match parent {
91+ ast:: PathExpr ( it) => Some ( PathParent :: PathExpr ( it) ) ,
92+ ast:: RecordExpr ( it) => Some ( PathParent :: RecordExpr ( it) ) ,
93+ ast:: PathPat ( it) => Some ( PathParent :: PathPat ( it) ) ,
94+ ast:: UseTree ( it) => Some ( PathParent :: UseTree ( it) ) ,
95+ _ => None
96+ }
97+ }
98+ }
99+
68100 fn syntax ( & self ) -> & SyntaxNode {
69101 match self {
70102 PathParent :: PathExpr ( it) => it. syntax ( ) ,
@@ -74,97 +106,49 @@ impl PathParent {
74106 }
75107 }
76108
77- fn make_field_list ( & self , ctx : & AssistContext < ' _ > ) -> Option < ast:: FieldList > {
109+ fn make_field_list (
110+ & self ,
111+ ctx : & AssistContext < ' _ > ,
112+ make : & SyntaxFactory ,
113+ ) -> Option < ast:: FieldList > {
78114 let scope = ctx. sema . scope ( self . syntax ( ) ) ?;
79115
80116 match self {
81117 PathParent :: PathExpr ( it) => {
82- if let Some ( call_expr) = it. syntax ( ) . parent ( ) . and_then ( ast:: CallExpr :: cast) {
83- make_tuple_field_list ( call_expr, ctx, & scope)
84- } else {
85- None
86- }
118+ let call_expr = ast:: CallExpr :: cast ( it. syntax ( ) . parent ( ) ?) ?;
119+ let args = call_expr. arg_list ( ) ?. args ( ) ;
120+ let tuple_fields = args. map ( |arg| {
121+ let ty =
122+ expr_ty ( ctx, make, arg, & scope) . unwrap_or_else ( || make. ty_infer ( ) . into ( ) ) ;
123+ make. tuple_field ( None , ty)
124+ } ) ;
125+ Some ( make. tuple_field_list ( tuple_fields) . into ( ) )
126+ }
127+ PathParent :: RecordExpr ( it) => {
128+ let fields = it. record_expr_field_list ( ) ?. fields ( ) ;
129+ let record_fields = fields. map ( |field| {
130+ let name = name_from_field ( make, & field) ;
131+
132+ let ty = field
133+ . expr ( )
134+ . and_then ( |it| expr_ty ( ctx, make, it, & scope) )
135+ . unwrap_or_else ( || make. ty_infer ( ) . into ( ) ) ;
136+
137+ make. record_field ( None , name, ty)
138+ } ) ;
139+ Some ( make. record_field_list ( record_fields) . into ( ) )
87140 }
88- PathParent :: RecordExpr ( it) => make_record_field_list ( it, ctx, & scope) ,
89141 PathParent :: UseTree ( _) | PathParent :: PathPat ( _) => None ,
90142 }
91143 }
92144}
93145
94- fn path_parent ( path : & ast:: Path ) -> Option < PathParent > {
95- let parent = path. syntax ( ) . parent ( ) ?;
96-
97- match_ast ! {
98- match parent {
99- ast:: PathExpr ( it) => Some ( PathParent :: PathExpr ( it) ) ,
100- ast:: RecordExpr ( it) => Some ( PathParent :: RecordExpr ( it) ) ,
101- ast:: PathPat ( it) => Some ( PathParent :: PathPat ( it) ) ,
102- ast:: UseTree ( it) => Some ( PathParent :: UseTree ( it) ) ,
103- _ => None
104- }
105- }
106- }
107-
108- fn add_variant_to_accumulator (
109- acc : & mut Assists ,
110- ctx : & AssistContext < ' _ > ,
111- target : syntax:: TextRange ,
112- adt : hir:: Enum ,
113- name_ref : & ast:: NameRef ,
114- parent : PathParent ,
115- ) -> Option < ( ) > {
116- let db = ctx. db ( ) ;
117- let InRealFile { file_id, value : enum_node } = adt. source ( db) ?. original_ast_node_rooted ( db) ?;
118-
119- acc. add (
120- AssistId ( "generate_enum_variant" , AssistKind :: Generate ) ,
121- "Generate variant" ,
122- target,
123- |builder| {
124- builder. edit_file ( file_id. file_id ( ) ) ;
125- let node = builder. make_mut ( enum_node) ;
126- let variant = make_variant ( ctx, name_ref, parent) ;
127- if let Some ( it) = node. variant_list ( ) {
128- it. add_variant ( variant. clone_for_update ( ) )
129- }
130- } ,
131- )
132- }
133-
134- fn make_variant (
135- ctx : & AssistContext < ' _ > ,
136- name_ref : & ast:: NameRef ,
137- parent : PathParent ,
138- ) -> ast:: Variant {
139- let field_list = parent. make_field_list ( ctx) ;
140- make:: variant ( make:: name ( & name_ref. text ( ) ) , field_list)
141- }
142-
143- fn make_record_field_list (
144- record : & ast:: RecordExpr ,
145- ctx : & AssistContext < ' _ > ,
146- scope : & hir:: SemanticsScope < ' _ > ,
147- ) -> Option < ast:: FieldList > {
148- let fields = record. record_expr_field_list ( ) ?. fields ( ) ;
149- let record_fields = fields. map ( |field| {
150- let name = name_from_field ( & field) ;
151-
152- let ty = field
153- . expr ( )
154- . and_then ( |it| expr_ty ( ctx, it, scope) )
155- . unwrap_or_else ( make:: ty_placeholder) ;
156-
157- make:: record_field ( None , name, ty)
158- } ) ;
159- Some ( make:: record_field_list ( record_fields) . into ( ) )
160- }
161-
162- fn name_from_field ( field : & ast:: RecordExprField ) -> ast:: Name {
146+ fn name_from_field ( make : & SyntaxFactory , field : & ast:: RecordExprField ) -> ast:: Name {
163147 let text = match field. name_ref ( ) {
164148 Some ( it) => it. to_string ( ) ,
165149 None => name_from_field_shorthand ( field) . unwrap_or ( "unknown" . to_owned ( ) ) ,
166150 } ;
167- make:: name ( & text)
151+ make. name ( & text)
168152}
169153
170154fn name_from_field_shorthand ( field : & ast:: RecordExprField ) -> Option < String > {
@@ -175,27 +159,15 @@ fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option<String> {
175159 Some ( path. as_single_name_ref ( ) ?. to_string ( ) )
176160}
177161
178- fn make_tuple_field_list (
179- call_expr : ast:: CallExpr ,
180- ctx : & AssistContext < ' _ > ,
181- scope : & hir:: SemanticsScope < ' _ > ,
182- ) -> Option < ast:: FieldList > {
183- let args = call_expr. arg_list ( ) ?. args ( ) ;
184- let tuple_fields = args. map ( |arg| {
185- let ty = expr_ty ( ctx, arg, scope) . unwrap_or_else ( make:: ty_placeholder) ;
186- make:: tuple_field ( None , ty)
187- } ) ;
188- Some ( make:: tuple_field_list ( tuple_fields) . into ( ) )
189- }
190-
191162fn expr_ty (
192163 ctx : & AssistContext < ' _ > ,
164+ make : & SyntaxFactory ,
193165 arg : ast:: Expr ,
194166 scope : & hir:: SemanticsScope < ' _ > ,
195167) -> Option < ast:: Type > {
196168 let ty = ctx. sema . type_of_expr ( & arg) . map ( |it| it. adjusted ( ) ) ?;
197169 let text = ty. display_source_code ( ctx. db ( ) , scope. module ( ) . into ( ) , false ) . ok ( ) ?;
198- Some ( make:: ty ( & text) )
170+ Some ( make. ty ( & text) )
199171}
200172
201173#[ cfg( test) ]
0 commit comments