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:
commit
1a24003eb6
159
crates/ide-assists/src/handlers/unwrap_tuple.rs
Normal file
159
crates/ide-assists/src/handlers/unwrap_tuple.rs
Normal 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;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user