Skip to content

Commit a512774

Browse files
committed
Auto merge of rust-lang#14752 - ponyii:fix/generate_derive_breaks_indentation, r=Veykril
fix: assists no longer break indentation Fixes rust-lang/rust-analyzer#14674 These are _ad hoc_ patches for a number of assists that can produce incorrectly indented code, namely: - generate_derive - add_missing_impl_members - add_missing_default_members Some general solution is required in future, as the same problem arises in many other assists, e.g. - replace_derive_with... - generate_default_from_enum... - generate_default_from_new - generate_delegate_methods (the list is incomplete)
2 parents 2f840c2 + 49f1092 commit a512774

File tree

4 files changed

+217
-10
lines changed

4 files changed

+217
-10
lines changed

crates/ide-assists/src/handlers/add_missing_impl_members.rs

Lines changed: 168 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,8 +1346,8 @@ struct SomeStruct {
13461346
}
13471347
impl PartialEq for SomeStruct {
13481348
$0fn ne(&self, other: &Self) -> bool {
1349-
!self.eq(other)
1350-
}
1349+
!self.eq(other)
1350+
}
13511351
}
13521352
"#,
13531353
);
@@ -1511,11 +1511,175 @@ fn main() {
15111511
struct S;
15121512
impl Tr for S {
15131513
fn method() {
1514-
${0:todo!()}
1515-
}
1514+
${0:todo!()}
1515+
}
15161516
}
15171517
}
15181518
"#,
15191519
);
15201520
}
1521+
1522+
#[test]
1523+
fn test_add_missing_preserves_indentation() {
1524+
// in different modules
1525+
check_assist(
1526+
add_missing_impl_members,
1527+
r#"
1528+
mod m {
1529+
pub trait Foo {
1530+
const CONST_MULTILINE: (
1531+
i32,
1532+
i32
1533+
);
1534+
1535+
fn foo(&self);
1536+
}
1537+
}
1538+
struct S;
1539+
impl m::Foo for S { $0 }"#,
1540+
r#"
1541+
mod m {
1542+
pub trait Foo {
1543+
const CONST_MULTILINE: (
1544+
i32,
1545+
i32
1546+
);
1547+
1548+
fn foo(&self);
1549+
}
1550+
}
1551+
struct S;
1552+
impl m::Foo for S {
1553+
$0const CONST_MULTILINE: (
1554+
i32,
1555+
i32
1556+
);
1557+
1558+
fn foo(&self) {
1559+
todo!()
1560+
}
1561+
}"#,
1562+
);
1563+
// in the same module
1564+
check_assist(
1565+
add_missing_impl_members,
1566+
r#"
1567+
mod m {
1568+
trait Foo {
1569+
type Output;
1570+
1571+
const CONST: usize = 42;
1572+
const CONST_2: i32;
1573+
const CONST_MULTILINE: (
1574+
i32,
1575+
i32
1576+
);
1577+
1578+
fn foo(&self);
1579+
fn bar(&self);
1580+
fn baz(&self);
1581+
}
1582+
1583+
struct S;
1584+
1585+
impl Foo for S {
1586+
fn bar(&self) {}
1587+
$0
1588+
}
1589+
}"#,
1590+
r#"
1591+
mod m {
1592+
trait Foo {
1593+
type Output;
1594+
1595+
const CONST: usize = 42;
1596+
const CONST_2: i32;
1597+
const CONST_MULTILINE: (
1598+
i32,
1599+
i32
1600+
);
1601+
1602+
fn foo(&self);
1603+
fn bar(&self);
1604+
fn baz(&self);
1605+
}
1606+
1607+
struct S;
1608+
1609+
impl Foo for S {
1610+
fn bar(&self) {}
1611+
1612+
$0type Output;
1613+
1614+
const CONST_2: i32;
1615+
1616+
const CONST_MULTILINE: (
1617+
i32,
1618+
i32
1619+
);
1620+
1621+
fn foo(&self) {
1622+
todo!()
1623+
}
1624+
1625+
fn baz(&self) {
1626+
todo!()
1627+
}
1628+
1629+
}
1630+
}"#,
1631+
);
1632+
}
1633+
1634+
#[test]
1635+
fn test_add_default_preserves_indentation() {
1636+
check_assist(
1637+
add_missing_default_members,
1638+
r#"
1639+
mod m {
1640+
pub trait Foo {
1641+
type Output;
1642+
1643+
const CONST: usize = 42;
1644+
const CONST_2: i32;
1645+
const CONST_MULTILINE: = (
1646+
i32,
1647+
i32,
1648+
) = (3, 14);
1649+
1650+
fn valid(some: u32) -> bool { false }
1651+
fn foo(some: u32) -> bool;
1652+
}
1653+
}
1654+
struct S;
1655+
impl m::Foo for S { $0 }"#,
1656+
r#"
1657+
mod m {
1658+
pub trait Foo {
1659+
type Output;
1660+
1661+
const CONST: usize = 42;
1662+
const CONST_2: i32;
1663+
const CONST_MULTILINE: = (
1664+
i32,
1665+
i32,
1666+
) = (3, 14);
1667+
1668+
fn valid(some: u32) -> bool { false }
1669+
fn foo(some: u32) -> bool;
1670+
}
1671+
}
1672+
struct S;
1673+
impl m::Foo for S {
1674+
$0const CONST: usize = 42;
1675+
1676+
const CONST_MULTILINE: = (
1677+
i32,
1678+
i32,
1679+
) = (3, 14);
1680+
1681+
fn valid(some: u32) -> bool { false }
1682+
}"#,
1683+
)
1684+
}
15211685
}

