mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 03:54:42 +00:00 
			
		
		
		
	internal: Migrate if let <=> match assists to SyntaxEditor
				
					
				
			This commit is contained in:
		
							parent
							
								
									eb2ce57a3e
								
							
						
					
					
						commit
						54d9b5a31a
					
				
					 1 changed files with 53 additions and 45 deletions
				
			
		|  | @ -1,4 +1,4 @@ | ||||||
| use std::iter::{self, successors}; | use std::iter::successors; | ||||||
| 
 | 
 | ||||||
| use either::Either; | use either::Either; | ||||||
| use ide_db::{ | use ide_db::{ | ||||||
|  | @ -8,11 +8,7 @@ use ide_db::{ | ||||||
|     RootDatabase, |     RootDatabase, | ||||||
| }; | }; | ||||||
| use syntax::{ | use syntax::{ | ||||||
|     ast::{ |     ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory, HasName}, | ||||||
|         self, |  | ||||||
|         edit::{AstNodeEdit, IndentLevel}, |  | ||||||
|         make, HasName, |  | ||||||
|     }, |  | ||||||
|     AstNode, TextRange, T, |     AstNode, TextRange, T, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -108,51 +104,58 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' | ||||||
|         AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite), |         AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite), | ||||||
|         format!("Replace if{let_} with match"), |         format!("Replace if{let_} with match"), | ||||||
|         available_range, |         available_range, | ||||||
|         move |edit| { |         move |builder| { | ||||||
|  |             let make = SyntaxFactory::new(); | ||||||
|             let match_expr = { |             let match_expr = { | ||||||
|                 let else_arm = make_else_arm(ctx, else_block, &cond_bodies); |                 let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies); | ||||||
|                 let make_match_arm = |(pat, body): (_, ast::BlockExpr)| { |                 let make_match_arm = |(pat, body): (_, ast::BlockExpr)| { | ||||||
|                     let body = body.reset_indent().indent(IndentLevel(1)); |                     let body = make.block_expr(body.statements(), body.tail_expr()); | ||||||
|  |                     body.indent(IndentLevel::from(1)); | ||||||
|  |                     let body = unwrap_trivial_block(body); | ||||||
|                     match pat { |                     match pat { | ||||||
|                         Either::Left(pat) => make::match_arm(pat, None, unwrap_trivial_block(body)), |                         Either::Left(pat) => make.match_arm(pat, None, body), | ||||||
|                         Either::Right(_) if !pat_seen => make::match_arm( |                         Either::Right(_) if !pat_seen => { | ||||||
|                             make::literal_pat("true").into(), |                             make.match_arm(make.literal_pat("true").into(), None, body) | ||||||
|                             None, |                         } | ||||||
|                             unwrap_trivial_block(body), |                         Either::Right(expr) => make.match_arm( | ||||||
|                         ), |                             make.wildcard_pat().into(), | ||||||
|                         Either::Right(expr) => make::match_arm( |                             Some(make.match_guard(expr)), | ||||||
|                             make::wildcard_pat().into(), |                             body, | ||||||
|                             Some(make::match_guard(expr)), |  | ||||||
|                             unwrap_trivial_block(body), |  | ||||||
|                         ), |                         ), | ||||||
|                     } |                     } | ||||||
|                 }; |                 }; | ||||||
|                 let arms = cond_bodies.into_iter().map(make_match_arm).chain(iter::once(else_arm)); |                 let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]); | ||||||
|                 let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms)); |                 let match_expr = make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms)); | ||||||
|                 match_expr.indent(IndentLevel::from_node(if_expr.syntax())).into() |                 match_expr.indent(IndentLevel::from_node(if_expr.syntax())); | ||||||
|  |                 match_expr.into() | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             let has_preceding_if_expr = |             let has_preceding_if_expr = | ||||||
|                 if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind())); |                 if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind())); | ||||||
|             let expr = if has_preceding_if_expr { |             let expr = if has_preceding_if_expr { | ||||||
|                 // make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
 |                 // make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
 | ||||||
|                 make::block_expr(None, Some(match_expr)).into() |                 make.block_expr([], Some(match_expr)).into() | ||||||
|             } else { |             } else { | ||||||
|                 match_expr |                 match_expr | ||||||
|             }; |             }; | ||||||
|             edit.replace_ast::<ast::Expr>(if_expr.into(), expr); | 
 | ||||||
