diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index fe51ec3f8210..328dea87ac11 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -63,6 +63,7 @@ pub struct HirFormatter<'a> { buf: String, curr_size: usize, pub(crate) max_size: Option, + pub limited_size: Option, omit_verbose_types: bool, closure_style: ClosureStyle, display_target: DisplayTarget, @@ -86,6 +87,7 @@ pub trait HirDisplay { &'a self, db: &'a dyn HirDatabase, max_size: Option, + limited_size: Option, omit_verbose_types: bool, display_target: DisplayTarget, closure_style: ClosureStyle, @@ -101,6 +103,7 @@ pub trait HirDisplay { db, t: self, max_size, + limited_size, omit_verbose_types, display_target, closure_style, @@ -117,6 +120,7 @@ pub trait HirDisplay { db, t: self, max_size: None, + limited_size: None, omit_verbose_types: false, closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::Diagnostics, @@ -137,6 +141,28 @@ pub trait HirDisplay { db, t: self, max_size, + limited_size: None, + omit_verbose_types: true, + closure_style: ClosureStyle::ImplFn, + display_target: DisplayTarget::Diagnostics, + } + } + + /// Returns a `Display`able type that is human-readable and tries to limit the item inside this type. + /// Use this for showing types which may contain two many item when user hover on, like `trait`, `struct`, `enum` + fn display_limited<'a>( + &'a self, + db: &'a dyn HirDatabase, + limited_size: Option, + ) -> HirDisplayWrapper<'a, Self> + where + Self: Sized, + { + HirDisplayWrapper { + db, + t: self, + max_size: None, + limited_size, omit_verbose_types: true, closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::Diagnostics, @@ -158,6 +184,7 @@ pub trait HirDisplay { buf: String::with_capacity(20), curr_size: 0, max_size: None, + limited_size: None, omit_verbose_types: false, closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::SourceCode { module_id, allow_opaque }, @@ -178,6 +205,7 @@ pub trait HirDisplay { db, t: self, max_size: None, + limited_size: None, omit_verbose_types: false, closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::Test, @@ -295,6 +323,7 @@ pub struct HirDisplayWrapper<'a, T> { db: &'a dyn HirDatabase, t: &'a T, max_size: Option, + limited_size: Option, omit_verbose_types: bool, closure_style: ClosureStyle, display_target: DisplayTarget, @@ -323,6 +352,7 @@ impl HirDisplayWrapper<'_, T> { buf: String::with_capacity(20), curr_size: 0, max_size: self.max_size, + limited_size: self.limited_size, omit_verbose_types: self.omit_verbose_types, display_target: self.display_target, closure_style: self.closure_style, diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 30f402a79f3d..20ba72d998c3 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -17,10 +17,10 @@ use hir_ty::{ }; use crate::{ - Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field, - Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, SelfParam, - Static, Struct, Trait, TraitAlias, TupleField, TyBuilder, Type, TypeAlias, TypeOrConstParam, - TypeParam, Union, Variant, + Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, + Field, Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, + SelfParam, Static, Struct, Trait, TraitAlias, TupleField, TyBuilder, Type, TypeAlias, + TypeOrConstParam, TypeParam, Union, Variant, }; impl HirDisplay for Function { @@ -595,6 +595,36 @@ impl HirDisplay for Trait { let def_id = GenericDefId::TraitId(self.id); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; + + let assoc_items = self.items(f.db); + let assoc_items_size = assoc_items.len(); + let limited_size = f.limited_size.unwrap_or(assoc_items_size); + if assoc_items.is_empty() { + f.write_str(" {}")?; + } else { + f.write_str(" {\n")?; + for (index, item) in assoc_items.iter().enumerate() { + f.write_str(" ")?; + match item { + AssocItem::Function(func) => { + func.hir_fmt(f)?; + } + AssocItem::Const(cst) => { + cst.hir_fmt(f)?; + } + AssocItem::TypeAlias(type_alias) => { + type_alias.hir_fmt(f)?; + } + }; + f.write_str(",\n")?; + if index + 1 == limited_size && index + 1 != assoc_items_size { + f.write_str(" ...\n")?; + break; + } + } + f.write_str("}")?; + } + Ok(()) } } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 4a7350feb385..b6877c5e8ce9 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -32,6 +32,7 @@ pub struct HoverConfig { pub documentation: bool, pub keywords: bool, pub format: HoverDocFormat, + pub trait_assoc_items_size: Option, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 563e78253a8a..f528bd512cce 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -406,7 +406,12 @@ pub(super) fn definition( config: &HoverConfig, ) -> Markup { let mod_path = definition_mod_path(db, &def); - let label = def.label(db); + let label = match def { + Definition::Trait(trait_) => { + trait_.display_limited(db, config.trait_assoc_items_size).to_string() + } + _ => def.label(db), + }; let docs = def.docs(db, famous_defs); let value = (|| match def { Definition::Variant(it) => { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index ead4f91595f0..70f4068478d8 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -17,6 +17,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { documentation: true, format: HoverDocFormat::Markdown, keywords: true, + trait_assoc_items_size: None, }; fn check_hover_no_result(ra_fixture: &str) { @@ -411,7 +412,7 @@ fn main() { name: "FnOnce", kind: Trait, container_name: "function", - description: "pub trait FnOnce\nwhere\n Args: Tuple,", + description: "pub trait FnOnce\nwhere\n Args: Tuple, {\n pub type Output,\n pub extern \"rust-call\" fn call_once(self, args: Args) -> Self::Output,\n}", }, }, ], @@ -2685,7 +2686,7 @@ fn main() { let s$0t = foo(); } focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, ], @@ -2719,7 +2720,7 @@ fn main() { let s$0t = foo(); } focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, HoverGotoTypeData { @@ -2766,7 +2767,7 @@ fn main() { let s$0t = foo(); } focus_range: 19..22, name: "Bar", kind: Trait, - description: "trait Bar", + description: "trait Bar {}", }, }, HoverGotoTypeData { @@ -2779,7 +2780,7 @@ fn main() { let s$0t = foo(); } focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, ], @@ -2816,7 +2817,7 @@ fn main() { let s$0t = foo(); } focus_range: 22..25, name: "Bar", kind: Trait, - description: "trait Bar", + description: "trait Bar {}", }, }, HoverGotoTypeData { @@ -2829,7 +2830,7 @@ fn main() { let s$0t = foo(); } focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, HoverGotoTypeData { @@ -2886,7 +2887,7 @@ fn foo(ar$0g: &impl Foo) {} focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, ], @@ -2920,7 +2921,7 @@ fn foo(ar$0g: &impl Foo + Bar) {} focus_range: 19..22, name: "Bar", kind: Trait, - description: "trait Bar", + description: "trait Bar {}", }, }, HoverGotoTypeData { @@ -2933,7 +2934,7 @@ fn foo(ar$0g: &impl Foo + Bar) {} focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, HoverGotoTypeData { @@ -2988,7 +2989,7 @@ pub mod future { name: "Future", kind: Trait, container_name: "future", - description: "pub trait Future", + description: "pub trait Future {}", }, }, HoverGotoTypeData { @@ -3033,7 +3034,7 @@ fn foo(ar$0g: &impl Foo) {} focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, HoverGotoTypeData { @@ -3096,7 +3097,7 @@ fn main() { let s$0t = foo(); } focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, ], @@ -3127,7 +3128,7 @@ fn foo(ar$0g: &dyn Foo) {} focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, ], @@ -3159,7 +3160,7 @@ fn foo(ar$0g: &dyn Foo) {} focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, HoverGotoTypeData { @@ -3220,7 +3221,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 28..36, name: "DynTrait", kind: Trait, - description: "trait DynTrait", + description: "trait DynTrait {}", }, }, HoverGotoTypeData { @@ -3233,7 +3234,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 6..15, name: "ImplTrait", kind: Trait, - description: "trait ImplTrait", + description: "trait ImplTrait {}", }, }, HoverGotoTypeData { @@ -3288,7 +3289,7 @@ fn main() { let s$0t = test().get(); } focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {\n type Item,\n fn get(self) -> Self::Item,\n}", }, }, ], @@ -3353,7 +3354,7 @@ fn foo(t: T$0){} focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo {}", }, }, ], @@ -6252,6 +6253,99 @@ impl T for () { ); } +#[test] +fn hover_trait_show_assoc_items() { + check( + r#" +trait T {} +impl T$0 for () {} +"#, + expect![[r#" + *T* + + ```rust + test + ``` + + ```rust + trait T {} + ``` + "#]], + ); + + check( + r#" +trait T { + fn func() {} +} +impl T$0 for () {} +"#, + expect![[r#" + *T* + + ```rust + test + ``` + + ```rust + trait T { + fn func(), + } + ``` + "#]], + ); + + check( + r#" +trait T { + fn func() {} + const FLAG: i32 = 34; +} +impl T$0 for () {} +"#, + expect![[r#" + *T* + + ```rust + test + ``` + + ```rust + trait T { + fn func(), + const FLAG: i32, + } + ``` + "#]], + ); + + check( + r#" +trait T { + fn func() {} + const FLAG: i32 = 34; + type Bar; +} +impl T$0 for () {} +"#, + expect![[r#" + *T* + + ```rust + test + ``` + + ```rust + trait T { + fn func(), + const FLAG: i32, + type Bar, + } + ``` + "#]], + ); +} + #[test] fn hover_ranged_macro_call() { check_hover_range( @@ -7363,7 +7457,7 @@ impl Iterator for S { name: "Future", kind: Trait, container_name: "future", - description: "pub trait Future", + description: "pub trait Future {\n pub type Output,\n pub fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll,\n}", }, }, HoverGotoTypeData { @@ -7377,7 +7471,7 @@ impl Iterator for S { name: "Iterator", kind: Trait, container_name: "iterator", - description: "pub trait Iterator", + description: "pub trait Iterator {\n pub type Item,\n pub fn next(&mut self) -> Option,\n pub fn nth(&mut self, n: usize) -> Option,\n pub fn by_ref(&mut self) -> &mut Self\nwhere\n Self: Sized,,\n}", }, }, HoverGotoTypeData { @@ -7390,7 +7484,7 @@ impl Iterator for S { focus_range: 49..56, name: "Notable", kind: Trait, - description: "trait Notable", + description: "trait Notable {}", }, }, HoverGotoTypeData { diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 2929a7522e59..0f19da0ac5fb 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -166,6 +166,7 @@ impl StaticIndex<'_> { documentation: true, keywords: true, format: crate::HoverDocFormat::Markdown, + trait_assoc_items_size: None, }; let tokens = tokens.filter(|token| { matches!( diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 16e1a2f54490..259ee3ea8c20 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -368,6 +368,9 @@ config_data! { /// How to render the size information in a memory layout hover. hover_memoryLayout_size: Option = "\"both\"", + /// How many associated items of a trait to display when hovering a trait. + hover_show_traitAssocItems: Option = "null", + /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. imports_granularity_enforce: bool = "false", /// How imports should be grouped into use statements. @@ -511,7 +514,6 @@ config_data! { /// Exclude tests from find-all-references. references_excludeTests: bool = "false", - /// Command to be executed instead of 'cargo' for runnables. runnables_command: Option = "null", /// Additional arguments to be passed to cargo for runnables such as @@ -1683,6 +1685,7 @@ impl Config { } }, keywords: self.data.hover_documentation_keywords_enable, + trait_assoc_items_size: self.data.hover_show_traitAssocItems, } } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index da7654b0f644..7b292fc78f76 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -505,6 +505,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.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`):: ++ +-- +How many associated items of a trait to display when hovering a trait. +-- [[rust-analyzer.imports.granularity.enforce]]rust-analyzer.imports.granularity.enforce (default: `false`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 3a1df5a2f901..719521a224ec 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1119,6 +1119,15 @@ } ] }, + "rust-analyzer.hover.show.traitAssocItems": { + "markdownDescription": "How many associated items of a trait to display when hovering a trait.", + "default": null, + "type": [ + "null", + "integer" + ], + "minimum": 0 + }, "rust-analyzer.imports.granularity.enforce": { "markdownDescription": "Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.", "default": false,