Auto merge of #17021 - roife:add-hover-limits-for-adts, r=Veykril

Support hovering limits for adts

Fix #17009

1. Currently, r-a supports limiting the number of struct fields displayed when hovering. This PR extends it to support enum variants and union fields. Since the display of these three (ADTs) is similar, this PR extends 'hover_show_structFields' to 'hover_show_adtFieldsOrVariants'.
2. This PR also resolved the problem that the layout of ADT was not restricted by display limitations when hovering on the Self type.
3. Additionally, this PR changes the default value of display limitations to `10` (instead of the original `null`), which helps users discover this feature.
This commit is contained in:
bors 2024-04-25 07:23:27 +00:00
commit d00de38599
8 changed files with 567 additions and 101 deletions

View File

@ -188,28 +188,7 @@ impl HirDisplay for Struct {
StructKind::Record => {
let has_where_clause = write_where_clause(def_id, f)?;
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("}")?;
}
display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
}
}
StructKind::Unit => _ = write_where_clause(def_id, f)?,
@ -226,18 +205,10 @@ impl HirDisplay for Enum {
write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id));
write_generic_params(def_id, f)?;
let has_where_clause = write_where_clause(def_id, f)?;
let variants = self.variants(f.db);
if !variants.is_empty() {
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
f.write_str("{\n")?;
for variant in variants {
f.write_str(" ")?;
variant.hir_fmt(f)?;
f.write_str(",\n")?;
}
f.write_str("}")?;
let has_where_clause = write_where_clause(def_id, f)?;
if let Some(limit) = f.entity_limit {
display_variants(&self.variants(f.db), has_where_clause, limit, f)?;
}
Ok(())
@ -251,24 +222,104 @@ impl HirDisplay for Union {
write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id));
write_generic_params(def_id, f)?;
let has_where_clause = write_where_clause(def_id, f)?;
let fields = self.fields(f.db);
if !fields.is_empty() {
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
f.write_str("{\n")?;
for field in self.fields(f.db) {
f.write_str(" ")?;
field.hir_fmt(f)?;
f.write_str(",\n")?;
}
f.write_str("}")?;
if let Some(limit) = f.entity_limit {
display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
}
Ok(())
}
}
fn display_fields(
fields: &[Field],
has_where_clause: bool,
limit: usize,
in_line: bool,
f: &mut HirFormatter<'_>,
) -> Result<(), HirDisplayError> {
let count = fields.len().min(limit);
let (indent, separator) = if in_line { ("", ' ') } else { (" ", '\n') };
f.write_char(if !has_where_clause { ' ' } else { separator })?;
if count == 0 {
if fields.is_empty() {
f.write_str("{}")?;
} else {
f.write_str("{ /* … */ }")?;
}
} else {
f.write_char('{')?;
if !fields.is_empty() {
f.write_char(separator)?;
for field in &fields[..count] {
f.write_str(indent)?;
field.hir_fmt(f)?;
f.write_char(',')?;
f.write_char(separator)?;
}
if fields.len() > count {
f.write_str(indent)?;
f.write_str("/* … */")?;
f.write_char(separator)?;
}
}
f.write_str("}")?;
}
Ok(())
}
fn display_variants(
variants: &[Variant],
has_where_clause: bool,
limit: usize,
f: &mut HirFormatter<'_>,
) -> Result<(), HirDisplayError> {
let count = variants.len().min(limit);
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
if count == 0 {
if variants.is_empty() {
f.write_str("{}")?;
} else {
f.write_str("{ /* … */ }")?;
}
} else {
f.write_str("{\n")?;
for variant in &variants[..count] {
f.write_str(" ")?;
write!(f, "{}", variant.name(f.db).display(f.db.upcast()))?;
match variant.kind(f.db) {
StructKind::Tuple => {
if variant.fields(f.db).is_empty() {
f.write_str("()")?;
} else {
f.write_str("( /* … */ )")?;
}
}
StructKind::Record => {
if variant.fields(f.db).is_empty() {
f.write_str(" {}")?;
} else {
f.write_str(" { /* … */ }")?;
}
}
StructKind::Unit => {}
}
f.write_str(",\n")?;
}
if variants.len() > count {
f.write_str(" /* … */\n")?;
}
f.write_str("}")?;
}
Ok(())
}
impl HirDisplay for Field {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?;
@ -304,21 +355,10 @@ impl HirDisplay for Variant {
}
f.write_char(')')?;
}
VariantData::Record(fields) => {
f.write_str(" {")?;
let mut first = true;
for (_, field) in fields.iter() {
if first {
first = false;
f.write_char(' ')?;
} else {
f.write_str(", ")?;
}
// Enum variant fields must be pub.
write!(f, "{}: ", field.name.display(f.db.upcast()))?;
field.type_ref.hir_fmt(f)?;
VariantData::Record(_) => {
if let Some(limit) = f.entity_limit {
display_fields(&self.fields(f.db), false, limit, true, f)?;
}
f.write_str(" }")?;
}
}
Ok(())

View File

@ -33,7 +33,8 @@ pub struct HoverConfig {
pub keywords: bool,
pub format: HoverDocFormat,
pub max_trait_assoc_items_count: Option<usize>,
pub max_struct_field_count: Option<usize>,
pub max_fields_count: Option<usize>,
pub max_enum_variants_count: Option<usize>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]

View File

@ -411,8 +411,21 @@ 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()
Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => {
adt.display_limited(db, config.max_fields_count).to_string()
}
Definition::Variant(variant) => {
variant.display_limited(db, config.max_fields_count).to_string()
}
Definition::Adt(adt @ Adt::Enum(_)) => {
adt.display_limited(db, config.max_enum_variants_count).to_string()
}
Definition::SelfType(impl_def) => {
let self_ty = &impl_def.self_ty(db);
match self_ty.as_adt() {
Some(adt) => adt.display_limited(db, config.max_fields_count).to_string(),
None => self_ty.display(db).to_string(),
}
}
Definition::Macro(it) => {
let mut label = it.display(db).to_string();

View File

@ -18,7 +18,8 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
format: HoverDocFormat::Markdown,
keywords: true,
max_trait_assoc_items_count: None,
max_struct_field_count: None,
max_fields_count: Some(5),
max_enum_variants_count: Some(5),
};
fn check_hover_no_result(ra_fixture: &str) {
@ -51,13 +52,43 @@ fn check(ra_fixture: &str, expect: Expect) {
}
#[track_caller]
fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) {
fn check_hover_fields_limit(
fields_count: impl Into<Option<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),
max_fields_count: fields_count.into(),
..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_hover_enum_variants_limit(
variants_count: impl Into<Option<usize>>,
ra_fixture: &str,
expect: Expect,
) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
&HoverConfig {
links_in_hover: true,
max_enum_variants_count: variants_count.into(),
..HOVER_BASE_CONFIG
},
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
@ -876,7 +907,9 @@ struct Foo$0 { field: u32 }
```rust
// size = 4, align = 4
struct Foo
struct Foo {
field: u32,
}
```
"#]],
);
@ -896,6 +929,9 @@ struct Foo$0 where u32: Copy { field: u32 }
struct Foo
where
u32: Copy,
{
field: u32,
}
```
"#]],
);
@ -903,7 +939,7 @@ struct Foo$0 where u32: Copy { field: u32 }
#[test]
fn hover_record_struct_limit() {
check_hover_struct_limit(
check_hover_fields_limit(
3,
r#"
struct Foo$0 { a: u32, b: i32, c: i32 }
@ -917,7 +953,7 @@ fn hover_record_struct_limit() {
```rust
// size = 12 (0xC), align = 4
struct Foo {
struct Foo {
a: u32,
b: i32,
c: i32,
@ -925,7 +961,7 @@ fn hover_record_struct_limit() {
```
"#]],
);
check_hover_struct_limit(
check_hover_fields_limit(
3,
r#"
struct Foo$0 { a: u32 }
@ -939,13 +975,13 @@ fn hover_record_struct_limit() {
```rust
// size = 4, align = 4
struct Foo {
struct Foo {
a: u32,
}
```
"#]],
);
check_hover_struct_limit(
check_hover_fields_limit(
3,
r#"
struct Foo$0 { a: u32, b: i32, c: i32, d: u32 }
@ -959,7 +995,7 @@ fn hover_record_struct_limit() {
```rust
// size = 16 (0x10), align = 4
struct Foo {
struct Foo {
a: u32,
b: i32,
c: i32,
@ -968,6 +1004,338 @@ fn hover_record_struct_limit() {
```
"#]],
);
check_hover_fields_limit(
None,
r#"
struct Foo$0 { a: u32, b: i32, c: i32 }
"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 12 (0xC), align = 4
struct Foo
```
"#]],
);
check_hover_fields_limit(
0,
r#"
struct Foo$0 { a: u32, b: i32, c: i32 }
"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 12 (0xC), align = 4
struct Foo { /**/ }
```
"#]],
);
// No extra spaces within `{}` when there are no fields
check_hover_fields_limit(
5,
r#"
struct Foo$0 {}
"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 0, align = 1
struct Foo {}
```
"#]],
);
}
#[test]
fn hover_record_variant_limit() {
check_hover_fields_limit(
3,
r#"
enum Foo { A$0 { a: u32, b: i32, c: i32 } }
"#,
expect![[r#"
*A*
```rust
test::Foo
```
```rust
// size = 12 (0xC), align = 4
A { a: u32, b: i32, c: i32, }
```
"#]],
);
check_hover_fields_limit(
3,
r#"
enum Foo { A$0 { a: u32 } }
"#,
expect![[r#"
*A*
```rust
test::Foo
```
```rust
// size = 4, align = 4
A { a: u32, }
```
"#]],
);
check_hover_fields_limit(
3,
r#"
enum Foo { A$0 { a: u32, b: i32, c: i32, d: u32 } }
"#,
expect![[r#"
*A*
```rust
test::Foo
```
```rust
// size = 16 (0x10), align = 4
A { a: u32, b: i32, c: i32, /**/ }
```
"#]],
);
check_hover_fields_limit(
None,
r#"
enum Foo { A$0 { a: u32, b: i32, c: i32 } }
"#,
expect![[r#"
*A*
```rust
test::Foo
```
```rust
// size = 12 (0xC), align = 4
A
```
"#]],
);
check_hover_fields_limit(
0,
r#"
enum Foo { A$0 { a: u32, b: i32, c: i32 } }
"#,
expect![[r#"
*A*
```rust
test::Foo
```
```rust
// size = 12 (0xC), align = 4
A { /**/ }
```
"#]],
);
}
#[test]
fn hover_enum_limit() {
check_hover_enum_variants_limit(
5,
r#"enum Foo$0 { A, B }"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 1, align = 1, niches = 254
enum Foo {
A,
B,
}
```
"#]],
);
check_hover_enum_variants_limit(
1,
r#"enum Foo$0 { A, B }"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 1, align = 1, niches = 254
enum Foo {
A,
/**/
}
```
"#]],
);
check_hover_enum_variants_limit(
0,
r#"enum Foo$0 { A, B }"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 1, align = 1, niches = 254
enum Foo { /**/ }
```
"#]],
);
check_hover_enum_variants_limit(
None,
r#"enum Foo$0 { A, B }"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 1, align = 1, niches = 254
enum Foo
```
"#]],
);
check_hover_enum_variants_limit(
7,
r#"enum Enum$0 {
Variant {},
Variant2 { field: i32 },
Variant3 { field: i32, field2: i32 },
Variant4(),
Variant5(i32),
Variant6(i32, i32),
Variant7,
Variant8,
}"#,
expect![[r#"
*Enum*
```rust
test
```
```rust
// size = 12 (0xC), align = 4, niches = 4294967288
enum Enum {
Variant {},
Variant2 { /**/ },
Variant3 { /**/ },
Variant4(),
Variant5( /**/ ),
Variant6( /**/ ),
Variant7,
/**/
}
```
"#]],
);
}
#[test]
fn hover_union_limit() {
check_hover_fields_limit(
5,
r#"union Foo$0 { a: u32, b: i32 }"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 4, align = 4
union Foo {
a: u32,
b: i32,
}
```
"#]],
);
check_hover_fields_limit(
1,
r#"union Foo$0 { a: u32, b: i32 }"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 4, align = 4
union Foo {
a: u32,
/**/
}
```
"#]],
);
check_hover_fields_limit(
0,
r#"union Foo$0 { a: u32, b: i32 }"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 4, align = 4
union Foo { /**/ }
```
"#]],
);
check_hover_fields_limit(
None,
r#"union Foo$0 { a: u32, b: i32 }"#,
expect![[r#"
*Foo*
```rust
test
```
```rust
// size = 4, align = 4
union Foo
```
"#]],
);
}
#[test]
@ -1431,11 +1799,14 @@ impl Thing {
```
```rust
struct Thing
struct Thing {
x: u32,
}
```
"#]],
);
check(
check_hover_fields_limit(
None,
r#"
struct Thing { x: u32 }
impl Thing {
@ -1456,9 +1827,9 @@ impl Thing {
);
check(
r#"
enum Thing { A }
struct Thing { x: u32 }
impl Thing {
pub fn new() -> Self$0 { Thing::A }
fn new() -> Self$0 { Self { x: 0 } }
}
"#,
expect![[r#"
@ -1469,8 +1840,8 @@ impl Thing {
```
```rust
enum Thing {
A,
struct Thing {
x: u32,
}
```
"#]],
@ -1478,23 +1849,44 @@ impl Thing {
check(
r#"
enum Thing { A }
impl Thing {
pub fn new() -> Self$0 { Thing::A }
}
"#,
expect![[r#"
*Self*
```rust
test
```
```rust
enum Thing {
A,
}
```
"#]],
);
check(
r#"
enum Thing { A }
impl Thing {
pub fn thing(a: Self$0) {}
}
"#,
expect![[r#"
*Self*
*Self*
```rust
test
```
```rust
test
```
```rust
enum Thing {
A,
}
```
"#]],
```rust
enum Thing {
A,
}
```
"#]],
);
check(
r#"
@ -2382,8 +2774,8 @@ fn test_hover_layout_of_enum() {
```rust
// size = 16 (0x10), align = 8, niches = 254
enum Foo {
Variant1(u8, u16),
Variant2(i32, u8, i64),
Variant1( /**/ ),
Variant2( /**/ ),
}
```
"#]],
@ -6554,7 +6946,7 @@ enum Enum {
```rust
// size = 4, align = 4
RecordV { field: u32 }
RecordV { field: u32, }
```
"#]],
);
@ -7936,7 +8328,9 @@ struct Pedro$0<'a> {
```rust
// size = 16 (0x10), align = 8, niches = 1
struct Pedro<'a>
struct Pedro<'a> {
hola: &str,
}
```
"#]],
)

View File

@ -167,7 +167,8 @@ impl StaticIndex<'_> {
keywords: true,
format: crate::HoverDocFormat::Markdown,
max_trait_assoc_items_count: None,
max_struct_field_count: None,
max_fields_count: Some(5),
max_enum_variants_count: Some(5),
};
let tokens = tokens.filter(|token| {
matches!(

View File

@ -315,8 +315,10 @@ config_data! {
/// How to render the size information in a memory layout hover.
hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Both),
/// How many fields of a struct to display when hovering a struct.
hover_show_structFields: Option<usize> = None,
/// How many variants of an enum to display when hovering on. Show none if empty.
hover_show_enumVariants: Option<usize> = Some(5),
/// How many fields of a struct, variant or union to display when hovering on. Show none if empty.
hover_show_fields: Option<usize> = Some(5),
/// How many associated items of a trait to display when hovering a trait.
hover_show_traitAssocItems: Option<usize> = None,
@ -1126,7 +1128,8 @@ impl Config {
},
keywords: self.hover_documentation_keywords_enable().to_owned(),
max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(),
max_struct_field_count: self.hover_show_structFields().to_owned(),
max_fields_count: self.hover_show_fields().to_owned(),
max_enum_variants_count: self.hover_show_enumVariants().to_owned(),
}
}

View File

@ -529,10 +529,15 @@ 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`)::
[[rust-analyzer.hover.show.enumVariants]]rust-analyzer.hover.show.enumVariants (default: `5`)::
+
--
How many fields of a struct to display when hovering a struct.
How many variants of an enum to display when hovering on. Show none if empty.
--
[[rust-analyzer.hover.show.fields]]rust-analyzer.hover.show.fields (default: `5`)::
+
--
How many fields of a struct, variant or union to display when hovering on. Show none if empty.
--
[[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`)::
+

View File

@ -1145,9 +1145,18 @@
}
]
},
"rust-analyzer.hover.show.structFields": {
"markdownDescription": "How many fields of a struct to display when hovering a struct.",
"default": null,
"rust-analyzer.hover.show.enumVariants": {
"markdownDescription": "How many variants of an enum to display when hovering on. Show none if empty.",
"default": 5,
"type": [
"null",
"integer"
],
"minimum": 0
},
"rust-analyzer.hover.show.fields": {
"markdownDescription": "How many fields of a struct, variant or union to display when hovering on. Show none if empty.",
"default": 5,
"type": [
"null",
"integer"