Skip to content

Commit 1a24003

Browse files
committed
Auto merge of rust-lang#13248 - harudagondi:unwrap-tuple, r=Veykril
Feature: Add assist to unwrap tuple declarations > Implement rust-lang#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.
2 parents 1f92965 + c2dc32c commit 1a24003

File tree

3 files changed

+180
-0
lines changed

3 files changed

+180
-0
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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+
if tuple_pat.fields().count() != tuple_init.fields().count() {
48+
return None;
49+
}
50+
if let Some(tys) = &tuple_ty {
51+
if tuple_pat.fields().count() != tys.fields().count() {
52+
return None;
53+
}
54+
}
55+
56+
let parent = let_kw.parent()?;
57+
58+
acc.add(
59+
AssistId("unwrap_tuple", AssistKind::RefactorRewrite),
60+
"Unwrap tuple",
61+
let_kw.text_range(),
62+
|edit| {
63+
let indents = " ".repeat(indent_level);
64+
65+
// If there is an ascribed type, insert that type for each declaration,
66+
// otherwise, omit that type.
67+
if let Some(tys) = tuple_ty {
68+
let mut zipped_decls = String::new();
69+
for (pat, ty, expr) in
70+
itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
71+
{
72+
zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents))
73+
}
74+
edit.replace(parent.text_range(), zipped_decls.trim());
75+
} else {
76+
let mut zipped_decls = String::new();
77+
for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
78+
zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents));
79+
}
80+
edit.replace(parent.text_range(), zipped_decls.trim());
81+
}
82+
},
83+
)
84+
}
85+
86+
#[cfg(test)]
87+
mod tests {
88+
use crate::tests::check_assist;
89+
90+
use super::*;
91+
92+
#[test]
93+
fn unwrap_tuples() {
94+
check_assist(
95+
unwrap_tuple,
96+
r#"
97+
fn main() {
98+
$0let (foo, bar) = ("Foo", "Bar");
99+
}
100+
"#,
101+
r#"
102+
fn main() {
103+
let foo = "Foo";
104+
let bar = "Bar";
105+
}
106+
"#,
107+
);
108+
109+
check_assist(
110+
unwrap_tuple,
111+
r#"
112+
fn main() {
113+
$0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
114+
}
115+
"#,
116+
r#"
117+
fn main() {
118+
let foo = "Foo";
119+
let bar = "Bar";
120+
let baz = "Baz";
121+
}
122+
"#,
123+
);
124+
}
125+
126+
#[test]
127+
fn unwrap_tuple_with_types() {
128+
check_assist(
129+
unwrap_tuple,
130+
r#"
131+
fn main() {
132+
$0let (foo, bar): (u8, i32) = (5, 10);
133+
}
134+
"#,
135+
r#"
136+
fn main() {
137+
let foo: u8 = 5;
138+
let bar: i32 = 10;
139+
}
140+
"#,
141+
);
142+
143+
check_assist(
144+
unwrap_tuple,
145+
r#"
146+
fn main() {
147+
$0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
148+
}
149+
"#,
150+
r#"
151+
fn main() {
152+
let foo: u8 = 5;
153+
let bar: i32 = 10;
154+
let baz: f64 = 17.5;
155+
}
156+
"#,
157+
);
158+
}
159+
}

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)