|  |             let mut editor = builder.make_editor(if_expr.syntax()); | ||||||
|  |             editor.replace(if_expr.syntax(), expr.syntax()); | ||||||
|  |             editor.add_mappings(make.finish_with_mappings()); | ||||||
|  |             builder.add_file_edits(ctx.file_id(), editor); | ||||||
|         }, |         }, | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn make_else_arm( | fn make_else_arm( | ||||||
|     ctx: &AssistContext<'_>, |     ctx: &AssistContext<'_>, | ||||||
|  |     make: &SyntaxFactory, | ||||||
|     else_block: Option<ast::BlockExpr>, |     else_block: Option<ast::BlockExpr>, | ||||||
|     conditionals: &[(Either<ast::Pat, ast::Expr>, ast::BlockExpr)], |     conditionals: &[(Either<ast::Pat, ast::Expr>, ast::BlockExpr)], | ||||||
| ) -> ast::MatchArm { | ) -> ast::MatchArm { | ||||||
|     let (pattern, expr) = if let Some(else_block) = else_block { |     let (pattern, expr) = if let Some(else_block) = else_block { | ||||||
|         let pattern = match conditionals { |         let pattern = match conditionals { | ||||||
|             [(Either::Right(_), _)] => make::literal_pat("false").into(), |             [(Either::Right(_), _)] => make.literal_pat("false").into(), | ||||||
|             [(Either::Left(pat), _)] => match ctx |             [(Either::Left(pat), _)] => match ctx | ||||||
|                 .sema |                 .sema | ||||||
|                 .type_of_pat(pat) |                 .type_of_pat(pat) | ||||||
|  | @ -162,24 +165,24 @@ fn make_else_arm( | ||||||
|                     if does_pat_match_variant(pat, &it.sad_pattern()) { |                     if does_pat_match_variant(pat, &it.sad_pattern()) { | ||||||
|                         it.happy_pattern_wildcard() |                         it.happy_pattern_wildcard() | ||||||
|                     } else if does_pat_variant_nested_or_literal(ctx, pat) { |                     } else if does_pat_variant_nested_or_literal(ctx, pat) { | ||||||
|                         make::wildcard_pat().into() |                         make.wildcard_pat().into() | ||||||
|                     } else { |                     } else { | ||||||
|                         it.sad_pattern() |                         it.sad_pattern() | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 None => make::wildcard_pat().into(), |                 None => make.wildcard_pat().into(), | ||||||
|             }, |             }, | ||||||
|             _ => make::wildcard_pat().into(), |             _ => make.wildcard_pat().into(), | ||||||
|         }; |         }; | ||||||
|         (pattern, unwrap_trivial_block(else_block)) |         (pattern, unwrap_trivial_block(else_block)) | ||||||
|     } else { |     } else { | ||||||
|         let pattern = match conditionals { |         let pattern = match conditionals { | ||||||
|             [(Either::Right(_), _)] => make::literal_pat("false").into(), |             [(Either::Right(_), _)] => make.literal_pat("false").into(), | ||||||
|             _ => make::wildcard_pat().into(), |             _ => make.wildcard_pat().into(), | ||||||
|         }; |         }; | ||||||
|         (pattern, make::ext::expr_unit()) |         (pattern, make.expr_unit()) | ||||||
|     }; |     }; | ||||||
|     make::match_arm(pattern, None, expr) |     make.match_arm(pattern, None, expr) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Assist: replace_match_with_if_let
 | // Assist: replace_match_with_if_let
 | ||||||
|  | @ -245,21 +248,21 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' | ||||||
|         } |         } | ||||||
|         _ => " let", |         _ => " let", | ||||||
|     }; |     }; | ||||||
|     let target = match_expr.syntax().text_range(); |  | ||||||
|     acc.add( |     acc.add( | ||||||
|         AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite), |         AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite), | ||||||
|         format!("Replace match with if{let_}"), |         format!("Replace match with if{let_}"), | ||||||
|         target, |         match_expr.syntax().text_range(), | ||||||
|         move |edit| { |         move |builder| { | ||||||
|             fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr { |             let make = SyntaxFactory::new(); | ||||||
|  |             let make_block_expr = |expr: ast::Expr| { | ||||||
|                 // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
 |                 // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
 | ||||||
|                 // formatted without enclosing braces. If we encounter such block exprs,
 |                 // formatted without enclosing braces. If we encounter such block exprs,
 | ||||||
|                 // wrap them in another BlockExpr.
 |                 // wrap them in another BlockExpr.
 | ||||||
|                 match expr { |                 match expr { | ||||||
|                     ast::Expr::BlockExpr(block) if block.modifier().is_none() => block, |                     ast::Expr::BlockExpr(block) if block.modifier().is_none() => block, | ||||||
|                     expr => make::block_expr(iter::empty(), Some(expr)), |                     expr => make.block_expr([], Some(expr)), | ||||||
|                 } |  | ||||||
|                 } |                 } | ||||||
|  |             }; | ||||||
| 
 | 
 | ||||||
|             let condition = match if_let_pat { |             let condition = match if_let_pat { | ||||||
|                 ast::Pat::LiteralPat(p) |                 ast::Pat::LiteralPat(p) | ||||||
|  | @ -270,20 +273,25 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' | ||||||
|                 ast::Pat::LiteralPat(p) |                 ast::Pat::LiteralPat(p) | ||||||
|                     if p.literal().is_some_and(|it| it.token().kind() == T![false]) => |                     if p.literal().is_some_and(|it| it.token().kind() == T![false]) => | ||||||
|                 { |                 { | ||||||
|                     make::expr_prefix(T![!], scrutinee).into() |                     make.expr_prefix(T![!], scrutinee).into() | ||||||
|                 } |                 } | ||||||
|                 _ => make::expr_let(if_let_pat, scrutinee).into(), |                 _ => make.expr_let(if_let_pat, scrutinee).into(), | ||||||
|             }; |             }; | ||||||
|             let then_block = make_block_expr(then_expr.reset_indent()); |             let then_expr = then_expr.clone_for_update(); | ||||||
|  |             then_expr.reindent_to(IndentLevel::single()); | ||||||
|  |             let then_block = make_block_expr(then_expr); | ||||||
|             let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) }; |             let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) }; | ||||||
|             let if_let_expr = make::expr_if( |             let if_let_expr = make.expr_if( | ||||||
|                 condition, |                 condition, | ||||||
|                 then_block, |                 then_block, | ||||||
|                 else_expr.map(make_block_expr).map(ast::ElseBranch::Block), |                 else_expr.map(make_block_expr).map(ast::ElseBranch::Block), | ||||||
|             ) |             ); | ||||||
|             .indent(IndentLevel::from_node(match_expr.syntax())); |             if_let_expr.indent(IndentLevel::from_node(match_expr.syntax())); | ||||||
| 
 | 
 | ||||||
|             edit.replace_ast::<ast::Expr>(match_expr.into(), if_let_expr.into()); |             let mut editor = builder.make_editor(match_expr.syntax()); | ||||||
|  |             editor.replace(match_expr.syntax(), if_let_expr.syntax()); | ||||||
|  |             editor.add_mappings(make.finish_with_mappings()); | ||||||
|  |             builder.add_file_edits(ctx.file_id(), editor); | ||||||
|         }, |         }, | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Giga Bowser
						Giga Bowser