Skip to content

Commit 3a9ba0e

Browse files
committed
feat: join lines merges assignments
1 parent 996300f commit 3a9ba0e

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

crates/ide/src/join_lines.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub struct JoinLinesConfig {
1515
pub join_else_if: bool,
1616
pub remove_trailing_comma: bool,
1717
pub unwrap_trivial_blocks: bool,
18+
pub join_assignments: bool,
1819
}
1920

2021
// Feature: Join Lines
@@ -162,6 +163,12 @@ fn remove_newline(
162163
}
163164
}
164165

166+
if config.join_assignments {
167+
if join_assignments(edit, &prev, &next).is_some() {
168+
return;
169+
}
170+
}
171+
165172
if config.unwrap_trivial_blocks {
166173
// Special case that turns something like:
167174
//
@@ -232,6 +239,41 @@ fn join_single_use_tree(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Opti
232239
Some(())
233240
}
234241

242+
fn join_assignments(
243+
edit: &mut TextEditBuilder,
244+
prev: &SyntaxElement,
245+
next: &SyntaxElement,
246+
) -> Option<()> {
247+
let let_stmt = ast::LetStmt::cast(prev.as_node()?.clone())?;
248+
if let_stmt.eq_token().is_some() {
249+
cov_mark::hit!(join_assignments_already_initialized);
250+
return None;
251+
}
252+
let let_ident_pat = match let_stmt.pat()? {
253+
ast::Pat::IdentPat(it) => it,
254+
_ => return None,
255+
};
256+
257+
let expr_stmt = ast::ExprStmt::cast(next.as_node()?.clone())?;
258+
let bin_expr = match expr_stmt.expr()? {
259+
ast::Expr::BinExpr(it) => it,
260+
_ => return None,
261+
};
262+
if !matches!(bin_expr.op_kind()?, ast::BinaryOp::Assignment { op: None }) {
263+
return None;
264+
}
265+
let lhs = bin_expr.lhs()?;
266+
let name_ref = lhs.name_ref()?;
267+
268+
if name_ref.to_string() != let_ident_pat.syntax().to_string() {
269+
cov_mark::hit!(join_assignments_mismatch);
270+
return None;
271+
}
272+
273+
edit.delete(let_stmt.semicolon_token()?.text_range().cover(lhs.syntax().text_range()));
274+
Some(())
275+
}
276+
235277
fn as_if_expr(element: &SyntaxElement) -> Option<ast::IfExpr> {
236278
let mut node = element.as_node()?.clone();
237279
if let Some(stmt) = ast::ExprStmt::cast(node.clone()) {
@@ -275,6 +317,7 @@ mod tests {
275317
join_else_if: true,
276318
remove_trailing_comma: true,
277319
unwrap_trivial_blocks: true,
320+
join_assignments: true,
278321
};
279322

280323
let (before_cursor_pos, before) = extract_offset(ra_fixture_before);
@@ -300,6 +343,7 @@ mod tests {
300343
join_else_if: true,
301344
remove_trailing_comma: true,
302345
unwrap_trivial_blocks: true,
346+
join_assignments: true,
303347
};
304348

305349
let (sel, before) = extract_range(ra_fixture_before);
@@ -990,6 +1034,55 @@ fn main() {
9901034
9911035
}
9921036
}
1037+
"#,
1038+
);
1039+
}
1040+
1041+
#[test]
1042+
fn join_assignments() {
1043+
check_join_lines(
1044+
r#"
1045+
fn foo() {
1046+
$0let foo;
1047+
foo = "bar";
1048+
}
1049+
"#,
1050+
r#"
1051+
fn foo() {
1052+
$0let foo = "bar";
1053+
}
1054+
"#,
1055+
);
1056+
1057+
cov_mark::check!(join_assignments_mismatch);
1058+
check_join_lines(
1059+
r#"
1060+
fn foo() {
1061+
let foo;
1062+
let qux;$0
1063+
foo = "bar";
1064+
}
1065+
"#,
1066+
r#"
1067+
fn foo() {
1068+
let foo;
1069+
let qux;$0 foo = "bar";
1070+
}
1071+
"#,
1072+
);
1073+
1074+
cov_mark::check!(join_assignments_already_initialized);
1075+
check_join_lines(
1076+
r#"
1077+
fn foo() {
1078+
let foo = "bar";$0
1079+
foo = "bar";
1080+
}
1081+
"#,
1082+
r#"
1083+
fn foo() {
1084+
let foo = "bar";$0 foo = "bar";
1085+
}
9931086
"#,
9941087
);
9951088
}

crates/rust-analyzer/src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ config_data! {
206206
joinLines_removeTrailingComma: bool = "true",
207207
/// Join lines unwraps trivial blocks.
208208
joinLines_unwrapTrivialBlock: bool = "true",
209+
/// Join lines merges consecutive declaration and initialization of an assignment.
210+
joinLines_joinAssignments: bool = "true",
209211

210212
/// Whether to show `Debug` lens. Only applies when
211213
/// `#rust-analyzer.lens.enable#` is set.
@@ -786,6 +788,7 @@ impl Config {
786788
join_else_if: self.data.joinLines_joinElseIf,
787789
remove_trailing_comma: self.data.joinLines_removeTrailingComma,
788790
unwrap_trivial_blocks: self.data.joinLines_unwrapTrivialBlock,
791+
join_assignments: self.data.joinLines_joinAssignments,
789792
}
790793
}
791794
pub fn call_info_full(&self) -> bool {

docs/user/generated_config.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ Join lines removes trailing commas.
318318
--
319319
Join lines unwraps trivial blocks.
320320
--
321+
[[rust-analyzer.joinLines.joinAssignments]]rust-analyzer.joinLines.joinAssignments (default: `true`)::
322+
+
323+
--
324+
Join lines merges consecutive declaration and initialization of an assignment.
325+
--
321326
[[rust-analyzer.lens.debug]]rust-analyzer.lens.debug (default: `true`)::
322327
+
323328
--

editors/code/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,11 @@
761761
"default": true,
762762
"type": "boolean"
763763
},
764+
"rust-analyzer.joinLines.joinAssignments": {
765+
"markdownDescription": "Join lines merges consecutive declaration and initialization of an assignment.",
766+
"default": true,
767+
"type": "boolean"
768+
},
764769
"rust-analyzer.lens.debug": {
765770
"markdownDescription": "Whether to show `Debug` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
766771
"default": true,

0 commit comments

Comments
 (0)