From 5004371a4ac0452c93063ba522e5187ea4d31113 Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 03:50:54 +0800 Subject: [PATCH 1/5] feat: Allow hir-def prettifier formatting into one-line --- crates/hir-def/src/body/pretty.rs | 70 ++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 55740a68ac..6555d8061e 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -16,6 +16,13 @@ use crate::{ use super::*; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(super) enum LineFormat { + Oneline, + Newline, + Indentation, +} + pub(super) fn print_body_hir( db: &dyn DefDatabase, body: &Body, @@ -52,7 +59,14 @@ pub(super) fn print_body_hir( } }; - let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false, edition }; + let mut p = Printer { + db, + body, + buf: header, + indent_level: 0, + line_format: LineFormat::Newline, + edition, + }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); let function_data = &db.function_data(it); @@ -95,8 +109,14 @@ pub(super) fn print_expr_hir( expr: ExprId, edition: Edition, ) -> String { - let mut p = - Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false, edition }; + let mut p = Printer { + db, + body, + buf: String::new(), + indent_level: 0, + line_format: LineFormat::Newline, + edition, + }; p.print_expr(expr); p.buf } @@ -109,10 +129,10 @@ macro_rules! w { macro_rules! wln { ($dst:expr) => { - { let _ = writeln!($dst); } + { $dst.newline(); } }; ($dst:expr, $($arg:tt)*) => { - { let _ = writeln!($dst, $($arg)*); } + { let _ = w!($dst, $($arg)*); $dst.newline(); } }; } @@ -121,24 +141,30 @@ struct Printer<'a> { body: &'a Body, buf: String, indent_level: usize, - needs_indent: bool, + line_format: LineFormat, edition: Edition, } impl Write for Printer<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { for line in s.split_inclusive('\n') { - if self.needs_indent { + if matches!(self.line_format, LineFormat::Indentation) { match self.buf.chars().rev().find(|ch| *ch != ' ') { Some('\n') | None => {} _ => self.buf.push('\n'), } self.buf.push_str(&" ".repeat(self.indent_level)); - self.needs_indent = false; } self.buf.push_str(line); - self.needs_indent = line.ends_with('\n'); + + if matches!(self.line_format, LineFormat::Newline | LineFormat::Indentation) { + self.line_format = if line.ends_with('\n') { + LineFormat::Indentation + } else { + LineFormat::Newline + }; + } } Ok(()) @@ -161,14 +187,28 @@ impl Printer<'_> { } } + // Add a newline if the current line is not empty. + // If the current line is empty, add a space instead. + // + // Do not use [`writeln!()`] or [`wln!()`] here, which will result in + // infinite recursive calls to this function. fn newline(&mut self) { - match self.buf.chars().rev().find_position(|ch| *ch != ' ') { - Some((_, '\n')) | None => {} - Some((idx, _)) => { - if idx != 0 { - self.buf.drain(self.buf.len() - idx..); + if matches!(self.line_format, LineFormat::Oneline) { + match self.buf.chars().last() { + Some(' ') | None => {} + Some(_) => { + w!(self, " "); + } + } + } else { + match self.buf.chars().rev().find_position(|ch| *ch != ' ') { + Some((_, '\n')) | None => {} + Some((idx, _)) => { + if idx != 0 { + self.buf.drain(self.buf.len() - idx..); + } + w!(self, "\n"); } - writeln!(self).unwrap() } } } From 60c42c25c7f88fe87d537c0107b75b59726bc603 Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 03:52:04 +0800 Subject: [PATCH 2/5] feat: add prettifier for Pat --- crates/hir-def/src/body.rs | 11 +++++++++++ crates/hir-def/src/body/pretty.rs | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index f18083d387..6493826f07 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -227,6 +227,17 @@ impl Body { pretty::print_expr_hir(db, self, owner, expr, edition) } + pub fn pretty_print_pat( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, + ) -> String { + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) + } + fn new( db: &dyn DefDatabase, owner: DefWithBodyId, diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 6555d8061e..3333956adb 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -121,6 +121,26 @@ pub(super) fn print_expr_hir( p.buf } +pub(super) fn print_pat_hir( + db: &dyn DefDatabase, + body: &Body, + _owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, +) -> String { + let mut p = Printer { + db, + body, + buf: String::new(), + indent_level: 0, + line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline }, + edition, + }; + p.print_pat(pat); + p.buf +} + macro_rules! w { ($dst:expr, $($arg:tt)*) => { { let _ = write!($dst, $($arg)*); } From 5caa56e18aeed31ea96be36aa2f36e40c6134786 Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 03:53:09 +0800 Subject: [PATCH 3/5] fix: use `pretty_print_pat` for params in fn --- crates/hir/src/display.rs | 13 ++-- crates/ide/src/hover/tests.rs | 115 ++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 923dca6466..c2b2fbef75 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -99,17 +99,20 @@ impl HirDisplay for Function { } // FIXME: Use resolved `param.ty` once we no longer discard lifetimes + let body = db.body(self.id.into()); for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) { - let local = param.as_local(db).map(|it| it.name(db)); if !first { f.write_str(", ")?; } else { first = false; } - match local { - Some(name) => write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?, - None => f.write_str("_: ")?, - } + + let pat_id = body.params[param.idx - body.self_param.is_some() as usize]; + let pat_str = + body.pretty_print_pat(db.upcast(), self.id.into(), pat_id, true, f.edition()); + f.write_str(&pat_str)?; + + f.write_str(": ")?; type_ref.hir_fmt(f)?; } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index f4528dd754..d1e51a2d28 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -8742,3 +8742,118 @@ fn foo() { "#]], ); } + + +#[test] +fn test_hover_function_with_pat_param() { + check( + r#"fn test_1$0((start_range, end_range): (u32, u32), a: i32) {}"#, + expect![[r#" + *test_1* + + ```rust + test + ``` + + ```rust + fn test_1((start_range, end_range): (u32, u32), a: i32) + ``` + "#]], + ); + + // Test case with tuple pattern and mutable parameters + check( + r#"fn test_2$0((mut x, y): (i32, i32)) {}"#, + expect![[r#" + *test_2* + + ```rust + test + ``` + + ```rust + fn test_2((mut x, y): (i32, i32)) + ``` + "#]], + ); + + // Test case with a pattern in a reference type + check( + r#"fn test_3$0(&(a, b): &(i32, i32)) {}"#, + expect![[r#" + *test_3* + + ```rust + test + ``` + + ```rust + fn test_3(&(a, b): &(i32, i32)) + ``` + "#]], + ); + + // Test case with complex pattern (struct destructuring) + check( + r#"struct Point { x: i32, y: i32 } fn test_4$0(Point { x, y }: Point) {}"#, + expect![[r#" + *test_4* + + ```rust + test + ``` + + ```rust + fn test_4(Point { x: x, y: y, }: Point) + ``` + "#]], + ); + + // Test case with a nested pattern + check( + r#"fn test_5$0(((a, b), c): ((i32, i32), i32)) {}"#, + expect![[r#" + *test_5* + + ```rust + test + ``` + + ```rust + fn test_5(((a, b), c): ((i32, i32), i32)) + ``` + "#]], + ); + + // Test case with an unused variable in the pattern + check( + r#"fn test_6$0((_, y): (i32, i64)) {}"#, + expect![[r#" + *test_6* + + ```rust + test + ``` + + ```rust + fn test_6((_, y): (i32, i64)) + ``` + "#]], + ); + + // Test case with a complex pattern involving both tuple and struct + check( + r#"struct Foo { a: i32, b: i32 } fn test_7$0((x, Foo { a, b }): (i32, Foo)) {}"#, + expect![[r#" + *test_7* + + ```rust + test + ``` + + ```rust + fn test_7((x, Foo { a: a, b: b, }): (i32, Foo)) + ``` + "#]], + ); +} From 5c97361622bb3c6410c3c36dda30bfbb1ebca5e8 Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 03:55:59 +0800 Subject: [PATCH 4/5] fix: add parenthesis for or-pattern --- crates/hir-def/src/body/pretty.rs | 2 ++ crates/ide/src/hover/tests.rs | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 3333956adb..64d0c16f52 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -599,12 +599,14 @@ impl Printer<'_> { w!(self, ")"); } Pat::Or(pats) => { + w!(self, "("); for (i, pat) in pats.iter().enumerate() { if i != 0 { w!(self, " | "); } self.print_pat(*pat); } + w!(self, ")"); } Pat::Record { path, args, ellipsis } => { match path { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index d1e51a2d28..1a97d99f05 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -8743,7 +8743,6 @@ fn foo() { ); } - #[test] fn test_hover_function_with_pat_param() { check( @@ -8856,4 +8855,20 @@ fn test_hover_function_with_pat_param() { ``` "#]], ); + + // Test case with Enum and Or pattern + check( + r#"enum MyEnum { A(i32), B(i32) } fn test_8$0((MyEnum::A(x) | MyEnum::B(x)): MyEnum) {}"#, + expect![[r#" + *test_8* + + ```rust + test + ``` + + ```rust + fn test_8((MyEnum::A(x) | MyEnum::B(x)): MyEnum) + ``` + "#]], + ); } From 5db510b2588fc6952124d8335138aaaeed1f9966 Mon Sep 17 00:00:00 2001 From: roife Date: Mon, 9 Sep 2024 23:14:31 +0800 Subject: [PATCH 5/5] feat: use shorthand when pretty-print record pat --- crates/hir-def/src/body/pretty.rs | 33 +++++++++++++++++--- crates/ide/src/hover/tests.rs | 52 +++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 64d0c16f52..37167fcb81 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -616,12 +616,37 @@ impl Printer<'_> { w!(self, " {{"); let edition = self.edition; + let oneline = matches!(self.line_format, LineFormat::Oneline); self.indented(|p| { - for arg in args.iter() { - w!(p, "{}: ", arg.name.display(self.db.upcast(), edition)); - p.print_pat(arg.pat); - wln!(p, ","); + for (idx, arg) in args.iter().enumerate() { + let field_name = arg.name.display(self.db.upcast(), edition).to_string(); + + let mut same_name = false; + if let Pat::Bind { id, subpat: None } = &self.body[arg.pat] { + if let Binding { name, mode: BindingAnnotation::Unannotated, .. } = + &self.body.bindings[*id] + { + if name.as_str() == field_name { + same_name = true; + } + } + } + + w!(p, "{}", field_name); + + if !same_name { + w!(p, ": "); + p.print_pat(arg.pat); + } + + // Do not print the extra comma if the line format is oneline + if oneline && idx == args.len() - 1 { + w!(p, " "); + } else { + wln!(p, ","); + } } + if *ellipsis { wln!(p, ".."); } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 1a97d99f05..f2e5d24fcc 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -8803,7 +8803,7 @@ fn test_hover_function_with_pat_param() { ``` ```rust - fn test_4(Point { x: x, y: y, }: Point) + fn test_4(Point { x, y }: Point) ``` "#]], ); @@ -8851,7 +8851,7 @@ fn test_hover_function_with_pat_param() { ``` ```rust - fn test_7((x, Foo { a: a, b: b, }): (i32, Foo)) + fn test_7((x, Foo { a, b }): (i32, Foo)) ``` "#]], ); @@ -8871,4 +8871,52 @@ fn test_hover_function_with_pat_param() { ``` "#]], ); + + // Test case with a pattern as a function parameter + check( + r#"struct Foo { a: i32, b: i32 } fn test_9$0(Foo { a, b }: Foo) {}"#, + expect![[r#" + *test_9* + + ```rust + test + ``` + + ```rust + fn test_9(Foo { a, b }: Foo) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter with a different name + check( + r#"struct Foo { a: i32, b: i32 } fn test_10$0(Foo { a, b: b1 }: Foo) {}"#, + expect![[r#" + *test_10* + + ```rust + test + ``` + + ```rust + fn test_10(Foo { a, b: b1 }: Foo) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter with annotations + check( + r#"struct Foo { a: i32, b: i32 } fn test_10$0(Foo { a, b: mut b }: Foo) {}"#, + expect![[r#" + *test_10* + + ```rust + test + ``` + + ```rust + fn test_10(Foo { a, b: mut b }: Foo) + ``` + "#]], + ); }