@@ -3,12 +3,12 @@ use std::collections::VecDeque;
3
3
use ide_db:: {
4
4
assists:: GroupLabel ,
5
5
famous_defs:: FamousDefs ,
6
- source_change:: SourceChangeBuilder ,
7
6
syntax_helpers:: node_ext:: { for_each_tail_expr, walk_expr} ,
8
7
} ;
9
8
use syntax:: {
10
- ast:: { self , make, AstNode , Expr :: BinExpr , HasArgList } ,
11
- ted, SyntaxKind , T ,
9
+ ast:: { self , syntax_factory:: SyntaxFactory , AstNode , Expr :: BinExpr , HasArgList } ,
10
+ syntax_editor:: { Position , SyntaxEditor } ,
11
+ SyntaxKind , SyntaxNode , T ,
12
12
} ;
13
13
14
14
use crate :: { utils:: invert_boolean_expression, AssistContext , AssistId , AssistKind , Assists } ;
@@ -58,9 +58,12 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
58
58
_ => return None ,
59
59
} ;
60
60
61
- let demorganed = bin_expr. clone_subtree ( ) . clone_for_update ( ) ;
61
+ let make = SyntaxFactory :: new ( ) ;
62
+
63
+ let demorganed = bin_expr. clone_subtree ( ) ;
64
+ let mut editor = SyntaxEditor :: new ( demorganed. syntax ( ) . clone ( ) ) ;
65
+ editor. replace ( demorganed. op_token ( ) ?, make. token ( inv_token) ) ;
62
66
63
- ted:: replace ( demorganed. op_token ( ) ?, ast:: make:: token ( inv_token) ) ;
64
67
let mut exprs = VecDeque :: from ( [
65
68
( bin_expr. lhs ( ) ?, demorganed. lhs ( ) ?) ,
66
69
( bin_expr. rhs ( ) ?, demorganed. rhs ( ) ?) ,
@@ -70,35 +73,39 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
70
73
if let BinExpr ( bin_expr) = & expr {
71
74
if let BinExpr ( cbin_expr) = & dm {
72
75
if op == bin_expr. op_kind ( ) ? {
73
- ted :: replace ( cbin_expr. op_token ( ) ?, ast :: make:: token ( inv_token) ) ;
76
+ editor . replace ( cbin_expr. op_token ( ) ?, make. token ( inv_token) ) ;
74
77
exprs. push_back ( ( bin_expr. lhs ( ) ?, cbin_expr. lhs ( ) ?) ) ;
75
78
exprs. push_back ( ( bin_expr. rhs ( ) ?, cbin_expr. rhs ( ) ?) ) ;
76
79
} else {
77
- let mut inv = invert_boolean_expression ( expr) ;
78
- if inv . needs_parens_in ( dm. syntax ( ) . parent ( ) ?) {
79
- inv = ast :: make:: expr_paren ( inv) . clone_for_update ( ) ;
80
+ let mut inv = invert_boolean_expression ( & make , expr) ;
81
+ if needs_parens_in_place_of ( & inv , & dm. syntax ( ) . parent ( ) ?, & dm ) {
82
+ inv = make. expr_paren ( inv) . into ( ) ;
80
83
}
81
- ted :: replace ( dm. syntax ( ) , inv. syntax ( ) ) ;
84
+ editor . replace ( dm. syntax ( ) , inv. syntax ( ) ) ;
82
85
}
83
86
} else {
84
87
return None ;
85
88
}
86
89
} else {
87
- let mut inv = invert_boolean_expression ( dm. clone_subtree ( ) ) . clone_for_update ( ) ;
88
- if inv . needs_parens_in ( dm. syntax ( ) . parent ( ) ?) {
89
- inv = ast :: make:: expr_paren ( inv) . clone_for_update ( ) ;
90
+ let mut inv = invert_boolean_expression ( & make , dm. clone ( ) ) ;
91
+ if needs_parens_in_place_of ( & inv , & dm. syntax ( ) . parent ( ) ?, & dm ) {
92
+ inv = make. expr_paren ( inv) . into ( ) ;
90
93
}
91
- ted :: replace ( dm. syntax ( ) , inv. syntax ( ) ) ;
94
+ editor . replace ( dm. syntax ( ) , inv. syntax ( ) ) ;
92
95
}
93
96
}
94
97
98
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
99
+ let edit = editor. finish ( ) ;
100
+ let demorganed = ast:: Expr :: cast ( edit. new_root ( ) . clone ( ) ) ?;
101
+
95
102
acc. add_group (
96
103
& GroupLabel ( "Apply De Morgan's law" . to_owned ( ) ) ,
97
104
AssistId ( "apply_demorgan" , AssistKind :: RefactorRewrite ) ,
98
105
"Apply De Morgan's law" ,
99
106
op_range,
100
- |edit | {
101
- let demorganed = ast :: Expr :: BinExpr ( demorganed ) ;
107
+ |builder | {
108
+ let make = SyntaxFactory :: new ( ) ;
102
109
let paren_expr = bin_expr. syntax ( ) . parent ( ) . and_then ( ast:: ParenExpr :: cast) ;
103
110
let neg_expr = paren_expr
104
111
. clone ( )
@@ -107,24 +114,32 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
107
114
. filter ( |prefix_expr| matches ! ( prefix_expr. op_kind( ) , Some ( ast:: UnaryOp :: Not ) ) )
108
115
. map ( ast:: Expr :: PrefixExpr ) ;
109
116
117
+ let mut editor;
110
118
if let Some ( paren_expr) = paren_expr {
111
119
if let Some ( neg_expr) = neg_expr {
112
120
cov_mark:: hit!( demorgan_double_negation) ;
113
121
let parent = neg_expr. syntax ( ) . parent ( ) ;
122
+ editor = builder. make_editor ( neg_expr. syntax ( ) ) ;
114
123
115
124
if parent. is_some_and ( |parent| demorganed. needs_parens_in ( parent) ) {
116
125
cov_mark:: hit!( demorgan_keep_parens_for_op_precedence2) ;
117
- edit . replace_ast ( neg_expr, make:: expr_paren ( demorganed) ) ;
126
+ editor . replace ( neg_expr. syntax ( ) , make. expr_paren ( demorganed) . syntax ( ) ) ;
118
127
} else {
119
- edit . replace_ast ( neg_expr, demorganed) ;
128
+ editor . replace ( neg_expr. syntax ( ) , demorganed. syntax ( ) ) ;
120
129
} ;
121
130
} else {
122
131
cov_mark:: hit!( demorgan_double_parens) ;
123
- edit. replace_ast ( paren_expr. into ( ) , add_bang_paren ( demorganed) ) ;
132
+ editor = builder. make_editor ( paren_expr. syntax ( ) ) ;
133
+
134
+ editor. replace ( paren_expr. syntax ( ) , add_bang_paren ( & make, demorganed) . syntax ( ) ) ;
124
135
}
125
136
} else {
126
- edit. replace_ast ( bin_expr. into ( ) , add_bang_paren ( demorganed) ) ;
137
+ editor = builder. make_editor ( bin_expr. syntax ( ) ) ;
138
+ editor. replace ( bin_expr. syntax ( ) , add_bang_paren ( & make, demorganed) . syntax ( ) ) ;
127
139
}
140
+
141
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
142
+ builder. add_file_edits ( ctx. file_id ( ) , editor) ;
128
143
} ,
129
144
)
130
145
}
@@ -161,7 +176,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
161
176
let ( name, arg_expr) = validate_method_call_expr ( ctx, & method_call) ?;
162
177
163
178
let ast:: Expr :: ClosureExpr ( closure_expr) = arg_expr else { return None } ;
164
- let closure_body = closure_expr. body ( ) ?;
179
+ let closure_body = closure_expr. body ( ) ?. clone_for_update ( ) ;
165
180
166
181
let op_range = method_call. syntax ( ) . text_range ( ) ;
167
182
let label = format ! ( "Apply De Morgan's law to `Iterator::{}`" , name. text( ) . as_str( ) ) ;
@@ -170,18 +185,19 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
170
185
AssistId ( "apply_demorgan_iterator" , AssistKind :: RefactorRewrite ) ,
171
186
label,
172
187
op_range,
173
- |edit| {
188
+ |builder| {
189
+ let make = SyntaxFactory :: new ( ) ;
190
+ let mut editor = builder. make_editor ( method_call. syntax ( ) ) ;
174
191
// replace the method name
175
192
let new_name = match name. text ( ) . as_str ( ) {
176
- "all" => make:: name_ref ( "any" ) ,
177
- "any" => make:: name_ref ( "all" ) ,
193
+ "all" => make. name_ref ( "any" ) ,
194
+ "any" => make. name_ref ( "all" ) ,
178
195
_ => unreachable ! ( ) ,
179
- }
180
- . clone_for_update ( ) ;
181
- edit. replace_ast ( name, new_name) ;
196
+ } ;
197
+ editor. replace ( name. syntax ( ) , new_name. syntax ( ) ) ;
182
198
183
199
// negate all tail expressions in the closure body
184
- let tail_cb = & mut |e : & _ | tail_cb_impl ( edit , e) ;
200
+ let tail_cb = & mut |e : & _ | tail_cb_impl ( & mut editor , & make , e) ;
185
201
walk_expr ( & closure_body, & mut |expr| {
186
202
if let ast:: Expr :: ReturnExpr ( ret_expr) = expr {
187
203
if let Some ( ret_expr_arg) = & ret_expr. expr ( ) {
@@ -198,15 +214,15 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
198
214
. and_then ( ast:: PrefixExpr :: cast)
199
215
. filter ( |prefix_expr| matches ! ( prefix_expr. op_kind( ) , Some ( ast:: UnaryOp :: Not ) ) )
200
216
{
201
- edit. delete (
202
- prefix_expr
203
- . op_token ( )
204
- . expect ( "prefix expression always has an operator" )
205
- . text_range ( ) ,
217
+ editor. delete (
218
+ prefix_expr. op_token ( ) . expect ( "prefix expression always has an operator" ) ,
206
219
) ;
207
220
} else {
208
- edit . insert ( method_call. syntax ( ) . text_range ( ) . start ( ) , "!" ) ;
221
+ editor . insert ( Position :: before ( method_call. syntax ( ) ) , make . token ( SyntaxKind :: BANG ) ) ;
209
222
}
223
+
224
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
225
+ builder. add_file_edits ( ctx. file_id ( ) , editor) ;
210
226
} ,
211
227
)
212
228
}
@@ -233,26 +249,50 @@ fn validate_method_call_expr(
233
249
it_type. impls_trait ( sema. db , iter_trait, & [ ] ) . then_some ( ( name_ref, arg_expr) )
234
250
}
235
251
236
- fn tail_cb_impl ( edit : & mut SourceChangeBuilder , e : & ast:: Expr ) {
252
+ fn tail_cb_impl ( editor : & mut SyntaxEditor , make : & SyntaxFactory , e : & ast:: Expr ) {
237
253
match e {
238
254
ast:: Expr :: BreakExpr ( break_expr) => {
239
255
if let Some ( break_expr_arg) = break_expr. expr ( ) {
240
- for_each_tail_expr ( & break_expr_arg, & mut |e| tail_cb_impl ( edit , e) )
256
+ for_each_tail_expr ( & break_expr_arg, & mut |e| tail_cb_impl ( editor , make , e) )
241
257
}
242
258
}
243
259
ast:: Expr :: ReturnExpr ( _) => {
244
260
// all return expressions have already been handled by the walk loop
245
261
}
246
262
e => {
247
- let inverted_body = invert_boolean_expression ( e. clone ( ) ) ;
248
- edit . replace ( e. syntax ( ) . text_range ( ) , inverted_body. syntax ( ) . text ( ) ) ;
263
+ let inverted_body = invert_boolean_expression ( make , e. clone ( ) ) ;
264
+ editor . replace ( e. syntax ( ) , inverted_body. syntax ( ) ) ;
249
265
}
250
266
}
251
267
}
252
268
253
269
/// Add bang and parentheses to the expression.
254
- fn add_bang_paren ( expr : ast:: Expr ) -> ast:: Expr {
255
- make:: expr_prefix ( T ! [ !] , make:: expr_paren ( expr) ) . into ( )
270
+ fn add_bang_paren ( make : & SyntaxFactory , expr : ast:: Expr ) -> ast:: Expr {
271
+ make. expr_prefix ( T ! [ !] , make. expr_paren ( expr) . into ( ) ) . into ( )
272
+ }
273
+
274
+ fn needs_parens_in_place_of (
275
+ this : & ast:: Expr ,
276
+ parent : & SyntaxNode ,
277
+ in_place_of : & ast:: Expr ,
278
+ ) -> bool {
279
+ assert_eq ! ( Some ( parent) , in_place_of. syntax( ) . parent( ) . as_ref( ) ) ;
280
+
281
+ let child_idx = parent
282
+ . children ( )
283
+ . enumerate ( )
284
+ . find_map ( |( i, it) | if & it == in_place_of. syntax ( ) { Some ( i) } else { None } )
285
+ . unwrap ( ) ;
286
+ let parent = parent. clone_subtree ( ) ;
287
+ let subtree_place = parent. children ( ) . nth ( child_idx) . unwrap ( ) ;
288
+
289
+ let mut editor = SyntaxEditor :: new ( parent) ;
290
+ editor. replace ( subtree_place, this. syntax ( ) ) ;
291
+ let edit = editor. finish ( ) ;
292
+
293
+ let replaced = edit. new_root ( ) . children ( ) . nth ( child_idx) . unwrap ( ) ;
294
+ let replaced = ast:: Expr :: cast ( replaced) . unwrap ( ) ;
295
+ replaced. needs_parens_in ( edit. new_root ( ) . clone ( ) )
256
296
}
257
297
258
298
#[ cfg( test) ]
0 commit comments