Auto merge of #13248 - harudagondi:unwrap-tuple, r=Veykril

Feature: Add assist to unwrap tuple declarations

> Implement #12923 for only tuples.
>
> Does not implement unwrapping for structs, as mentioned in the issue.

Add assist to unwrap tuples declarations to separate declarations.

```rust
fn main() {
	$0let (foo, bar, baz) = (1.0, "example", String::new())
}
```

becomes:

```rust
fn main() {
	let foo = 1.0;
	let bar = "example";
	let baz = String::new();
}
```

## Changelog

### Feature

- Added assist to unwrap tuple declarations.
This commit is contained in:
bors 2022-09-26 09:35:59 +00:00
commit 1a24003eb6
3 changed files with 180 additions and 0 deletions

View File

@ -0,0 +1,159 @@
use syntax::{
ast::{self, edit::AstNodeEdit},
AstNode, T,
};
use crate::{AssistContext, AssistId, AssistKind, Assists};
// Assist: unwrap_tuple
//
// Unwrap the tuple to different variables.
//
// ```
// # //- minicore: result
// fn main() {
// $0let (foo, bar) = ("Foo", "Bar");
// }
// ```
// ->
// ```
// fn main() {
// let foo = "Foo";
// let bar = "Bar";
// }
// ```
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()?;
// This only applies for tuple patterns, types, and initializers.
let tuple_pat = match pat {
ast::Pat::TuplePat(pat) => pat,
_ => return None,
};
let tuple_ty = ty.and_then(|it| match it {
ast::Type::TupleType(ty) => Some(ty),
_ => None,
});
let tuple_init = match init {
ast::Expr::TupleExpr(expr) => expr,
_ => return None,
};
if tuple_pat.fields().count() != tuple_init.fields().count() {
return None;
}
if let Some(tys) = &tuple_ty {
if tuple_pat.fields().count() != tys.fields().count() {
return None;
}
}
let parent = let_kw.parent()?;
acc.add(
AssistId("unwrap_tuple", AssistKind::RefactorRewrite),
"Unwrap tuple",
let_kw.text_range(),
|edit| {
let indents = " ".repeat(indent_level);
// 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!("{}let {pat}: {ty} = {expr};\n", indents))
}
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!("{}let {pat} = {expr};\n", indents));
}
edit.replace(parent.text_range(), zipped_decls.trim());
}
},
)
}
#[cfg(test)]
mod tests {
use crate::tests::check_assist;
use super::*;
#[test]
fn unwrap_tuples() {
check_assist(
unwrap_tuple,
r#"
fn main() {
$0let (foo, bar) = ("Foo", "Bar");
}
"#,
r#"
fn main() {
let foo = "Foo";
let bar = "Bar";
}
"#,
);
check_assist(
unwrap_tuple,
r#"
fn main() {
$0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
}
"#,
r#"
fn main() {
let foo = "Foo";
let bar = "Bar";
let baz = "Baz";
}
"#,
);
}
#[test]
fn unwrap_tuple_with_types() {
check_assist(
unwrap_tuple,
r#"
fn main() {
$0let (foo, bar): (u8, i32) = (5, 10);
}
"#,
r#"
fn main() {
let foo: u8 = 5;
let bar: i32 = 10;
}
"#,
);
check_assist(
unwrap_tuple,
r#"
fn main() {
$0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
}
"#,
r#"
fn main() {
let foo: u8 = 5;
let bar: i32 = 10;
let baz: f64 = 17.5;
}
"#,
);
}
}

View File

@ -189,6 +189,7 @@ mod handlers {
mod replace_turbofish_with_explicit_type;
mod split_import;
mod unmerge_match_arm;
mod unwrap_tuple;
mod sort_items;
mod toggle_ignore;
mod unmerge_use;
@ -291,6 +292,7 @@ pub(crate) fn all() -> &'static [Handler] {
unnecessary_async::unnecessary_async,
unwrap_block::unwrap_block,
unwrap_result_return_type::unwrap_result_return_type,
unwrap_tuple::unwrap_tuple,
wrap_return_type_in_result::wrap_return_type_in_result,
// These are manually sorted for better priorities. By default,
// priority is determined by the size of the target range (smaller

View File

@ -2386,6 +2386,25 @@ fn foo() -> i32 { 42i32 }
)
}
#[test]
fn doctest_unwrap_tuple() {
check_doc_test(
"unwrap_tuple",
r#####"
//- minicore: result
fn main() {
$0let (foo, bar) = ("Foo", "Bar");
}
"#####,
r#####"
fn main() {
let foo = "Foo";
let bar = "Bar";
}
"#####,
)
}
#[test]
fn doctest_wrap_return_type_in_result() {
check_doc_test(