Repository: rust-lang/rust-analyzer
Pull request #16906 (2024-03-21 10:00:51)

feat: limit struct hover display nums
follow up https://github.com/rust-lang/rust-analyzer/pull/15847, https://github.com/rust-lang/rust-analyzer/pull/15938


Patch:
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index c5d44c11f2..23c6b078b9 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -186,18 +186,29 @@ impl HirDisplay for Struct {
             }
             StructKind::Record => {
                 let has_where_clause = write_where_clause(def_id, f)?;
-                let fields = self.fields(f.db);
-                f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
-                if fields.is_empty() {
-                    f.write_str("{}")?;
-                } else {
-                    f.write_str("{\n")?;
-                    for field in self.fields(f.db) {
-                        f.write_str("    ")?;
-                        field.hir_fmt(f)?;
-                        f.write_str(",\n")?;
+                if let Some(limit) = f.entity_limit {
+                    let fields = self.fields(f.db);
+                    let count = fields.len().min(limit);
+                    f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
+                    if count == 0 {
+                        if fields.is_empty() {
+                            f.write_str("{}")?;
+                        } else {
+                            f.write_str("{ /* … */ }")?;
+                        }
+                    } else {
+                        f.write_str(" {\n")?;
+                        for field in &fields[..count] {
+                            f.write_str("    ")?;
+                            field.hir_fmt(f)?;
+                            f.write_str(",\n")?;
+                        }
+
+                        if fields.len() > count {
+                            f.write_str("    /* … */\n")?;
+                        }
+                        f.write_str("}")?;
                     }
-                    f.write_str("}")?;
                 }
             }
             StructKind::Unit => _ = write_where_clause(def_id, f)?,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 8f4c629b58..822751c0e4 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -33,6 +33,7 @@ pub struct HoverConfig {
     pub keywords: bool,
     pub format: HoverDocFormat,
     pub max_trait_assoc_items_count: Option<usize>,
+    pub max_struct_field_count: Option<usize>,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 63777d4910..abedbff831 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -410,6 +410,9 @@ pub(super) fn definition(
         Definition::Trait(trait_) => {
             trait_.display_limited(db, config.max_trait_assoc_items_count).to_string()
         }
+        Definition::Adt(Adt::Struct(struct_)) => {
+            struct_.display_limited(db, config.max_struct_field_count).to_string()
+        }
         _ => def.label(db),
     };
     let docs = def.docs(db, famous_defs);
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index fe063081f7..3fef16df25 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -167,6 +167,7 @@ impl StaticIndex<'_> {
             keywords: true,
             format: crate::HoverDocFormat::Markdown,
             max_trait_assoc_items_count: None,
+            max_struct_field_count: None,
         };
         let tokens = tokens.filter(|token| {
             matches!(
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index e6b60f6906..a5545e7984 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -375,6 +375,8 @@ config_data! {
         /// How to render the size information in a memory layout hover.
         hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = "\"both\"",
 
+        /// How many fields of a struct to display when hovering a struct.
+        hover_show_structFields: Option<usize> = "null",
         /// How many associated items of a trait to display when hovering a trait.
         hover_show_traitAssocItems: Option<usize> = "null",
 
@@ -1690,6 +1692,7 @@ impl Config {
             },
             keywords: self.data.hover_documentation_keywords_enable,
             max_trait_assoc_items_count: self.data.hover_show_traitAssocItems,
+            max_struct_field_count: self.data.hover_show_structFields,
         }
     }
 
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 5e782b7831..c4024f6d28 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -520,6 +520,11 @@ How to render the offset information in a memory layout hover.
 --
 How to render the size information in a memory layout hover.
 --
+[[rust-analyzer.hover.show.structFields]]rust-analyzer.hover.show.structFields (default: `null`)::
++
+--
+How many fields of a struct to display when hovering a struct.
+--
 [[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index c34b8e25de..c3ea1ceeb6 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1144,6 +1144,15 @@
                         }
                     ]
                 },
+                "rust-analyzer.hover.show.structFields": {
+                    "markdownDescription": "How many fields of a struct to display when hovering a struct.",
+                    "default": null,
+                    "type": [
+                        "null",
+                        "integer"
+                    ],
+                    "minimum": 0
+                },
                 "rust-analyzer.hover.show.traitAssocItems": {
                     "markdownDescription": "How many associated items of a trait to display when hovering a trait.",
                     "default": null,


Test patch:
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 466e5570b3..192f6c0272 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -18,6 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
     format: HoverDocFormat::Markdown,
     keywords: true,
     max_trait_assoc_items_count: None,
+    max_struct_field_count: None,
 };
 
 fn check_hover_no_result(ra_fixture: &str) {
@@ -49,6 +50,28 @@ fn check(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual)
 }
 
+#[track_caller]
+fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) {
+    let (analysis, position) = fixture::position(ra_fixture);
+    let hover = analysis
+        .hover(
+            &HoverConfig {
+                links_in_hover: true,
+                max_struct_field_count: Some(count),
+                ..HOVER_BASE_CONFIG
+            },
+            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
+        )
+        .unwrap()
+        .unwrap();
+
+    let content = analysis.db.file_text(position.file_id);
+    let hovered_element = &content[hover.range];
+
+    let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup);
+    expect.assert_eq(&actual)
+}
+
 #[track_caller]
 fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
@@ -853,9 +876,7 @@ struct Foo$0 { field: u32 }
 
             ```rust
             // size = 4, align = 4
-            struct Foo {
-                field: u32,
-            }
+            struct Foo
             ```
         "#]],
     );
