Auto merge of #14752 - ponyii:fix/generate_derive_breaks_indentation, r=Veykril
fix: assists no longer break indentation Fixes https://github.com/rust-lang/rust-analyzer/issues/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)
This commit is contained in:
commit
a512774fd9
4 changed files with 217 additions and 10 deletions
|
|
@ -1346,8 +1346,8 @@ struct SomeStruct {
|
|||
}
|
||||
impl PartialEq for SomeStruct {
|
||||
$0fn ne(&self, other: &Self) -> bool {
|
||||
!self.eq(other)
|
||||
}
|
||||
!self.eq(other)
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
@ -1511,11 +1511,175 @@ fn main() {
|
|||
struct S;
|
||||
impl Tr for S {
|
||||
fn method() {
|
||||
${0:todo!()}
|
||||
}
|
||||
${0:todo!()}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_missing_preserves_indentation() {
|
||||
// in different modules
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
r#"
|
||||
mod m {
|
||||
pub trait Foo {
|
||||
const CONST_MULTILINE: (
|
||||
i32,
|
||||
i32
|
||||
);
|
||||
|
||||
fn foo(&self);
|
||||
}
|
||||
}
|
||||
struct S;
|
||||
impl m::Foo for S { $0 }"#,
|
||||
r#"
|
||||
mod m {
|
||||
pub trait Foo {
|
||||
const CONST_MULTILINE: (
|
||||
i32,
|
||||
i32
|
||||
);
|
||||
|
||||
fn foo(&self);
|
||||
}
|
||||
}
|
||||
struct S;
|
||||
impl m::Foo for S {
|
||||
$0const CONST_MULTILINE: (
|
||||
i32,
|
||||
i32
|
||||
);
|
||||
|
||||
fn foo(&self) {
|
||||
todo!()
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
// in the same module
|
||||
check_assist(
|
||||
add_missing_impl_members,
|
||||
r#"
|
||||
mod m {
|
||||
trait Foo {
|
||||
type Output;
|
||||
|
||||
const CONST: usize = 42;
|
||||
const CONST_2: i32;
|
||||
const CONST_MULTILINE: (
|
||||
i32,
|
||||
i32
|
||||
);
|
||||
|
||||
fn foo(&self);
|
||||
fn bar(&self);
|
||||
fn baz(&self);
|
||||
}
|
||||
|
||||
struct S;
|
||||
|
||||
impl Foo for S {
|
||||
fn bar(&self) {}
|
||||
$0
|
||||
}
|
||||
}"#,
|
||||
r#"
|
||||
mod m {
|
||||
trait Foo {
|
||||
type Output;
|
||||
|
||||
const CONST: usize = 42;
|
||||
const CONST_2: i32;
|
||||
const CONST_MULTILINE: (
|
||||
i32,
|
||||
i32
|
||||
);
|
||||
|
||||
fn foo(&self);
|
||||
fn bar(&self);
|
||||
fn baz(&self);
|
||||
}
|
||||
|
||||
struct S;
|
||||
|
||||
impl Foo for S {
|
||||
fn bar(&self) {}
|
||||
|
||||
$0type Output;
|
||||
|
||||
const CONST_2: i32;
|
||||
|
||||
const CONST_MULTILINE: (
|
||||
i32,
|
||||
i32
|
||||
);
|
||||
|
||||
fn foo(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn baz(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_default_preserves_indentation() {
|
||||
check_assist(
|
||||
add_missing_default_members,
|
||||
r#"
|
||||
mod m {
|
||||
pub trait Foo {
|
||||
type Output;
|
||||
|
||||
const CONST: usize = 42;
|
||||
const CONST_2: i32;
|
||||
const CONST_MULTILINE: = (
|
||||
i32,
|
||||
i32,
|
||||
) = (3, 14);
|
||||
|
||||
fn valid(some: u32) -> bool { false }
|
||||
fn foo(some: u32) -> bool;
|
||||
}
|
||||
}
|
||||
struct S;
|
||||
impl m::Foo for S { $0 }"#,
|
||||
r#"
|
||||
mod m {
|
||||
pub trait Foo {
|
||||
type Output;
|
||||
|
||||
const CONST: usize = 42;
|
||||
const CONST_2: i32;
|
||||
const CONST_MULTILINE: = (
|
||||
i32,
|
||||
i32,
|
||||
) = (3, 14);
|
||||
|
||||
fn valid(some: u32) -> bool { false }
|
||||
fn foo(some: u32) -> bool;
|
||||
}
|
||||
}
|
||||
struct S;
|
||||
impl m::Foo for S {
|
||||
$0const CONST: usize = 42;
|
||||
|
||||
const CONST_MULTILINE: = (
|
||||
i32,
|
||||
i32,
|
||||
) = (3, 14);
|
||||
|
||||
fn valid(some: u32) -> bool { false }
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use syntax::{
|
||||
ast::{self, AstNode, HasAttrs},
|
||||
ast::{self, edit::IndentLevel, AstNode, HasAttrs},
|
||||
SyntaxKind::{COMMENT, WHITESPACE},
|
||||
TextSize,
|
||||
};
|
||||
|
|
@ -42,7 +42,12 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
|
|||
.next();
|
||||
match derive_attr {
|
||||
None => {
|
||||
builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
|
||||
let indent_level = IndentLevel::from_node(nominal.syntax());
|
||||
builder.insert_snippet(
|
||||
cap,
|
||||
node_start,
|
||||
format!("#[derive($0)]\n{indent_level}"),
|
||||
);
|
||||
}
|
||||
Some(tt) => {
|
||||
// Just move the cursor.
|
||||
|
|
@ -84,6 +89,20 @@ mod tests {
|
|||
"struct Foo { $0 a: i32, }",
|
||||
"#[derive($0)]\nstruct Foo { a: i32, }",
|
||||
);
|
||||
check_assist(
|
||||
generate_derive,
|
||||
"
|
||||
mod m {
|
||||
struct Foo { a: i32,$0 }
|
||||
}
|
||||
",
|
||||
"
|
||||
mod m {
|
||||
#[derive($0)]
|
||||
struct Foo { a: i32, }
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -111,6 +130,24 @@ struct Foo { a: i32$0, }
|
|||
struct Foo { a: i32, }
|
||||
",
|
||||
);
|
||||
check_assist(
|
||||
generate_derive,
|
||||
"
|
||||
mod m {
|
||||
/// `Foo` is a pretty important struct.
|
||||
/// It does stuff.
|
||||
struct Foo { a: i32,$0 }
|
||||
}
|
||||
",
|
||||
"
|
||||
mod m {
|
||||
/// `Foo` is a pretty important struct.
|
||||
/// It does stuff.
|
||||
#[derive($0)]
|
||||
struct Foo { a: i32, }
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use stdx::format_to;
|
|||
use syntax::{
|
||||
ast::{
|
||||
self,
|
||||
edit::{self, AstNodeEdit},
|
||||
edit_in_place::{AttrsOwnerEdit, Removable},
|
||||
edit::{AstNodeEdit, IndentLevel},
|
||||
edit_in_place::{AttrsOwnerEdit, Indent, Removable},
|
||||
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
|
||||
},
|
||||
ted, AstNode, AstToken, Direction, SourceFile,
|
||||
|
|
@ -139,9 +139,11 @@ pub fn add_trait_assoc_items_to_impl(
|
|||
|
||||
let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
|
||||
|
||||
let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1;
|
||||
let items = items.into_iter().map(|assoc_item| {
|
||||
transform.apply(assoc_item.syntax());
|
||||
assoc_item.remove_attrs_and_docs();
|
||||
assoc_item.reindent_to(new_indent_level);
|
||||
assoc_item
|
||||
});
|
||||
|
||||
|
|
@ -153,8 +155,10 @@ pub fn add_trait_assoc_items_to_impl(
|
|||
first_item.get_or_insert_with(|| item.clone());
|
||||
match &item {
|
||||
ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
|
||||
let body = make::block_expr(None, Some(make::ext::expr_todo()))
|
||||
.indent(edit::IndentLevel(1));
|
||||
let body = AstNodeEdit::indent(
|
||||
&make::block_expr(None, Some(make::ext::expr_todo())),
|
||||
new_indent_level,
|
||||
);
|
||||
ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
|
||||
}
|
||||
ast::AssocItem::TypeAlias(type_alias) => {
|
||||
|
|
|
|||
|
|
@ -465,6 +465,8 @@ impl ast::Impl {
|
|||
}
|
||||
|
||||
impl ast::AssocItemList {
|
||||
/// Attention! This function does align the first line of `item` with respect to `self`,
|
||||
/// but it does _not_ change indentation of other lines (if any).
|
||||
pub fn add_item(&self, item: ast::AssocItem) {
|
||||
let (indent, position, whitespace) = match self.assoc_items().last() {
|
||||
Some(last_item) => (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue