From 91e482b46d43a24cd0a48ea1119b93105140cff2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 27 Mar 2020 12:12:17 +0100 Subject: Replace if with if-let --- .../src/handlers/replace_let_with_if_let.rs | 108 +++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 crates/ra_assists/src/handlers/replace_let_with_if_let.rs (limited to 'crates/ra_assists/src/handlers/replace_let_with_if_let.rs') diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs new file mode 100644 index 000000000..10e41f97e --- /dev/null +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs @@ -0,0 +1,108 @@ +use hir::Adt; +use ra_syntax::{ + ast::{self, make}, + AstNode, T, +}; + +use crate::{ + assist_ctx::{Assist, AssistCtx}, + AssistId, +}; +use ast::edit::{AstNodeEdit, IndentLevel}; +use std::iter::once; + +// Assist: replace_let_with_if_let +// +// Replaces `if let` with an else branch with a `match` expression. +// +// ``` +// # enum Option { Some(T), None } +// +// fn main(action: Action) { +// <|>let x = compute(); +// } +// +// fn compute() -> Option { None } +// ``` +// -> +// ``` +// # enum Option { Some(T), None } +// +// fn main(action: Action) { +// if let Some(x) = compute() { +// } +// } +// +// fn compute() -> Option { None } +// ``` +pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option { + let let_kw = ctx.find_token_at_offset(T![let])?; + let let_stmt = let_kw.ancestors().find_map(ast::LetStmt::cast)?; + let init = let_stmt.initializer()?; + let original_pat = let_stmt.pat()?; + let ty = ctx.sema.type_of_expr(&init)?; + let enum_ = match ty.as_adt() { + Some(Adt::Enum(it)) => it, + _ => return None, + }; + let happy_case = + [("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| { + if &enum_.name(ctx.db).to_string() == known_type { + return Some(happy_case); + } + None + }); + + ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| { + let with_placeholder: ast::Pat = match happy_case { + None => make::placeholder_pat().into(), + Some(var_name) => make::tuple_struct_pat( + make::path_unqualified(make::path_segment(make::name_ref(var_name))), + once(make::placeholder_pat().into()), + ) + .into(), + }; + let block = + IndentLevel::from_node(let_stmt.syntax()).increase_indent(make::block_expr(None, None)); + let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); + let stmt = make::expr_stmt(if_); + + let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); + let target_offset = + let_stmt.syntax().text_range().start() + placeholder.syntax().text_range().start(); + let stmt = stmt.replace_descendant(placeholder.into(), original_pat); + + edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); + edit.target(let_kw.text_range()); + edit.set_cursor(target_offset); + }) +} + +#[cfg(test)] +mod tests { + use crate::helpers::check_assist; + + use super::*; + + #[test] + fn replace_let_unknown_enum() { + check_assist( + replace_let_with_if_let, + r" +enum E { X(T), Y(T) } + +fn main() { + <|>let x = E::X(92); +} + ", + r" +enum E { X(T), Y(T) } + +fn main() { + if let <|>x = E::X(92) { + } +} + ", + ) + } +} -- cgit v1.2.3