@@ -875,8 +896,74 @@ struct Foo$0 where u32: Copy { field: u32 }
             struct Foo
             where
                 u32: Copy,
-            {
-                field: u32,
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_record_struct_limit() {
+    check_hover_struct_limit(
+        3,
+        r#"
+    struct Foo$0 { a: u32, b: i32, c: i32 }
+    "#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 12 (0xC), align = 4
+            struct Foo  {
+                a: u32,
+                b: i32,
+                c: i32,
+            }
+            ```
+        "#]],
+    );
+    check_hover_struct_limit(
+        3,
+        r#"
+    struct Foo$0 { a: u32 }
+    "#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 4, align = 4
+            struct Foo  {
+                a: u32,
+            }
+            ```
+        "#]],
+    );
+    check_hover_struct_limit(
+        3,
+        r#"
+    struct Foo$0 { a: u32, b: i32, c: i32, d: u32 }
+    "#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 16 (0x10), align = 4
+            struct Foo  {
+                a: u32,
+                b: i32,
+                c: i32,
+                /* … */
             }
             ```
         "#]],
@@ -1344,9 +1431,7 @@ impl Thing {
                 ```
 
                 ```rust
-                struct Thing {
-                    x: u32,
-                }
+                struct Thing
                 ```
             "#]],
     );
@@ -1365,9 +1450,7 @@ impl Thing {
                 ```
 
                 ```rust
-                struct Thing {
-                    x: u32,
-                }
+                struct Thing
                 ```
             "#]],
     );
@@ -2599,7 +2682,7 @@ fn main() { let s$0t = S{ f1:0 }; }
                                     focus_range: 7..8,
                                     name: "S",
                                     kind: Struct,
-                                    description: "struct S {\n    f1: u32,\n}",
+                                    description: "struct S",
                                 },
                             },
                         ],
@@ -2645,7 +2728,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
                                 focus_range: 24..25,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S<T> {\n    f1: T,\n}",
+                                description: "struct S<T>",
                             },
                         },
                     ],
@@ -2704,7 +2787,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
                                 focus_range: 24..25,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S<T> {\n    f1: T,\n}",
+                                description: "struct S<T>",
                             },
                         },
                     ],
@@ -2957,7 +3040,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 39..41,
                                 name: "S1",
                                 kind: Struct,
-                                description: "struct S1 {}",
+                                description: "struct S1",
                             },
                         },
                         HoverGotoTypeData {
@@ -2970,7 +3053,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 52..54,
                                 name: "S2",
                                 kind: Struct,
-                                description: "struct S2 {}",
+                                description: "struct S2",
                             },
                         },
                     ],
@@ -3061,7 +3144,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
                                 focus_range: 36..37,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {}",
+                                description: "struct S",
                             },
                         },
                     ],
@@ -3161,7 +3244,7 @@ fn foo(ar$0g: &impl Foo<S>) {}
                                 focus_range: 23..24,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {}",
+                                description: "struct S",
                             },
                         },
                     ],
@@ -3198,7 +3281,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 49..50,
                                 name: "B",
                                 kind: Struct,
-                                description: "struct B<T> {}",
+                                description: "struct B<T>",
                             },
                         },
                         HoverGotoTypeData {
@@ -3287,7 +3370,7 @@ fn foo(ar$0g: &dyn Foo<S>) {}
                                 focus_range: 23..24,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {}",
+                                description: "struct S",
                             },
                         },
                     ],
@@ -3322,7 +3405,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
                                 focus_range: 50..51,
                                 name: "B",
                                 kind: Struct,
-                                description: "struct B<T> {}",
+                                description: "struct B<T>",
                             },
                         },
                         HoverGotoTypeData {
@@ -3361,7 +3444,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
                                 focus_range: 65..66,
                                 name: "S",
                                 kind: Struct,
-                                description: "struct S {}",
+                                description: "struct S",
                             },
                         },
                     ],