@@ -3,12 +3,12 @@ use std::collections::VecDeque;
33use ide_db:: {
44 assists:: GroupLabel ,
55 famous_defs:: FamousDefs ,
6- source_change:: SourceChangeBuilder ,
76 syntax_helpers:: node_ext:: { for_each_tail_expr, walk_expr} ,
87} ;
98use 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 ,
1212} ;
1313
1414use crate :: { utils:: invert_boolean_expression, AssistContext , AssistId , AssistKind , Assists } ;
@@ -58,9 +58,12 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
5858 _ => return None ,
5959 } ;
6060
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) ) ;
6266
63- ted:: replace ( demorganed. op_token ( ) ?, ast:: make:: token ( inv_token) ) ;
6467 let mut exprs = VecDeque :: from ( [
6568 ( bin_expr. lhs ( ) ?, demorganed. lhs ( ) ?) ,
6669 ( bin_expr. rhs ( ) ?, demorganed. rhs ( ) ?) ,
@@ -70,35 +73,39 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
7073 if let BinExpr ( bin_expr) = & expr {
7174 if let BinExpr ( cbin_expr) = & dm {
7275 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) ) ;
7477 exprs. push_back ( ( bin_expr. lhs ( ) ?, cbin_expr. lhs ( ) ?) ) ;
7578 exprs. push_back ( ( bin_expr. rhs ( ) ?, cbin_expr. rhs ( ) ?) ) ;
7679 } 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 ( ) ;
8083 }
81- ted :: replace ( dm. syntax ( ) , inv. syntax ( ) ) ;
84+ editor . replace ( dm. syntax ( ) , inv. syntax ( ) ) ;
8285 }
8386 } else {
8487 return None ;
8588 }
8689 } 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 ( ) ;
9093 }
91- ted :: replace ( dm. syntax ( ) , inv. syntax ( ) ) ;
94+ editor . replace ( dm. syntax ( ) , inv. syntax ( ) ) ;
9295 }
9396 }
9497
98+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
99+ let edit = editor. finish ( ) ;
100+ let demorganed = ast:: Expr :: cast ( edit. new_root ( ) . clone ( ) ) ?;
101+
95102 acc. add_group (
96103 & GroupLabel ( "Apply De Morgan's law" . to_owned ( ) ) ,
97104 AssistId ( "apply_demorgan" , AssistKind :: RefactorRewrite ) ,
98105 "Apply De Morgan's law" ,
99106 op_range,
100- |edit | {
101- let demorganed = ast :: Expr :: BinExpr ( demorganed ) ;
107+ |builder | {
108+ let make = SyntaxFactory :: new ( ) ;
102109 let paren_expr = bin_expr. syntax ( ) . parent ( ) . and_then ( ast:: ParenExpr :: cast) ;
103110 let neg_expr = paren_expr
104111 . clone ( )
@@ -107,24 +114,32 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
107114 . filter ( |prefix_expr| matches ! ( prefix_expr. op_kind( ) , Some ( ast:: UnaryOp :: Not ) ) )
108115 . map ( ast:: Expr :: PrefixExpr ) ;
109116
117+ let mut editor;
110118 if let Some ( paren_expr) = paren_expr {
111119 if let Some ( neg_expr) = neg_expr {
112120 cov_mark:: hit!( demorgan_double_negation) ;
113121 let parent = neg_expr. syntax ( ) . parent ( ) ;
122+ editor = builder. make_editor ( neg_expr. syntax ( ) ) ;
114123
115124 if parent. is_some_and ( |parent| demorganed. needs_parens_in ( parent) ) {
116125 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 ( ) ) ;
118127 } else {
119- edit . replace_ast ( neg_expr, demorganed) ;
128+ editor . replace ( neg_expr. syntax ( ) , demorganed. syntax ( ) ) ;
120129 } ;
121130 } else {
122131 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 ( ) ) ;
124135 }
125136 } 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 ( ) ) ;
127139 }
140+
141+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
142+ builder. add_file_edits ( ctx. file_id ( ) , editor) ;
128143 } ,
129144 )
130145}
@@ -161,7 +176,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
161176 let ( name, arg_expr) = validate_method_call_expr ( ctx, & method_call) ?;
162177
163178 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 ( ) ;
165180
166181 let op_range = method_call. syntax ( ) . text_range ( ) ;
167182 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<'_>
170185 AssistId ( "apply_demorgan_iterator" , AssistKind :: RefactorRewrite ) ,
171186 label,
172187 op_range,
173- |edit| {
188+ |builder| {
189+ let make = SyntaxFactory :: new ( ) ;
190+ let mut editor = builder. make_editor ( method_call. syntax ( ) ) ;
174191 // replace the method name
175192 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" ) ,
178195 _ => unreachable ! ( ) ,
179- }
180- . clone_for_update ( ) ;
181- edit. replace_ast ( name, new_name) ;
196+ } ;
197+ editor. replace ( name. syntax ( ) , new_name. syntax ( ) ) ;
182198
183199 // 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) ;
185201 walk_expr ( & closure_body, & mut |expr| {
186202 if let ast:: Expr :: ReturnExpr ( ret_expr) = expr {
187203 if let Some ( ret_expr_arg) = & ret_expr. expr ( ) {
@@ -198,15 +214,15 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
198214 . and_then ( ast:: PrefixExpr :: cast)
199215 . filter ( |prefix_expr| matches ! ( prefix_expr. op_kind( ) , Some ( ast:: UnaryOp :: Not ) ) )
200216 {
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" ) ,
206219 ) ;
207220 } else {
208- edit . insert ( method_call. syntax ( ) . text_range ( ) . start ( ) , "!" ) ;
221+ editor . insert ( Position :: before ( method_call. syntax ( ) ) , make . token ( SyntaxKind :: BANG ) ) ;
209222 }
223+
224+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
225+ builder. add_file_edits ( ctx. file_id ( ) , editor) ;
210226 } ,
211227 )
212228}
@@ -233,26 +249,50 @@ fn validate_method_call_expr(
233249 it_type. impls_trait ( sema. db , iter_trait, & [ ] ) . then_some ( ( name_ref, arg_expr) )
234250}
235251
236- fn tail_cb_impl ( edit : & mut SourceChangeBuilder , e : & ast:: Expr ) {
252+ fn tail_cb_impl ( editor : & mut SyntaxEditor , make : & SyntaxFactory , e : & ast:: Expr ) {
237253 match e {
238254 ast:: Expr :: BreakExpr ( break_expr) => {
239255 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) )
241257 }
242258 }
243259 ast:: Expr :: ReturnExpr ( _) => {
244260 // all return expressions have already been handled by the walk loop
245261 }
246262 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 ( ) ) ;
249265 }
250266 }
251267}
252268
253269/// 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 ( ) )
256296}
257297
258298#[ cfg( test) ]
0 commit comments