1
1
use hir:: { HasSource , HirDisplay , InRealFile } ;
2
2
use ide_db:: assists:: { AssistId , AssistKind } ;
3
3
use syntax:: {
4
- ast:: { self , make , HasArgList } ,
4
+ ast:: { self , syntax_factory :: SyntaxFactory , HasArgList } ,
5
5
match_ast, AstNode , SyntaxNode ,
6
6
} ;
7
7
@@ -33,7 +33,7 @@ use crate::assist_context::{AssistContext, Assists};
33
33
// ```
34
34
pub ( crate ) fn generate_enum_variant ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
35
35
let path: ast:: Path = ctx. find_node_at_offset ( ) ?;
36
- let parent = path_parent ( & path) ?;
36
+ let parent = PathParent :: new ( & path) ?;
37
37
38
38
if ctx. sema . resolve_path ( & path) . is_some ( ) {
39
39
// No need to generate anything if the path resolves
@@ -46,14 +46,32 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>)
46
46
return None ;
47
47
}
48
48
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) ) ) ) =
50
50
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
+ } ;
55
54
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
+ )
57
75
}
58
76
59
77
#[ derive( Debug ) ]
@@ -65,6 +83,20 @@ enum PathParent {
65
83
}
66
84
67
85
impl 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
+
68
100
fn syntax ( & self ) -> & SyntaxNode {
69
101
match self {
70
102
PathParent :: PathExpr ( it) => it. syntax ( ) ,
@@ -74,97 +106,49 @@ impl PathParent {
74
106
}
75
107
}
76
108
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 > {
78
114
let scope = ctx. sema . scope ( self . syntax ( ) ) ?;
79
115
80
116
match self {
81
117
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 ( ) )
87
140
}
88
- PathParent :: RecordExpr ( it) => make_record_field_list ( it, ctx, & scope) ,
89
141
PathParent :: UseTree ( _) | PathParent :: PathPat ( _) => None ,
90
142
}
91
143
}
92
144
}
93
145
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 {
163
147
let text = match field. name_ref ( ) {
164
148
Some ( it) => it. to_string ( ) ,
165
149
None => name_from_field_shorthand ( field) . unwrap_or ( "unknown" . to_owned ( ) ) ,
166
150
} ;
167
- make:: name ( & text)
151
+ make. name ( & text)
168
152
}
169
153
170
154
fn name_from_field_shorthand ( field : & ast:: RecordExprField ) -> Option < String > {
@@ -175,27 +159,15 @@ fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option<String> {
175
159
Some ( path. as_single_name_ref ( ) ?. to_string ( ) )
176
160
}
177
161
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
-
191
162
fn expr_ty (
192
163
ctx : & AssistContext < ' _ > ,
164
+ make : & SyntaxFactory ,
193
165
arg : ast:: Expr ,
194
166
scope : & hir:: SemanticsScope < ' _ > ,
195
167
) -> Option < ast:: Type > {
196
168
let ty = ctx. sema . type_of_expr ( & arg) . map ( |it| it. adjusted ( ) ) ?;
197
169
let text = ty. display_source_code ( ctx. db ( ) , scope. module ( ) . into ( ) , false ) . ok ( ) ?;
198
- Some ( make:: ty ( & text) )
170
+ Some ( make. ty ( & text) )
199
171
}
200
172
201
173
#[ cfg( test) ]
0 commit comments