crates/ide-assists/src/handlers/generate_derive.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use syntax::{
2-
ast::{self, AstNode, HasAttrs},
2+
ast::{self, edit::IndentLevel, AstNode, HasAttrs},
33
SyntaxKind::{COMMENT, WHITESPACE},
44
TextSize,
55
};
@@ -42,7 +42,12 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
4242
.next();
4343
match derive_attr {
4444
None => {
45-
builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
45+
let indent_level = IndentLevel::from_node(nominal.syntax());
46+
builder.insert_snippet(
47+
cap,
48+
node_start,
49+
format!("#[derive($0)]\n{indent_level}"),
50+
);
4651
}
4752
Some(tt) => {
4853
// Just move the cursor.
@@ -84,6 +89,20 @@ mod tests {
8489
"struct Foo { $0 a: i32, }",
8590
"#[derive($0)]\nstruct Foo { a: i32, }",
8691
);
92+
check_assist(
93+
generate_derive,
94+
"
95+
mod m {
96+
struct Foo { a: i32,$0 }
97+
}
98+
",
99+
"
100+
mod m {
101+
#[derive($0)]
102+
struct Foo { a: i32, }
103+
}
104+
",
105+
);
87106
}
88107

89108
#[test]
@@ -111,6 +130,24 @@ struct Foo { a: i32$0, }
111130
struct Foo { a: i32, }
112131
",
113132
);
133+
check_assist(
134+
generate_derive,
135+
"
136+
mod m {
137+
/// `Foo` is a pretty important struct.
138+
/// It does stuff.
139+
struct Foo { a: i32,$0 }
140+
}
141+
",
142+
"
143+
mod m {
144+
/// `Foo` is a pretty important struct.
145+
/// It does stuff.
146+
#[derive($0)]
147+
struct Foo { a: i32, }
148+
}
149+
",
150+
);
114151
}
115152

116153
#[test]

crates/ide-assists/src/utils.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use stdx::format_to;
99
use syntax::{
1010
ast::{
1111
self,
12-
edit::{self, AstNodeEdit},
13-
edit_in_place::{AttrsOwnerEdit, Removable},
12+
edit::{AstNodeEdit, IndentLevel},
13+
edit_in_place::{AttrsOwnerEdit, Indent, Removable},
1414
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
1515
},
1616
ted, AstNode, AstToken, Direction, SourceFile,
@@ -139,9 +139,11 @@ pub fn add_trait_assoc_items_to_impl(
139139

140140
let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
141141

142+
let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1;
142143
let items = items.into_iter().map(|assoc_item| {
143144
transform.apply(assoc_item.syntax());
144145
assoc_item.remove_attrs_and_docs();
146+
assoc_item.reindent_to(new_indent_level);
145147
assoc_item
146148
});
147149

@@ -153,8 +155,10 @@ pub fn add_trait_assoc_items_to_impl(
153155
first_item.get_or_insert_with(|| item.clone());
154156
match &item {
155157
ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
156-
let body = make::block_expr(None, Some(make::ext::expr_todo()))
157-
.indent(edit::IndentLevel(1));
158+
let body = AstNodeEdit::indent(
159+
&make::block_expr(None, Some(make::ext::expr_todo())),
160+
new_indent_level,
161+
);
158162
ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
159163
}
160164
ast::AssocItem::TypeAlias(type_alias) => {

crates/syntax/src/ast/edit_in_place.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,8 @@ impl ast::Impl {
465465
}
466466

467467
impl ast::AssocItemList {
468+
/// Attention! This function does align the first line of `item` with respect to `self`,
469+
/// but it does _not_ change indentation of other lines (if any).
468470
pub fn add_item(&self, item: ast::AssocItem) {
469471
let (indent, position, whitespace) = match self.assoc_items().last() {
470472
Some(last_item) => (

0 commit comments

Comments
 (0)