Skip to content

Commit ed0cf1c

Browse files
committed
Add functionality to unwrap tuple declarations
1 parent b6e3f41 commit ed0cf1c

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use syntax::{
2+
ast::{self, edit::AstNodeEdit},
3+
AstNode, T,
4+
};
5+
6+
use crate::{AssistContext, AssistId, AssistKind, Assists};
7+
8+
// Assist: unwrap_tuple
9+
//
10+
// Unwrap the tuple to different variables.
11+
//
12+
// ```
13+
// # //- minicore: result
14+
// fn main() {
15+
// $0let (foo, bar) = ("Foo", "Bar");
16+
// }
17+
// ```
18+
// ->
19+
// ```
20+
// fn main() {
21+
// let foo = "Foo";
22+
// let bar = "Bar";
23+
// }
24+
// ```
25+
pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
26+
let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
27+
let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?;
28+
let indent_level = let_stmt.indent_level().0 as usize;
29+
let pat = let_stmt.pat()?;
30+
let ty = let_stmt.ty();
31+
let init = let_stmt.initializer()?;
32+
33+
// This only applies for tuple patterns, types, and initializers.
34+
let tuple_pat = match pat {
35+
ast::Pat::TuplePat(pat) => pat,
36+
_ => return None,
37+
};
38+
let tuple_ty = ty.and_then(|it| match it {
39+
ast::Type::TupleType(ty) => Some(ty),
40+
_ => None,
41+
});
42+
let tuple_init = match init {
43+
ast::Expr::TupleExpr(expr) => expr,
44+
_ => return None,
45+
};
46+
47+
stdx::always!(
48+
tuple_pat.fields().count() == tuple_init.fields().count(),
49+
"Length of tuples in pattern and initializer do not match"
50+
);
51+
52+
let parent = let_kw.parent()?;
53+
54+
acc.add(
55+
AssistId("unwrap_tuple", AssistKind::RefactorRewrite),
56+
"Unwrap tuple",
57+
let_kw.text_range(),
58+
|edit| {
59+
let indents = " ".repeat(indent_level);
60+
61+
// If there is an ascribed type, insert that type for each declaration,
62+
// otherwise, omit that type.
63+
if let Some(tys) = tuple_ty {
64+
stdx::always!(
65+
tuple_pat.fields().count() == tys.fields().count(),
66+
"Length of tuples in patterns and type do not match"
67+
);
68+
69+
let mut zipped_decls = String::new();
70+
for (pat, ty, expr) in
71+
itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
72+
{
73+
zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents))
74+
}
75+
edit.replace(parent.text_range(), zipped_decls.trim());
76+
} else {
77+
let mut zipped_decls = String::new();
78+
for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
79+
zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents));
80+
}
81+
edit.replace(parent.text_range(), zipped_decls.trim());
82+
}
83+
},
84+
)
85+
}
86+
87+
#[cfg(test)]
88+
mod tests {
89+
use crate::tests::check_assist;
90+
91+
use super::*;
92+
93+
#[test]
94+
fn unwrap_tuples() {
95+
check_assist(
96+
unwrap_tuple,
97+
r#"
98+
fn main() {
99+
$0let (foo, bar) = ("Foo", "Bar");
100+
}
101+
"#,
102+
r#"
103+
fn main() {
104+
let foo = "Foo";
105+
let bar = "Bar";
106+
}
107+
"#,
108+
);
109+
110+
check_assist(
111+
unwrap_tuple,
112+
r#"
113+
fn main() {
114+
$0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
115+
}
116+
"#,
117+
r#"
118+
fn main() {
119+
let foo = "Foo";
120+
let bar = "Bar";
121+
let baz = "Baz";
122+
}
123+
"#,
124+
);
125+
}
126+
127+
#[test]
128+
fn unwrap_tuple_with_types() {
129+
check_assist(
130+
unwrap_tuple,
131+
r#"
132+
fn main() {
133+
$0let (foo, bar): (u8, i32) = (5, 10);
134+
}
135+
"#,
136+
r#"
137+
fn main() {
138+
let foo: u8 = 5;
139+
let bar: i32 = 10;
140+
}
141+
"#,
142+
);
143+
144+
check_assist(
145+
unwrap_tuple,
146+
r#"
147+
fn main() {
148+
$0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
149+
}
150+
"#,
151+
r#"
152+
fn main() {
153+
let foo: u8 = 5;
154+
let bar: i32 = 10;
155+
let baz: f64 = 17.5;
156+
}
157+
"#,
158+
);
159+
}
160+
}

crates/ide-assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ mod handlers {
189189
mod replace_turbofish_with_explicit_type;
190190
mod split_import;
191191
mod unmerge_match_arm;
192+
mod unwrap_tuple;
192193
mod sort_items;
193194
mod toggle_ignore;
194195
mod unmerge_use;
@@ -291,6 +292,7 @@ mod handlers {
291292
unnecessary_async::unnecessary_async,
292293
unwrap_block::unwrap_block,
293294
unwrap_result_return_type::unwrap_result_return_type,
295+
unwrap_tuple::unwrap_tuple,
294296
wrap_return_type_in_result::wrap_return_type_in_result,
295297
// These are manually sorted for better priorities. By default,
296298
// priority is determined by the size of the target range (smaller

crates/ide-assists/src/tests/generated.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2386,6 +2386,25 @@ fn foo() -> i32 { 42i32 }
23862386
)
23872387
}
23882388

2389+
#[test]
2390+
fn doctest_unwrap_tuple() {
2391+
check_doc_test(
2392+
"unwrap_tuple",
2393+
r#####"
2394+
//- minicore: result
2395+
fn main() {
2396+
$0let (foo, bar) = ("Foo", "Bar");
2397+
}
2398+
"#####,
2399+
r#####"
2400+
fn main() {
2401+
let foo = "Foo";
2402+
let bar = "Bar";
2403+
}
2404+
"#####,
2405+
)
2406+
}
2407+
23892408
#[test]
23902409
fn doctest_wrap_return_type_in_result() {
23912410
check_doc_test(

0 commit comments

Comments
 (0)