Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 44 additions & 20 deletions crates/ide-assists/src/handlers/unwrap_tuple.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::iter;

use either::Either;
use syntax::{
AstNode, T,
ast::{self, edit::AstNodeEdit},
Expand All @@ -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::<ast::LetStmt, ast::LetExpr>::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 {
Expand Down Expand Up @@ -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));
},
)
}
Expand Down Expand Up @@ -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(
Expand Down
Loading