mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 05:15:04 +00:00
Add functionality to unwrap tuple declarations
This commit is contained in:
parent
b6e3f41c2a
commit
ed0cf1c5fa
3 changed files with 181 additions and 0 deletions
160
crates/ide-assists/src/handlers/unwrap_tuple.rs
Normal file
160
crates/ide-assists/src/handlers/unwrap_tuple.rs
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
stdx::always!(
|
||||||
|
tuple_pat.fields().count() == tuple_init.fields().count(),
|
||||||
|
"Length of tuples in pattern and initializer do not match"
|
||||||
|
);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
stdx::always!(
|
||||||
|
tuple_pat.fields().count() == tys.fields().count(),
|
||||||
|
"Length of tuples in patterns and type do not match"
|
||||||
|
);
|
||||||
|
|
||||||
|
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 replace_turbofish_with_explicit_type;
|
||||||
mod split_import;
|
mod split_import;
|
||||||
mod unmerge_match_arm;
|
mod unmerge_match_arm;
|
||||||
|
mod unwrap_tuple;
|
||||||
mod sort_items;
|
mod sort_items;
|
||||||
mod toggle_ignore;
|
mod toggle_ignore;
|
||||||
mod unmerge_use;
|
mod unmerge_use;
|
||||||
|
@ -291,6 +292,7 @@ mod handlers {
|
||||||
unnecessary_async::unnecessary_async,
|
unnecessary_async::unnecessary_async,
|
||||||
unwrap_block::unwrap_block,
|
unwrap_block::unwrap_block,
|
||||||
unwrap_result_return_type::unwrap_result_return_type,
|
unwrap_result_return_type::unwrap_result_return_type,
|
||||||
|
unwrap_tuple::unwrap_tuple,
|
||||||
wrap_return_type_in_result::wrap_return_type_in_result,
|
wrap_return_type_in_result::wrap_return_type_in_result,
|
||||||
// These are manually sorted for better priorities. By default,
|
// These are manually sorted for better priorities. By default,
|
||||||
// priority is determined by the size of the target range (smaller
|
// 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]
|
#[test]
|
||||||
fn doctest_wrap_return_type_in_result() {
|
fn doctest_wrap_return_type_in_result() {
|
||||||
check_doc_test(
|
check_doc_test(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue