diff --git a/crates/ide-assists/src/handlers/unwrap_tuple.rs b/crates/ide-assists/src/handlers/unwrap_tuple.rs index 46f3e85e1234..2345075a52be 100644 --- a/crates/ide-assists/src/handlers/unwrap_tuple.rs +++ b/crates/ide-assists/src/handlers/unwrap_tuple.rs @@ -1,3 +1,6 @@ +use std::iter; + +use either::Either; use syntax::{ AstNode, T, ast::{self, edit::AstNodeEdit}, @@ -24,11 +27,16 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let let_kw = ctx.find_token_syntax_at_offset(T![let])?; - let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?; - let indent_level = let_stmt.indent_level().0 as usize; - let pat = let_stmt.pat()?; - let ty = let_stmt.ty(); - let init = let_stmt.initializer()?; + let let_stmt = let_kw.parent().and_then(Either::::cast)?; + let mut indent_level = let_stmt.indent_level(); + let pat = either::for_both!(&let_stmt, it => it.pat())?; + let (ty, init, prefix, suffix) = match &let_stmt { + Either::Left(let_stmt) => (let_stmt.ty(), let_stmt.initializer()?, "", ";"), + Either::Right(let_expr) => { + indent_level = indent_level + 1; + (None, let_expr.expr()?, "&& ", "") + } + }; // This only applies for tuple patterns, types, and initializers. let tuple_pat = match pat { @@ -60,25 +68,19 @@ pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option "Unwrap tuple", let_kw.text_range(), |edit| { - let indents = " ".repeat(indent_level); + let mut decls = String::new(); // If there is an ascribed type, insert that type for each declaration, // otherwise, omit that type. - if let Some(tys) = tuple_ty { - let mut zipped_decls = String::new(); - for (pat, ty, expr) in - itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields()) - { - zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n")) - } - edit.replace(parent.text_range(), zipped_decls.trim()); - } else { - let mut zipped_decls = String::new(); - for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) { - zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n")); - } - edit.replace(parent.text_range(), zipped_decls.trim()); + let tys = + tuple_ty.into_iter().flat_map(|it| it.fields().map(Some)).chain(iter::repeat(None)); + for (pat, ty, expr) in itertools::izip!(tuple_pat.fields(), tys, tuple_init.fields()) { + let ty = ty.map_or_else(String::new, |ty| format!(": {ty}")); + decls.push_str(&format!("{prefix}let {pat}{ty} = {expr}{suffix}\n{indent_level}")) } + + let s = decls.trim(); + edit.replace(parent.text_range(), s.strip_prefix(prefix).unwrap_or(s)); }, ) } @@ -123,6 +125,28 @@ fn main() { ); } + #[test] + fn unwrap_tuples_in_let_expr() { + check_assist( + unwrap_tuple, + r#" +fn main() { + if $0let (foo, bar) = ("Foo", "Bar") { + code(); + } +} +"#, + r#" +fn main() { + if let foo = "Foo" + && let bar = "Bar" { + code(); + } +} +"#, + ); + } + #[test] fn unwrap_tuple_with_types() { check_assist(