Move required components doc to type doc (#16575)

# Objective

Make documentation of a component's required components more visible by
moving it to the type's docs

## Solution

Change `#[require]` from a derive macro helper to an attribute macro.

Disadvantages:
- this silences any unused code warnings on the component, as it is used
by the macro!
- need to import `require` if not using the ecs prelude (I have not
included this in the migration guilde as Rust tooling already suggests
the fix)

---

## Showcase
![Documentation of
Camera](https://github.com/user-attachments/assets/3329511b-747a-4c8d-a43e-57f7c9c71a3c)

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
This commit is contained in:
SpecificProtagonist 2024-12-03 20:45:20 +01:00 committed by GitHub
parent dd49dc71d2
commit d92fc1e456
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 68 additions and 35 deletions

View file

@ -11,7 +11,10 @@ use crate::{
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, Handle}; use bevy_asset::{load_internal_asset, Handle};
use bevy_ecs::{ use bevy_ecs::{
bundle::Bundle, component::Component, query::With, reflect::ReflectComponent, bundle::Bundle,
component::{require, Component},
query::With,
reflect::ReflectComponent,
schedule::IntoSystemConfigs, schedule::IntoSystemConfigs,
}; };
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};

View file

@ -10,7 +10,7 @@ use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, Handle}; use bevy_asset::{load_internal_asset, Handle};
use bevy_core::FrameCount; use bevy_core::FrameCount;
use bevy_ecs::{ use bevy_ecs::{
prelude::{Bundle, Component, Entity, ReflectComponent}, prelude::{require, Bundle, Component, Entity, ReflectComponent},
query::{QueryItem, With}, query::{QueryItem, With},
schedule::IntoSystemConfigs, schedule::IntoSystemConfigs,
system::{Commands, Query, Res, ResMut, Resource}, system::{Commands, Query, Res, ResMut, Resource},

View file

@ -1,4 +1,4 @@
use proc_macro::TokenStream; use proc_macro::{TokenStream, TokenTree};
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use std::collections::HashSet; use std::collections::HashSet;
@ -127,22 +127,9 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
let struct_name = &ast.ident; let struct_name = &ast.ident;
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
let required_component_docs = attrs.requires.map(|r| {
let paths = r
.iter()
.map(|r| format!("[`{}`]", r.path.to_token_stream()))
.collect::<Vec<_>>()
.join(", ");
let doc = format!("Required Components: {paths}. \n\n A component's Required Components are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order.");
quote! {
#[doc = #doc]
}
});
// This puts `register_required` before `register_recursive_requires` to ensure that the constructors of _all_ top // This puts `register_required` before `register_recursive_requires` to ensure that the constructors of _all_ top
// level components are initialized first, giving them precedence over recursively defined constructors for the same component type // level components are initialized first, giving them precedence over recursively defined constructors for the same component type
TokenStream::from(quote! { TokenStream::from(quote! {
#required_component_docs
impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause { impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #storage; const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #storage;
fn register_required_components( fn register_required_components(
@ -173,6 +160,28 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
}) })
} }
pub fn document_required_components(attr: TokenStream, item: TokenStream) -> TokenStream {
let paths = parse_macro_input!(attr with Punctuated::<Require, Comma>::parse_terminated)
.iter()
.map(|r| format!("[`{}`]", r.path.to_token_stream()))
.collect::<Vec<_>>()
.join(", ");
// Insert information about required components after any existing doc comments
let mut out = TokenStream::new();
let mut end_of_attributes_reached = false;
for tt in item {
if !end_of_attributes_reached & matches!(tt, TokenTree::Ident(_)) {
end_of_attributes_reached = true;
let doc: TokenStream = format!("#[doc = \"\n\n# Required Components\n{paths} \n\n A component's required components are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order.\"]").parse().unwrap();
out.extend(doc);
}
out.extend(Some(tt));
}
out
}
pub const COMPONENT: &str = "component"; pub const COMPONENT: &str = "component";
pub const STORAGE: &str = "storage"; pub const STORAGE: &str = "storage";
pub const REQUIRE: &str = "require"; pub const REQUIRE: &str = "require";

View file

@ -721,11 +721,19 @@ pub fn derive_resource(input: TokenStream) -> TokenStream {
component::derive_resource(input) component::derive_resource(input)
} }
#[proc_macro_derive(Component, attributes(component, require))] #[proc_macro_derive(Component, attributes(component))]
pub fn derive_component(input: TokenStream) -> TokenStream { pub fn derive_component(input: TokenStream) -> TokenStream {
component::derive_component(input) component::derive_component(input)
} }
/// Allows specifying a component's required components.
///
/// See `Component` docs for usage.
#[proc_macro_attribute]
pub fn require(attr: TokenStream, item: TokenStream) -> TokenStream {
component::document_required_components(attr, item)
}
#[proc_macro_derive(States)] #[proc_macro_derive(States)]
pub fn derive_states(input: TokenStream) -> TokenStream { pub fn derive_states(input: TokenStream) -> TokenStream {
states::derive_states(input) states::derive_states(input)

View file

@ -29,6 +29,8 @@ use core::{
}; };
use derive_more::derive::{Display, Error}; use derive_more::derive::{Display, Error};
pub use bevy_ecs_macros::require;
/// A data type that can be used to store data for an [entity]. /// A data type that can be used to store data for an [entity].
/// ///
/// `Component` is a [derivable trait]: this means that a data type can implement it by applying a `#[derive(Component)]` attribute to it. /// `Component` is a [derivable trait]: this means that a data type can implement it by applying a `#[derive(Component)]` attribute to it.

View file

@ -46,7 +46,7 @@ pub mod prelude {
pub use crate::{ pub use crate::{
bundle::Bundle, bundle::Bundle,
change_detection::{DetectChanges, DetectChangesMut, Mut, Ref}, change_detection::{DetectChanges, DetectChangesMut, Mut, Ref},
component::Component, component::{require, Component},
entity::{Entity, EntityMapper}, entity::{Entity, EntityMapper},
event::{Event, EventMutator, EventReader, EventWriter, Events}, event::{Event, EventMutator, EventReader, EventWriter, Events},
observer::{CloneEntityWithObserversExt, Observer, Trigger}, observer::{CloneEntityWithObserversExt, Observer, Trigger},
@ -82,11 +82,10 @@ pub mod prelude {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate as bevy_ecs; use crate as bevy_ecs;
use crate::component::{RequiredComponents, RequiredComponentsError};
use crate::{ use crate::{
bundle::Bundle, bundle::Bundle,
change_detection::Ref, change_detection::Ref,
component::{Component, ComponentId}, component::{require, Component, ComponentId, RequiredComponents, RequiredComponentsError},
entity::Entity, entity::Entity,
prelude::Or, prelude::Or,
query::{Added, Changed, FilteredAccess, QueryFilter, With, Without}, query::{Added, Changed, FilteredAccess, QueryFilter, With, Without},

View file

@ -2256,7 +2256,7 @@ fn observe<E: Event, B: Bundle, M>(
mod tests { mod tests {
use crate::{ use crate::{
self as bevy_ecs, self as bevy_ecs,
component::Component, component::{require, Component},
system::{Commands, Resource}, system::{Commands, Resource},
world::{CommandQueue, FromWorld, World}, world::{CommandQueue, FromWorld, World},
}; };

View file

@ -7,6 +7,7 @@ use bevy_ecs::{
component::Component, component::Component,
entity::Entity, entity::Entity,
event::{Event, EventReader, EventWriter}, event::{Event, EventReader, EventWriter},
prelude::require,
system::{Commands, Query}, system::{Commands, Query},
}; };
use bevy_math::Vec2; use bevy_math::Vec2;

View file

@ -5,7 +5,7 @@ use bevy_asset::{load_internal_asset, AssetId, Handle};
use bevy_core_pipeline::core_3d::Camera3d; use bevy_core_pipeline::core_3d::Camera3d;
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{ use bevy_ecs::{
component::Component, component::{require, Component},
entity::Entity, entity::Entity,
query::With, query::With,
reflect::ReflectComponent, reflect::ReflectComponent,

View file

@ -68,7 +68,7 @@ use bevy_core_pipeline::{
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{ use bevy_ecs::{
bundle::Bundle, bundle::Bundle,
component::Component, component::{require, Component},
entity::Entity, entity::Entity,
prelude::With, prelude::With,
query::Has, query::Has,

View file

@ -9,7 +9,7 @@ use bevy_core_pipeline::{
prepass::{DepthPrepass, NormalPrepass, ViewPrepassTextures}, prepass::{DepthPrepass, NormalPrepass, ViewPrepassTextures},
}; };
use bevy_ecs::{ use bevy_ecs::{
prelude::{Bundle, Component, Entity}, prelude::{require, Bundle, Component, Entity},
query::{Has, QueryItem, With}, query::{Has, QueryItem, With},
reflect::ReflectComponent, reflect::ReflectComponent,
schedule::IntoSystemConfigs, schedule::IntoSystemConfigs,

View file

@ -15,7 +15,7 @@ use bevy_core_pipeline::{
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{ use bevy_ecs::{
bundle::Bundle, bundle::Bundle,
component::Component, component::{require, Component},
entity::Entity, entity::Entity,
query::{Has, QueryItem, With}, query::{Has, QueryItem, With},
reflect::ReflectComponent, reflect::ReflectComponent,

View file

@ -39,7 +39,9 @@ use bevy_core_pipeline::core_3d::{
prepare_core_3d_depth_textures, prepare_core_3d_depth_textures,
}; };
use bevy_ecs::{ use bevy_ecs::{
bundle::Bundle, component::Component, reflect::ReflectComponent, bundle::Bundle,
component::{require, Component},
reflect::ReflectComponent,
schedule::IntoSystemConfigs as _, schedule::IntoSystemConfigs as _,
}; };
use bevy_image::Image; use bevy_image::Image;

View file

@ -22,7 +22,7 @@ use bevy_ecs::{
component::{Component, ComponentId}, component::{Component, ComponentId},
entity::Entity, entity::Entity,
event::EventReader, event::EventReader,
prelude::With, prelude::{require, With},
query::Has, query::Has,
reflect::ReflectComponent, reflect::ReflectComponent,
system::{Commands, Query, Res, ResMut, Resource}, system::{Commands, Query, Res, ResMut, Resource},

View file

@ -1,7 +1,7 @@
use crate::{mesh::Mesh, view::Visibility}; use crate::{mesh::Mesh, view::Visibility};
use bevy_asset::{AssetId, Handle}; use bevy_asset::{AssetId, Handle};
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_ecs::{component::Component, prelude::require, reflect::ReflectComponent};
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_transform::components::Transform; use bevy_transform::components::Transform;
use derive_more::derive::From; use derive_more::derive::From;

View file

@ -1,6 +1,6 @@
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::component::Component; use bevy_ecs::component::{require, Component};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_transform::components::Transform; use bevy_transform::components::Transform;
use derive_more::derive::From; use derive_more::derive::From;

View file

@ -1,6 +1,9 @@
use bevy_asset::{Assets, Handle}; use bevy_asset::{Assets, Handle};
use bevy_color::Color; use bevy_color::Color;
use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_ecs::{
component::{require, Component},
reflect::ReflectComponent,
};
use bevy_image::Image; use bevy_image::Image;
use bevy_math::{Rect, UVec2, Vec2}; use bevy_math::{Rect, UVec2, Vec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};

View file

@ -7,9 +7,9 @@ use crate::{
use bevy_asset::Assets; use bevy_asset::Assets;
use bevy_color::LinearRgba; use bevy_color::LinearRgba;
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::component::Component;
use bevy_ecs::{ use bevy_ecs::{
change_detection::{DetectChanges, Ref}, change_detection::{DetectChanges, Ref},
component::{require, Component},
entity::Entity, entity::Entity,
prelude::{ReflectComponent, With}, prelude::{ReflectComponent, With},
query::{Changed, Without}, query::{Changed, Without},

View file

@ -3,7 +3,7 @@ use bevy_math::{Affine3A, Dir3, Isometry3d, Mat3, Mat4, Quat, Vec3};
use core::ops::Mul; use core::ops::Mul;
#[cfg(feature = "bevy-support")] #[cfg(feature = "bevy-support")]
use { use {
bevy_ecs::{component::Component, reflect::ReflectComponent}, bevy_ecs::{component::Component, prelude::require, reflect::ReflectComponent},
bevy_reflect::prelude::*, bevy_reflect::prelude::*,
}; };

View file

@ -1,7 +1,10 @@
use crate::Node; use crate::Node;
use bevy_asset::{Asset, AssetId, Handle}; use bevy_asset::{Asset, AssetId, Handle};
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_ecs::{
component::{require, Component},
reflect::ReflectComponent,
};
use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_reflect::{prelude::ReflectDefault, Reflect};
use bevy_render::{ use bevy_render::{
extract_component::ExtractComponent, extract_component::ExtractComponent,

View file

@ -1,5 +1,8 @@
use crate::{FocusPolicy, Interaction, Node}; use crate::{FocusPolicy, Interaction, Node};
use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; use bevy_ecs::{
prelude::{require, Component},
reflect::ReflectComponent,
};
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
/// Marker struct for buttons /// Marker struct for buttons

View file

@ -8,7 +8,7 @@ use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{ use bevy_ecs::{
change_detection::DetectChanges, change_detection::DetectChanges,
entity::{Entity, EntityHashMap}, entity::{Entity, EntityHashMap},
prelude::Component, prelude::{require, Component},
query::With, query::With,
reflect::ReflectComponent, reflect::ReflectComponent,
system::{Local, Query, Res, ResMut}, system::{Local, Query, Res, ResMut},