Unnamed repository; edit this file 'description' to name the repository.
feat: Hide private fields on hover depending on context
Lukas Wirth 4 weeks ago
parent ee5de0e · commit 78d1b34
-rw-r--r--crates/hir-ty/src/display.rs19
-rw-r--r--crates/hir/src/display.rs44
-rw-r--r--crates/ide/src/hover.rs26
-rw-r--r--crates/ide/src/hover/render.rs30
-rw-r--r--crates/ide/src/hover/tests.rs152
5 files changed, 249 insertions, 22 deletions
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 5d0a010a7f..d8e7d6379d 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -130,6 +130,7 @@ pub struct HirFormatter<'a, 'db> {
pub entity_limit: Option<usize>,
/// When rendering functions, whether to show the constraint from the container
show_container_bounds: bool,
+ render_private_fields: bool,
omit_verbose_types: bool,
closure_style: ClosureStyle,
display_lifetimes: DisplayLifetime,
@@ -263,6 +264,7 @@ pub trait HirDisplay<'db> {
display_kind,
closure_style,
show_container_bounds,
+ render_private_fields: true,
display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
@@ -287,6 +289,7 @@ pub trait HirDisplay<'db> {
display_target,
display_kind: DisplayKind::Diagnostics,
show_container_bounds: false,
+ render_private_fields: true,
display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
@@ -312,6 +315,7 @@ pub trait HirDisplay<'db> {
display_target,
display_kind: DisplayKind::Diagnostics,
show_container_bounds: false,
+ render_private_fields: true,
display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
@@ -337,6 +341,7 @@ pub trait HirDisplay<'db> {
display_target,
display_kind: DisplayKind::Diagnostics,
show_container_bounds: false,
+ render_private_fields: true,
display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
@@ -364,6 +369,7 @@ pub trait HirDisplay<'db> {
display_target: DisplayTarget::from_crate(db, module_id.krate(db)),
display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque },
show_container_bounds: false,
+ render_private_fields: true,
display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
currently_formatting_bounds: Default::default(),
trait_bounds_need_parens: false,
@@ -394,6 +400,7 @@ pub trait HirDisplay<'db> {
display_target,
display_kind: DisplayKind::Test,
show_container_bounds: false,
+ render_private_fields: true,
display_lifetimes: DisplayLifetime::Always,
}
}
@@ -419,6 +426,7 @@ pub trait HirDisplay<'db> {
display_target,
display_kind: DisplayKind::Diagnostics,
show_container_bounds,
+ render_private_fields: true,
display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
@@ -495,6 +503,10 @@ impl<'db> HirFormatter<'_, 'db> {
pub fn show_container_bounds(&self) -> bool {
self.show_container_bounds
}
+
+ pub fn render_private_fields(&self) -> bool {
+ self.render_private_fields
+ }
}
#[derive(Debug, Clone, Copy)]
@@ -566,6 +578,7 @@ pub struct HirDisplayWrapper<'a, 'db, T> {
display_kind: DisplayKind,
display_target: DisplayTarget,
show_container_bounds: bool,
+ render_private_fields: bool,
display_lifetimes: DisplayLifetime,
}
@@ -601,6 +614,7 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> {
display_target: self.display_target,
closure_style: self.closure_style,
show_container_bounds: self.show_container_bounds,
+ render_private_fields: self.render_private_fields,
display_lifetimes: self.display_lifetimes,
currently_formatting_bounds: Default::default(),
trait_bounds_need_parens: false,
@@ -616,6 +630,11 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> {
self.display_lifetimes = l;
self
}
+
+ pub fn with_private_fields(mut self, render: bool) -> Self {
+ self.render_private_fields = render;
+ self
+ }
}
impl<'db, T> fmt::Display for HirDisplayWrapper<'_, 'db, T>
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 0095401ff5..06fa91804d 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -14,6 +14,7 @@ use hir_def::{
TraitSignature, TypeAliasSignature,
},
type_ref::{TypeBound, TypeRef, TypeRefId},
+ visibility::Visibility,
};
use hir_expand::name::Name;
use hir_ty::{
@@ -363,20 +364,22 @@ impl<'db> HirDisplay<'db> for Struct {
let def_id = GenericDefId::AdtId(AdtId::StructId(self.id));
write_generic_params(def_id, f)?;
- let variant_data = self.variant_fields(f.db);
match self.kind(f.db) {
StructKind::Tuple => {
f.write_char('(')?;
- let mut it = variant_data.fields().iter().peekable();
+ let (fields, hidden_fields) = visible_fields(self.fields(f.db), f);
+ let mut it = fields.iter().peekable();
- while let Some((id, _)) = it.next() {
- let field = Field { parent: (*self).into(), id };
+ while let Some(field) = it.next() {
write_visibility(module_id, field.visibility(f.db), f)?;
field.ty(f.db).hir_fmt(f)?;
- if it.peek().is_some() {
+ if it.peek().is_some() || hidden_fields {
f.write_str(", ")?;
}
}
+ if hidden_fields {
+ f.write_str("/* … */")?;
+ }
f.write_char(')')?;
write_where_clause(def_id, f)?;
@@ -384,7 +387,8 @@ impl<'db> HirDisplay<'db> for Struct {
StructKind::Record => {
let has_where_clause = write_where_clause(def_id, f)?;
if let Some(limit) = f.entity_limit {
- write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
+ let (fields, hidden_fields) = visible_fields(self.fields(f.db), f);
+ write_fields(&fields, hidden_fields, has_where_clause, limit, false, f)?;
}
}
StructKind::Unit => _ = write_where_clause(def_id, f)?,
@@ -421,14 +425,33 @@ impl<'db> HirDisplay<'db> for Union {
let has_where_clause = write_where_clause(def_id, f)?;
if let Some(limit) = f.entity_limit {
- write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
+ let (fields, hidden_fields) = visible_fields(self.fields(f.db), f);
+ write_fields(&fields, hidden_fields, has_where_clause, limit, false, f)?;
}
Ok(())
}
}
+fn visible_fields<'db>(fields: Vec<Field>, f: &mut HirFormatter<'_, 'db>) -> (Vec<Field>, bool) {
+ if f.render_private_fields() {
+ return (fields, false);
+ }
+
+ let mut hidden_fields = false;
+ let fields = fields
+ .into_iter()
+ .filter(|field| {
+ let is_public = field.visibility(f.db) == Visibility::Public;
+ hidden_fields |= !is_public;
+ is_public
+ })
+ .collect();
+ (fields, hidden_fields)
+}
+
fn write_fields<'db>(
fields: &[Field],
+ hidden_fields: bool,
has_where_clause: bool,
limit: usize,
in_line: bool,
@@ -438,7 +461,7 @@ fn write_fields<'db>(
let (indent, separator) = if in_line { ("", ' ') } else { (" ", '\n') };
f.write_char(if !has_where_clause { ' ' } else { separator })?;
if count == 0 {
- f.write_str(if fields.is_empty() { "{}" } else { "{ /* … */ }" })?;
+ f.write_str(if fields.is_empty() && !hidden_fields { "{}" } else { "{ /* … */ }" })?;
} else {
f.write_char('{')?;
@@ -450,7 +473,7 @@ fn write_fields<'db>(
write!(f, ",{separator}")?;
}
- if fields.len() > count {
+ if fields.len() > count || hidden_fields {
write!(f, "{indent}/* … */{separator}")?;
}
}
@@ -542,7 +565,8 @@ impl<'db> HirDisplay<'db> for EnumVariant {
}
FieldsShape::Record => {
if let Some(limit) = f.entity_limit {
- write_fields(&self.fields(f.db), false, limit, true, f)?;
+ let (fields, hidden_fields) = visible_fields(self.fields(f.db), f);
+ write_fields(&fields, hidden_fields, false, limit, true, f)?;
}
}
}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index c3a8e0362f..28e1dc72bf 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -481,6 +481,10 @@ pub(crate) fn hover_for_definition(
};
let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default();
let subst_types = subst.map(|subst| subst.types(db));
+ let render_private_fields = sema.scope(scope_node).is_some_and(|scope| {
+ def.krate(db)
+ .is_some_and(|def_crate| should_render_private_fields(db, def_crate, scope.krate()))
+ });
let (markup, range_map) = render::definition(
sema.db,
@@ -489,6 +493,7 @@ pub(crate) fn hover_for_definition(
&notable_traits,
macro_arm,
render_extras,
+ render_private_fields,
subst_types.as_ref(),
config,
edition,
@@ -508,6 +513,27 @@ pub(crate) fn hover_for_definition(
}
}
+/// | Hover location | Definition location | Render private fields? |
+/// |---|---:|---:|
+/// | Workspace crate | Workspace crate | Yes |
+/// | Workspace crate | External/library crate | No |
+/// | External/library crate | Same external/library crate | Yes |
+/// | External/library crate | Different external/library crate | No |
+/// | Anywhere | Same crate as definition | Yes |
+fn should_render_private_fields(
+ db: &RootDatabase,
+ def_crate: hir::Crate,
+ hover_crate: hir::Crate,
+) -> bool {
+ let is_workspace_crate = |db: &RootDatabase, krate: hir::Crate| {
+ let origin = krate.origin(db);
+ !origin.is_lib() && !origin.is_lang()
+ };
+
+ def_crate == hover_crate
+ || is_workspace_crate(db, def_crate) && is_workspace_crate(db, hover_crate)
+}
+
fn notable_traits<'db>(
db: &'db RootDatabase,
ty: &hir::Type<'db>,
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index fe94e169ed..f757989784 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -456,6 +456,7 @@ pub(super) fn definition(
notable_traits: &[(Trait, Vec<(Option<Type<'_>>, Name)>)],
macro_arm: Option<u32>,
render_extras: bool,
+ render_private_fields: bool,
subst_types: Option<&Vec<(Symbol, Type<'_>)>>,
config: &HoverConfig<'_>,
edition: Edition,
@@ -465,22 +466,27 @@ pub(super) fn definition(
let label = match def {
Definition::Trait(trait_) => trait_
.display_limited(db, config.max_trait_assoc_items_count, display_target)
+ .with_private_fields(render_private_fields)
+ .to_string(),
+ Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => adt
+ .display_limited(db, config.max_fields_count, display_target)
+ .with_private_fields(render_private_fields)
+ .to_string(),
+ Definition::EnumVariant(variant) => variant
+ .display_limited(db, config.max_fields_count, display_target)
+ .with_private_fields(render_private_fields)
+ .to_string(),
+ Definition::Adt(adt @ Adt::Enum(_)) => adt
+ .display_limited(db, config.max_enum_variants_count, display_target)
+ .with_private_fields(render_private_fields)
.to_string(),
- Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => {
- adt.display_limited(db, config.max_fields_count, display_target).to_string()
- }
- Definition::EnumVariant(variant) => {
- variant.display_limited(db, config.max_fields_count, display_target).to_string()
- }
- Definition::Adt(adt @ Adt::Enum(_)) => {
- adt.display_limited(db, config.max_enum_variants_count, display_target).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, display_target).to_string()
- }
+ Some(adt) => adt
+ .display_limited(db, config.max_fields_count, display_target)
+ .with_private_fields(render_private_fields)
+ .to_string(),
None => self_ty.display(db, display_target).to_string(),
}
}
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 2bd79d79df..1ccaedcecd 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -995,6 +995,158 @@ struct Foo$0 where u32: Copy { field: u32 }
}
#[test]
+fn hover_filters_private_struct_fields_by_hover_context() {
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+ let _: dep::Foo$0;
+}
+
+//- /dep.rs crate:dep library
+pub struct Foo {
+ pub visible: i32,
+ hidden: i32,
+ pub(crate) crate_visible: i32,
+}
+"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ dep
+ ```
+
+ ```rust
+ pub struct Foo {
+ pub visible: i32,
+ /* … */
+ }
+ ```
+ "#]],
+ );
+
+ check(
+ r#"
+//- /dep.rs crate:dep library
+pub struct Foo {
+ pub visible: i32,
+ hidden: i32,
+ pub(crate) crate_visible: i32,
+}
+
+fn main() {
+ let _: Foo$0;
+}
+"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ dep
+ ```
+
+ ```rust
+ pub struct Foo {
+ pub visible: i32,
+ hidden: i32,
+ pub(crate) crate_visible: i32,
+ }
+ ```
+ "#]],
+ );
+
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+ let _: dep::Foo$0;
+}
+
+//- /dep.rs crate:dep
+pub struct Foo {
+ pub visible: i32,
+ hidden: i32,
+ pub(crate) crate_visible: i32,
+}
+"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ dep
+ ```
+
+ ```rust
+ pub struct Foo {
+ pub visible: i32,
+ hidden: i32,
+ pub(crate) crate_visible: i32,
+ }
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_filters_private_tuple_struct_fields_by_hover_context() {
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+ let _: dep::Foo$0;
+}
+
+//- /dep.rs crate:dep library
+pub struct Foo(pub i32, i32, pub(crate) i32);
+"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ dep
+ ```
+
+ ```rust
+ pub struct Foo(pub i32, /* … */)
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_filters_private_union_fields_by_hover_context() {
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+ let _: dep::U$0;
+}
+
+//- /dep.rs crate:dep library
+pub union U {
+ pub visible: i32,
+ hidden: u32,
+}
+"#,
+ expect![[r#"
+ *U*
+
+ ```rust
+ dep
+ ```
+
+ ```rust
+ pub union U {
+ pub visible: i32,
+ /* … */
+ }
+ ```
+ "#]],
+ );
+}
+
+#[test]
fn hover_record_struct_limit() {
check_hover_fields_limit(
3,