Unnamed repository; edit this file 'description' to name the repository.
fix: Improve prettify_macro_expansion()
- Add basic tests for prettify_macro_expansion
Example
---
```rust
const _: () = {
loop {break}
foo()
};
```
**Before this PR**
```rust
const _:() = {
loop{
break
}foo()
};
```
**After this PR**
```rust
const _: () = {
loop {
break
}
foo()
};
```
---
```rust
const _: () = {
match 2 {
tmp => foo!(),
};
};
```
**Before this PR**
```rust
const _:() = {
match 2 {
tmp => foo!(),
};
};
```
**After this PR**
```rust
const _: () = {
match 2 {
tmp => foo!(),
};
};
```
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/inline_macro.rs | 2 | ||||
| -rw-r--r-- | crates/ide-completion/src/completions/item_list/trait_impl.rs | 2 | ||||
| -rw-r--r-- | crates/ide/src/expand_macro.rs | 15 | ||||
| -rw-r--r-- | crates/mbe/src/tests.rs | 2 | ||||
| -rw-r--r-- | crates/syntax-bridge/Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/syntax-bridge/src/prettify_macro_expansion.rs | 328 |
7 files changed, 316 insertions, 35 deletions
diff --git a/Cargo.lock b/Cargo.lock index e6575c28c1..92acf544ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2744,6 +2744,7 @@ dependencies = [ name = "syntax-bridge" version = "0.0.0" dependencies = [ + "expect-test", "intern", "parser", "rustc-hash 2.1.1", diff --git a/crates/ide-assists/src/handlers/inline_macro.rs b/crates/ide-assists/src/handlers/inline_macro.rs index 280bd7f2ca..1cf5402db8 100644 --- a/crates/ide-assists/src/handlers/inline_macro.rs +++ b/crates/ide-assists/src/handlers/inline_macro.rs @@ -258,7 +258,7 @@ macro_rules! whitespace { if true {} }; } -fn f() { if true{}; } +fn f() { if true {}; } "#, ) } diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index 4072f05a41..9bac34885c 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -1356,7 +1356,7 @@ noop! { struct Test; impl Foo for Test { - fn foo(&mut self,bar:i64,baz: &mut u32) -> Result<(),u32> { + fn foo(&mut self,bar: i64,baz: &mut u32) -> Result<(),u32> { $0 } } diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 6ac4fa1fba..fe58786991 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -357,7 +357,7 @@ fn main() { "#, expect![[r#" bar! - for _ in 0..42{}"#]], + for _ in 0..42 {}"#]], ); } @@ -433,9 +433,7 @@ fn main() { expect![[r#" match_ast! { - if let Some(it) = ast::TraitDef::cast(container.clone()){} - else if let Some(it) = ast::ImplDef::cast(container.clone()){} - else { + if let Some(it) = ast::TraitDef::cast(container.clone()){}else if let Some(it) = ast::ImplDef::cast(container.clone()){}else { { continue } @@ -594,7 +592,7 @@ struct Foo {} "#, expect![[r#" proc_macros::DeriveIdentity - struct Foo{}"#]], + struct Foo {}"#]], ); } @@ -612,7 +610,7 @@ struct Foo {} expect![[r#" proc_macros::DeriveIdentity #[derive(proc_macros::DeriveIdentity)] - struct Foo{}"#]], + struct Foo {}"#]], ); } @@ -628,7 +626,7 @@ struct Foo {} "#, expect![[r#" proc_macros::DeriveIdentity - struct Foo{}"#]], + struct Foo {}"#]], ); check( r#" @@ -640,7 +638,7 @@ struct Foo {} "#, expect![[r#" proc_macros::DeriveIdentity - struct Foo{}"#]], + struct Foo {}"#]], ); } @@ -783,7 +781,6 @@ foo(); macro_rules! foo { () => { fn item(){} - }; } foo();"#]], diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 4a1af31656..271dfc877b 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs @@ -237,7 +237,7 @@ fn expr_2021() { PUNCH ; [alone] 0:Root[0000, 0]@39..40#ROOT2024 _; - (const { + (const { 1 });"#]], ); diff --git a/crates/syntax-bridge/Cargo.toml b/crates/syntax-bridge/Cargo.toml index b0fd40ff59..c928f23e02 100644 --- a/crates/syntax-bridge/Cargo.toml +++ b/crates/syntax-bridge/Cargo.toml @@ -25,6 +25,7 @@ span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true [dev-dependencies] +expect-test = "1.5.1" test-utils.workspace = true [features] diff --git a/crates/syntax-bridge/src/prettify_macro_expansion.rs b/crates/syntax-bridge/src/prettify_macro_expansion.rs index 2f932e0458..43f270800d 100644 --- a/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -45,17 +45,23 @@ pub fn prettify_macro_expansion( for event in syn.preorder_with_tokens() { let token = match event { WalkEvent::Enter(NodeOrToken::Token(token)) => token, - WalkEvent::Leave(NodeOrToken::Node(node)) - if matches!( - node.kind(), - ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES - ) => - { - if indent > 0 { - mods.push((Position::after(node.clone()), PrettifyWsKind::Indent(indent))); - } - if node.parent().is_some() { - mods.push((Position::after(node), PrettifyWsKind::Newline)); + WalkEvent::Leave(NodeOrToken::Node(node)) => { + let is_last_child = + node.parent().is_some_and(|parent| parent.last_child().as_ref() == Some(&node)); + let is_always_newline = matches!(node.kind(), ATTR); + let is_non_last_newline = match node.kind() { + MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES | EXTERN_BLOCK + | EXTERN_CRATE | MODULE => true, + EXPR_STMT if Some(R_CURLY) == node.last_token().map(|it| it.kind()) => true, + _ => false, + }; + if !is_last_child && is_non_last_newline || is_always_newline { + if indent > 0 { + mods.push((Position::after(node.clone()), PrettifyWsKind::Indent(indent))); + } + if node.parent().is_some() { + mods.push((Position::after(node), PrettifyWsKind::Newline)); + } } continue; } @@ -77,16 +83,12 @@ pub fn prettify_macro_expansion( match tok.kind() { k if is_text(k) - && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#]), false) => + && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#] | L_CURLY), false) => { mods.push(do_ws(after, tok)); } L_CURLY if is_next(|it| it != R_CURLY, true) => { indent += 1; - if is_last(is_text, false) { - mods.push(do_ws(before, tok)); - } - mods.push(do_indent(after, tok, indent)); mods.push(do_nl(after, tok)); } @@ -98,16 +100,10 @@ pub fn prettify_macro_expansion( } mods.push(do_nl(before, tok)); } - R_CURLY => { - if indent > 0 { - mods.push(do_indent(after, tok, indent)); - } - mods.push(do_nl(after, tok)); - } LIFETIME_IDENT if is_next(is_text, true) => { mods.push(do_ws(after, tok)); } - AS_KW | DYN_KW | IMPL_KW | CONST_KW | MUT_KW => { + AS_KW | DYN_KW | IMPL_KW | CONST_KW | MUT_KW | LET_KW | MATCH_KW => { mods.push(do_ws(after, tok)); } T![;] if is_next(|it| it != R_CURLY, true) => { @@ -126,6 +122,12 @@ pub fn prettify_macro_expansion( mods.push(do_ws(before, tok)); mods.push(do_ws(after, tok)); } + T![:] if is_next(|it| it != T![:], false) && is_last(|it| it != T![:], false) => { + // XXX: Why input included WHITESPACE? + if is_next(|it| it != SyntaxKind::WHITESPACE, false) { + mods.push(do_ws(after, tok)); + } + } T![!] if is_last(|it| it == MACRO_RULES_KW, false) && is_next(is_text, false) => { mods.push(do_ws(after, tok)); } @@ -161,3 +163,283 @@ fn is_text(k: SyntaxKind) -> bool { // Consider all keywords in all editions. k.is_any_identifier() || k.is_literal() || k == UNDERSCORE } + +#[cfg(test)] +mod tests { + use super::*; + use expect_test::{Expect, expect}; + + #[expect(deprecated)] + fn check_pretty(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let ra_fixture = stdx::trim_indent(ra_fixture); + let source_file = syntax::ast::SourceFile::parse(&ra_fixture, span::Edition::CURRENT); + let syn = source_file.syntax_node().clone_for_update(); + + remove_whitespaces(&syn); + let pretty = prettify_macro_expansion(syn, &mut |_| None, |_| ()); + let mut pretty = pretty.to_string(); + if pretty.contains('\n') { + pretty.push('\n'); + } + expect.assert_eq(&pretty); + + fn remove_whitespaces(node: &SyntaxNode) { + let mut to_remove = vec![]; + node.preorder_with_tokens().for_each(|it| match it { + WalkEvent::Enter(NodeOrToken::Token(tok)) if tok.kind().is_trivia() => { + to_remove.push(tok); + } + _ => (), + }); + to_remove.iter().for_each(ted::remove) + } + } + + #[test] + fn test_colon() { + check_pretty( + r#" + const X: i32 = x::y::z; + macro_rules! foo { + () => { + $crate::foo::bar!() + }; + } + "#, + expect![[r#" + const X: i32 = x::y::z; + macro_rules! foo { + () => { + $crate::foo::bar!() + }; + } + "#]], + ); + } + + #[test] + fn test_curly_indent() { + check_pretty( + r#" + const _: () = { + { + 2; + 3 + } + }; + "#, + expect![[r#" + const _: () = { + { + 2; + 3 + } + }; + "#]], + ); + } + + #[test] + fn test_pats() { + check_pretty( + r#" + const _: () = { + let x = 2; + let mut y = 3; + let ref mut z @ 0..5 = 4; + let (x, ref y) = (5, 6); + let (Foo { x, y }, Bar(z, t)); + let (&mut x, (y | y)); + match () {} + }; + "#, + expect![[r#" + const _: () = { + let x = 2; + let mut y = 3; + let ref mut [email protected] = 4; + let (x,ref y) = (5,6); + let (Foo { + x,y + },Bar(z,t)); + let (&mut x,(y|y)); + match (){} + }; + "#]], + ); + } + + #[test] + fn test_attrs() { + check_pretty( + r#" + #[attr1] + #[attr2] + const _: () = {}; + #[attr1] + const _: () = { + #[attr2] + {} + }; + "#, + expect![[r#" + #[attr1] + #[attr2] + const _: () = {}; + #[attr1] + const _: () = { + #[attr2] + {} + }; + "#]], + ); + } + + #[test] + fn test_items() { + check_pretty( + r#" + fn foo() {} + struct Foo {} + struct Foo; + enum Foo {} + impl Foo {} + const _: () = {}; + static S: () = {}; + extern {} + mod x {} + mod x; + type X = 2; + use a; + use b::{c, d}; + macro_rules! foo { () => {}; } + "#, + expect![[r#" + fn foo(){} + struct Foo {} + struct Foo; + + enum Foo {} + impl Foo {} + const _: () = {}; + static S: () = {}; + extern {} + mod x {} + mod x; + + type X = 2; + use a; + use b::{ + c,d + }; + macro_rules! foo { + () => {}; + } + "#]], + ); + } + + #[test] + fn test_exprs() { + check_pretty( + r#" + const _: () = { + let _ = 1+2; + let _ = !true && false; + let _ = foo() + !bar() + dbg!(2) + *x; + let _ = async move || {}; + let _ = async move {}; + let _ = x.await; + 'lab: for _ in 0..5 { + loop { } + break 'lab expr; + if let pat = expr { + foo() + } else if true { + bar() + } else {} + fun() + } + }; + "#, + expect![[r#" + const _: () = { + let _ = 1+2; + let _ = !true&&false; + let _ = foo()+!bar()+dbg!(2)+*x; + let _ = async move||{}; + let _ = async move {}; + let _ = x.await; + 'lab: for _ in 0..5 { + loop {} + break 'lab expr; + if let pat = expr { + foo() + }else if true { + bar() + }else {} + fun() + } + }; + "#]], + ); + } + + #[test] + fn test_match_arm() { + check_pretty( + r#" + const _: () = { + match 2 { + tmp => foo!(), + }; + }; + "#, + expect![[r#" + const _: () = { + match 2 { + tmp => foo!(), + }; + }; + "#]], + ); + + check_pretty( + r#" + const _: () = { + match 2 { + tmp => {} + }; + }; + "#, + expect![[r#" + const _: () = { + match 2 { + tmp => {} + }; + }; + "#]], + ); + + check_pretty( + r#" + const _: () = { + match 2 { + 1 => {} + 2 => foo(), + _ => {}, + }; + }; + "#, + expect![[r#" + const _: () = { + match 2 { + 1 => {} + 2 => foo(), + _ => {}, + }; + }; + "#]], + ); + } +} |