diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8a04fadc94530..8366a43785d2b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -73,7 +73,8 @@ jobs: --exclude bevy_mobile_example \ --exclude build-wasm-example \ --exclude build-templated-pages \ - --exclude example-showcase + --exclude example-showcase \ + --config "build.rustdoc = \"tools/rustdoc-wrapper/rustdoc.sh\"" # This adds the following: # - A top level redirect to the bevy crate documentation diff --git a/Cargo.toml b/Cargo.toml index 2c5785346e303..9d9159f11bf7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4119,17 +4119,17 @@ panic = "abort" # for details on why this is needed. Since dependencies don't expect to be built # with `--cfg docsrs` (and thus fail to compile) we use a different cfg. rustc-args = ["--cfg", "docsrs_dep"] -rustdoc-args = [ +rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"] +all-features = true +cargo-args = [ "-Zunstable-options", - "--generate-link-to-definition", + "-Zrustdoc-scrape-examples", # Embed tags to the top of documentation pages for common Bevy traits # that are implemented by the current type, like `Component` or `Resource`. # This makes it easier to see at a glance what types are used for. - "--html-after-content", - "docs-rs/trait-tags.html", + "--config", + "build.rustdoc = \"tools/rustdoc-wrapper/rustdoc.sh\"", ] -all-features = true -cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] [[example]] name = "monitor_info" diff --git a/crates/bevy_core_pipeline/src/motion_blur/mod.rs b/crates/bevy_core_pipeline/src/motion_blur/mod.rs index 7703698f1ab10..58466d7c825bb 100644 --- a/crates/bevy_core_pipeline/src/motion_blur/mod.rs +++ b/crates/bevy_core_pipeline/src/motion_blur/mod.rs @@ -9,10 +9,7 @@ use crate::{ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, weak_handle, Handle}; use bevy_ecs::{ - component::{require, Component}, - query::With, - reflect::ReflectComponent, - schedule::IntoSystemConfigs, + component::Component, query::With, reflect::ReflectComponent, schedule::IntoSystemConfigs, }; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{ diff --git a/crates/bevy_core_pipeline/src/taa/mod.rs b/crates/bevy_core_pipeline/src/taa/mod.rs index 55eb25ae02ebf..2f30544be9e73 100644 --- a/crates/bevy_core_pipeline/src/taa/mod.rs +++ b/crates/bevy_core_pipeline/src/taa/mod.rs @@ -8,7 +8,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, weak_handle, Handle}; use bevy_diagnostic::FrameCount; use bevy_ecs::{ - prelude::{require, Component, Entity, ReflectComponent}, + prelude::{Component, Entity, ReflectComponent}, query::{QueryItem, With}, resource::Resource, schedule::IntoSystemConfigs, diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index 06dd9b1591e69..f2c330df86a7e 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -1,4 +1,4 @@ -use proc_macro::{TokenStream, TokenTree}; +use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote, ToTokens}; use std::collections::HashSet; @@ -202,6 +202,21 @@ pub fn derive_component(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; 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::>() + .join(", "); + // Put information about required components onto the component implementation. + // This is rather hidden, but we can't modify the type's doc comment from a derive macro. + // Instead put it in the main description via our rustdoc wrapper. + let doc = format!("# 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."); + quote! { + #[doc = #doc] + } + }); + let mutable_type = (attrs.immutable || relationship.is_some()) .then_some(quote! { #bevy_ecs_path::component::Immutable }) .unwrap_or(quote! { #bevy_ecs_path::component::Mutable }); @@ -218,6 +233,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream { // 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 TokenStream::from(quote! { + #required_component_docs impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause { const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #storage; type Mutability = #mutable_type; @@ -402,34 +418,6 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { ) } -pub fn document_required_components(attr: TokenStream, item: TokenStream) -> TokenStream { - let paths = parse_macro_input!(attr with Punctuated::::parse_terminated) - .iter() - .map(|r| format!("[`{}`]", r.path.to_token_stream())) - .collect::>() - .join(", "); - - let bevy_ecs_path = crate::bevy_ecs_path() - .to_token_stream() - .to_string() - .replace(' ', ""); - let required_components_path = bevy_ecs_path + "::component::Component#required-components"; - - // 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]({required_components_path}) 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 STORAGE: &str = "storage"; pub const REQUIRE: &str = "require"; diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index f61889651df1e..9887f1fabe2c1 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -597,20 +597,12 @@ pub fn derive_resource(input: TokenStream) -> TokenStream { #[proc_macro_derive( Component, - attributes(component, relationship, relationship_target, entities) + attributes(component, require, relationship, relationship_target, entities) )] pub fn derive_component(input: TokenStream) -> TokenStream { 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)] pub fn derive_states(input: TokenStream) -> TokenStream { states::derive_states(input) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index eeb6820bae69d..b3ff8ed37c654 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -32,8 +32,6 @@ use core::{ use disqualified::ShortName; use thiserror::Error; -pub use bevy_ecs_macros::require; - /// 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. diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 61aa1b9a4e689..289f732d8f1ca 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -825,7 +825,6 @@ mod tests { world::{FromWorld, World}, }; use alloc::vec::Vec; - use bevy_ecs_macros::require; use bevy_ptr::OwningPtr; use bevy_reflect::Reflect; use core::{alloc::Layout, ops::Deref}; diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 8d415d7469183..bae3dbfed2438 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -72,7 +72,7 @@ pub mod prelude { bundle::Bundle, change_detection::{DetectChanges, DetectChangesMut, Mut, Ref}, children, - component::{require, Component}, + component::Component, entity::{Entity, EntityBorrow, EntityMapper}, event::{Event, EventMutator, EventReader, EventWriter, Events}, hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children}, @@ -132,7 +132,7 @@ mod tests { use crate::{ bundle::Bundle, change_detection::Ref, - component::{require, Component, ComponentId, RequiredComponents, RequiredComponentsError}, + component::{Component, ComponentId, RequiredComponents, RequiredComponentsError}, entity::Entity, entity_disabling::DefaultQueryFilters, prelude::Or, diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 442c4185dc464..12c5427225b5d 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -2222,7 +2222,7 @@ impl<'a, T: Component> EntityEntryCommands<'a, T> { #[cfg(test)] mod tests { use crate::{ - component::{require, Component}, + component::Component, resource::Resource, system::Commands, world::{CommandQueue, FromWorld, World}, diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index cf42d0d875896..f1837f99dd6f5 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -7,7 +7,7 @@ use crate::{ world::World, }; use alloc::boxed::Box; -use bevy_ecs_macros::{require, Component, Resource}; +use bevy_ecs_macros::{Component, Resource}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; use core::marker::PhantomData; diff --git a/crates/bevy_gizmos/src/retained.rs b/crates/bevy_gizmos/src/retained.rs index 435f417552463..51170144b1632 100644 --- a/crates/bevy_gizmos/src/retained.rs +++ b/crates/bevy_gizmos/src/retained.rs @@ -3,10 +3,7 @@ use core::ops::{Deref, DerefMut}; use bevy_asset::Handle; -use bevy_ecs::{ - component::{require, Component}, - reflect::ReflectComponent, -}; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::Reflect; use bevy_transform::components::Transform; diff --git a/crates/bevy_input/src/gamepad.rs b/crates/bevy_input/src/gamepad.rs index 30d503cf7947f..4487a3caa33ab 100644 --- a/crates/bevy_input/src/gamepad.rs +++ b/crates/bevy_input/src/gamepad.rs @@ -12,7 +12,6 @@ use bevy_ecs::{ entity::Entity, event::{Event, EventReader, EventWriter}, name::Name, - prelude::require, system::{Commands, Query}, }; use bevy_math::ops; diff --git a/crates/bevy_pbr/src/atmosphere/mod.rs b/crates/bevy_pbr/src/atmosphere/mod.rs index f525c0e2b8d3d..82dc201c5de2d 100644 --- a/crates/bevy_pbr/src/atmosphere/mod.rs +++ b/crates/bevy_pbr/src/atmosphere/mod.rs @@ -36,7 +36,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::load_internal_asset; use bevy_core_pipeline::core_3d::graph::Node3d; use bevy_ecs::{ - component::{require, Component}, + component::Component, query::{Changed, QueryItem, With}, schedule::IntoSystemConfigs, system::{lifetimeless::Read, Query}, diff --git a/crates/bevy_pbr/src/decal/clustered.rs b/crates/bevy_pbr/src/decal/clustered.rs index 43edcd0bc34d5..d382d50c8ced4 100644 --- a/crates/bevy_pbr/src/decal/clustered.rs +++ b/crates/bevy_pbr/src/decal/clustered.rs @@ -20,7 +20,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, weak_handle, AssetId, Handle}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - component::{require, Component}, + component::Component, entity::{hash_map::EntityHashMap, Entity}, prelude::ReflectComponent, query::With, diff --git a/crates/bevy_pbr/src/decal/forward.rs b/crates/bevy_pbr/src/decal/forward.rs index 7732f1d3a4ab3..8490017fbbae6 100644 --- a/crates/bevy_pbr/src/decal/forward.rs +++ b/crates/bevy_pbr/src/decal/forward.rs @@ -4,7 +4,7 @@ use crate::{ }; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, weak_handle, Asset, Assets, Handle}; -use bevy_ecs::component::{require, Component}; +use bevy_ecs::component::Component; use bevy_math::{prelude::Rectangle, Quat, Vec2, Vec3}; use bevy_reflect::{Reflect, TypePath}; use bevy_render::{ @@ -63,7 +63,7 @@ impl Plugin for ForwardDecalPlugin { /// # Usage Notes /// /// * Spawn this component on an entity with a [`crate::MeshMaterial3d`] component holding a [`ForwardDecalMaterial`]. -/// * Any camera rendering a forward decal must have the [`bevy_core_pipeline::DepthPrepass`] component. +/// * Any camera rendering a forward decal must have the [`bevy_core_pipeline::prepass::DepthPrepass`] component. /// * Looking at forward decals at a steep angle can cause distortion. This can be mitigated by padding your decal's /// texture with extra transparent pixels on the edges. #[derive(Component, Reflect)] diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index c728a1cc6b863..65a7ee6740668 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -5,7 +5,7 @@ use bevy_asset::{load_internal_asset, weak_handle, AssetId, Handle}; use bevy_core_pipeline::core_3d::Camera3d; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - component::{require, Component}, + component::Component, entity::Entity, query::With, reflect::ReflectComponent, diff --git a/crates/bevy_pbr/src/meshlet/mod.rs b/crates/bevy_pbr/src/meshlet/mod.rs index 4057f29e39a3c..bf701acb1c032 100644 --- a/crates/bevy_pbr/src/meshlet/mod.rs +++ b/crates/bevy_pbr/src/meshlet/mod.rs @@ -65,7 +65,7 @@ use bevy_core_pipeline::{ }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - component::{require, Component}, + component::Component, entity::Entity, query::Has, reflect::ReflectComponent, diff --git a/crates/bevy_pbr/src/ssao/mod.rs b/crates/bevy_pbr/src/ssao/mod.rs index 4d97f52cff4ff..3422f511c5167 100644 --- a/crates/bevy_pbr/src/ssao/mod.rs +++ b/crates/bevy_pbr/src/ssao/mod.rs @@ -7,7 +7,7 @@ use bevy_core_pipeline::{ prepass::{DepthPrepass, NormalPrepass, ViewPrepassTextures}, }; use bevy_ecs::{ - prelude::{require, Component, Entity}, + prelude::{Component, Entity}, query::{Has, QueryItem, With}, reflect::ReflectComponent, resource::Resource, diff --git a/crates/bevy_pbr/src/ssr/mod.rs b/crates/bevy_pbr/src/ssr/mod.rs index 15b783cef5d5f..fa7424145f51b 100644 --- a/crates/bevy_pbr/src/ssr/mod.rs +++ b/crates/bevy_pbr/src/ssr/mod.rs @@ -12,7 +12,7 @@ use bevy_core_pipeline::{ }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - component::{require, Component}, + component::Component, entity::Entity, query::{Has, QueryItem, With}, reflect::ReflectComponent, diff --git a/crates/bevy_pbr/src/volumetric_fog/mod.rs b/crates/bevy_pbr/src/volumetric_fog/mod.rs index 4b90d63afccb7..ff0b913f1d3cc 100644 --- a/crates/bevy_pbr/src/volumetric_fog/mod.rs +++ b/crates/bevy_pbr/src/volumetric_fog/mod.rs @@ -36,11 +36,7 @@ use bevy_core_pipeline::core_3d::{ graph::{Core3d, Node3d}, prepare_core_3d_depth_textures, }; -use bevy_ecs::{ - component::{require, Component}, - reflect::ReflectComponent, - schedule::IntoSystemConfigs as _, -}; +use bevy_ecs::{component::Component, reflect::ReflectComponent, schedule::IntoSystemConfigs as _}; use bevy_image::Image; use bevy_math::{ primitives::{Cuboid, Plane3d}, diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 298be27c5fa9e..0c67f0ea049b0 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -25,7 +25,7 @@ use bevy_ecs::{ component::{Component, HookContext}, entity::{Entity, EntityBorrow}, event::EventReader, - prelude::{require, With}, + prelude::With, query::Has, reflect::ReflectComponent, resource::Resource, diff --git a/crates/bevy_render/src/mesh/components.rs b/crates/bevy_render/src/mesh/components.rs index b5b03ac2b8f27..f55897ce6d82a 100644 --- a/crates/bevy_render/src/mesh/components.rs +++ b/crates/bevy_render/src/mesh/components.rs @@ -5,7 +5,7 @@ use crate::{ use bevy_asset::{AsAssetId, AssetEvent, AssetId, Handle}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - change_detection::DetectChangesMut, component::Component, event::EventReader, prelude::require, + change_detection::DetectChangesMut, component::Component, event::EventReader, reflect::ReflectComponent, system::Query, }; use bevy_platform_support::{collections::HashSet, hash::FixedHasher}; diff --git a/crates/bevy_scene/src/components.rs b/crates/bevy_scene/src/components.rs index 8709c7990fd50..355628c6c7300 100644 --- a/crates/bevy_scene/src/components.rs +++ b/crates/bevy_scene/src/components.rs @@ -1,9 +1,6 @@ use bevy_asset::Handle; use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::{ - component::{require, Component}, - prelude::ReflectComponent, -}; +use bevy_ecs::{component::Component, prelude::ReflectComponent}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_transform::components::Transform; use derive_more::derive::From; diff --git a/crates/bevy_sprite/src/sprite.rs b/crates/bevy_sprite/src/sprite.rs index 59c60071a0293..82d5f155a88e5 100644 --- a/crates/bevy_sprite/src/sprite.rs +++ b/crates/bevy_sprite/src/sprite.rs @@ -1,9 +1,6 @@ use bevy_asset::{Assets, Handle}; use bevy_color::Color; -use bevy_ecs::{ - component::{require, Component}, - reflect::ReflectComponent, -}; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_image::{Image, TextureAtlas, TextureAtlasLayout}; use bevy_math::{Rect, UVec2, Vec2}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 13e9760f29a13..394b12d03b7be 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -10,7 +10,7 @@ use bevy_derive::{Deref, DerefMut}; use bevy_ecs::entity::hash_set::EntityHashSet; use bevy_ecs::{ change_detection::{DetectChanges, Ref}, - component::{require, Component}, + component::Component, entity::Entity, prelude::{ReflectComponent, With}, query::{Changed, Without}, diff --git a/crates/bevy_transform/src/components/transform.rs b/crates/bevy_transform/src/components/transform.rs index 2949015848470..8dc4d2453c718 100644 --- a/crates/bevy_transform/src/components/transform.rs +++ b/crates/bevy_transform/src/components/transform.rs @@ -3,7 +3,7 @@ use bevy_math::{Affine3A, Dir3, Isometry3d, Mat3, Mat4, Quat, Vec3}; use core::ops::Mul; #[cfg(feature = "bevy-support")] -use bevy_ecs::{component::Component, prelude::require}; +use bevy_ecs::component::Component; #[cfg(feature = "bevy_reflect")] use {bevy_ecs::reflect::ReflectComponent, bevy_reflect::prelude::*}; diff --git a/crates/bevy_ui/src/ui_material.rs b/crates/bevy_ui/src/ui_material.rs index a9d712d5be548..9f56e834a4c73 100644 --- a/crates/bevy_ui/src/ui_material.rs +++ b/crates/bevy_ui/src/ui_material.rs @@ -1,10 +1,7 @@ use crate::Node; use bevy_asset::{Asset, AssetId, Handle}; use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::{ - component::{require, Component}, - reflect::ReflectComponent, -}; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_render::{ extract_component::ExtractComponent, diff --git a/crates/bevy_ui/src/widget/button.rs b/crates/bevy_ui/src/widget/button.rs index 8445a4ad6266b..a4e5afc6fae48 100644 --- a/crates/bevy_ui/src/widget/button.rs +++ b/crates/bevy_ui/src/widget/button.rs @@ -1,8 +1,5 @@ use crate::{FocusPolicy, Interaction, Node}; -use bevy_ecs::{ - prelude::{require, Component}, - reflect::ReflectComponent, -}; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; /// Marker struct for buttons diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 42c91fd833415..0be96febabc25 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -7,8 +7,8 @@ use bevy_color::Color; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ change_detection::DetectChanges, + component::Component, entity::Entity, - prelude::{require, Component}, query::With, reflect::ReflectComponent, system::{Query, Res, ResMut}, diff --git a/docs-rs/README.md b/docs-rs/README.md deleted file mode 100644 index 5d9092b806185..0000000000000 --- a/docs-rs/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Docs.rs Extensions - -This directory includes some templates and styling to extend and modify [rustdoc]'s output -for Bevy's documentation on [docs.rs]. Currently this consists of tags indicating core -`bevy_ecs` traits. - -## 3rd Party Crates - -To use in your own crate, first copy this folder into your project, -then add the following to your Cargo.toml: - -```toml -[package.metadata.docs.rs] -rustc-args = ["--cfg", "docsrs_dep"] -rustdoc-args = [ - "--cfg", "docsrs_dep", - "--html-after-content", "docs-rs/trait-tags.html", -] - -[lints.rust] -unexpected_cfgs = { check-cfg = ['cfg(docsrs_dep)'] } -``` - -## Local Testing - -Build the documentation with the extension enabled like this: - -```bash -RUSTDOCFLAGS="--html-after-content docs-rs/trait-tags.html --cfg docsrs_dep" RUSTFLAGS="--cfg docsrs_dep" cargo doc --no-deps --package -``` - -[rustdoc]: https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html -[docs.rs]: https://docs.rs diff --git a/docs-rs/trait-tags.html b/docs-rs/trait-tags.html deleted file mode 100644 index bdd7ba1fb2338..0000000000000 --- a/docs-rs/trait-tags.html +++ /dev/null @@ -1,172 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/rustdoc-wrapper/.gitignore b/tools/rustdoc-wrapper/.gitignore new file mode 100644 index 0000000000000..2f7896d1d1365 --- /dev/null +++ b/tools/rustdoc-wrapper/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/tools/rustdoc-wrapper/Cargo.toml b/tools/rustdoc-wrapper/Cargo.toml new file mode 100644 index 0000000000000..39f8061de140c --- /dev/null +++ b/tools/rustdoc-wrapper/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rustdoc-wrapper" +version = "0.1.0" +edition = "2021" + +[dependencies] +walkdir = "2" +kuchikiki = "0.8" +dom_query = "0.15" diff --git a/tools/rustdoc-wrapper/README.md b/tools/rustdoc-wrapper/README.md new file mode 100644 index 0000000000000..55d5f817ca028 --- /dev/null +++ b/tools/rustdoc-wrapper/README.md @@ -0,0 +1,34 @@ +# Rustdoc Postprocessor + +We want to adjust rustdoc's html output to make it more obvious +which types are `Component`s, `Plugin`s etc. To do so, this +tool wraps rustdoc and modifies its output by adding relevant tags +to the top of a type's doc page. + +Note that the format of rustdoc's html output is (and always will be) unstable. These customizations may therefor break at time, at which point they should be enabled until fixed. + +On docs.rs and dev-docs.bevyengine.org the wrapper is invoked by passing the following flag: + +```none +--config "build.rustdoc = \"tools/rustdoc-wrapper/rustdoc.sh\"" +``` + +If you want to build Bevy's documentation with these customizations +applied yourself, first compile it: + +```bash +cargo build --release --package rustdoc-wrapper +``` + +and then point `build.rustdoc` at it. + +If you want to be able to run cargo doc without passing the right rustdoc path every time, you can set it in your `.cargo/config.toml`: + +```toml +[build] +rustdoc = "target/release/rustdoc-wrapper" +``` + +## 3rd-Party Crates + +The above also works with other crates that use Bevy. diff --git a/tools/rustdoc-wrapper/rustdoc.sh b/tools/rustdoc-wrapper/rustdoc.sh new file mode 100755 index 0000000000000..0f0a359f6f6bc --- /dev/null +++ b/tools/rustdoc-wrapper/rustdoc.sh @@ -0,0 +1,6 @@ +#!/bin/env sh + +# Use a different target directory because the workspace-level one will be locked. +cargo build --package rustdoc-wrapper --release --target-dir tools/rustdoc-wrapper/target --color always +# Pass on all arguments to our rustdoc wrapper. +tools/rustdoc-wrapper/target/release/rustdoc-wrapper "$@" diff --git a/tools/rustdoc-wrapper/src/main.rs b/tools/rustdoc-wrapper/src/main.rs new file mode 100644 index 0000000000000..0db394070cccd --- /dev/null +++ b/tools/rustdoc-wrapper/src/main.rs @@ -0,0 +1,203 @@ +use std::{ + collections::{HashMap, HashSet}, + ffi::OsStr, + fs::read_to_string, + ops::Deref, + path::Path, + process::Command, + sync::LazyLock, +}; + +use dom_query::Document; +use walkdir::WalkDir; + +fn main() { + // Generate HTML as normal + assert!(Command::new("rustdoc") + .args(std::env::args().skip(1)) + .status() + .unwrap() + .success()); + + // Find package name + let package = std::env::args() + .skip_while(|arg| *arg != "--crate-name") + .nth(1) + .expect("No crate name passed") + .clone(); + + // Find output directory + let target_dir = std::env::args() + .skip_while(|arg| (*arg != "-o") & (*arg != "--output")) + .nth(1) + .unwrap() + .clone(); + let package_dir = Path::new(&target_dir).join(package); + + // Extra style sheet is shared between files. + // It's not shared between docs.rs packages though, so don't put it in static.files + std::fs::write(package_dir.join("bevy_style.css"), STYLE.as_bytes()).unwrap(); + + // Post-process HTML to apply our modifications + for entry in WalkDir::new(package_dir) { + let entry = entry.unwrap(); + let path = entry.path(); + if path.extension() == Some(OsStr::new("html")) { + let mut doc = Document::from(&*read_to_string(path).unwrap()); + add_trait_tags(&mut doc); + add_required_components(&mut doc); + let style_url = "../".repeat(entry.depth() - 1) + "bevy_style.css"; + doc.select("head") + .append_html(format!("")); + std::fs::write(path, doc.html().as_bytes()).unwrap(); + } + } +} + +/// Adds tags indicating which core Bevy traits it implements to a type's page. +// We only use the HTML and not rustdoc's JSON output because +// we would need to be able to match Rust items to HTML files. +// There is an index from HTML to source file we could use, but +// it's in JS instead of in an easily parsable format. +// Scanning the HTML is also bit simpler as we don't need to e.g. +// manually resolver blob imports. +fn add_trait_tags(doc: &mut Document) { + let traits = implemented_bevy_traits(doc); + + // Tags sit below headline + let heading = doc.select("h1").first(); + heading.append_html("
"); + let container = heading.select(".bevy-tag-container"); + + for (mut tag, url) in traits { + if (tag == "Component") + & doc + .select(".trait-impl.associatedtype .code-header") + .iter() + .any(|assoc| assoc.text().contains("type Mutability = Immutable")) + { + tag = "Immutable Component".to_owned() + } + + container.append_html(format!( + "{tag}", + tag.to_lowercase(), + url.unwrap_or_default() + )); + } +} + +/// Adds information about a component's required components +fn add_required_components(doc: &mut Document) { + // Insert as first section after main doc comment + let next_heading = doc.select("#main-content > h2:first-of-type"); + + for elem in doc.select(":has(> #required-components) > *").iter() { + next_heading.before_html(elem.html()); + } + + // The original section nested in the trait implementation uses a smaller heading, so adjust + doc.select("#required-components").first().rename("h2"); +} + +/// If this is the documentation page of a single type, +/// returns which of the relevant traits it implements, +/// alongside a (relative) url to the trait, if available. +fn implemented_bevy_traits(doc: &Document) -> HashMap> { + // Scanning the table of contents is easiest, but we need to find the link + // elsewhere as the ToC just points at the impls section. + doc.select("#rustdoc-toc .trait-implementation a") + .iter() + .filter_map(|label| { + let name = label.text(); + BEVY_TRAITS + .contains(&*name) + .then(|| ((*name).to_owned(), trait_url(doc, &name))) + }) + .collect() +} + +fn trait_url(doc: &Document, name: &str) -> Option { + let search = format!("trait.{name}.html"); + doc.select("a").iter().find_map(|a| { + a.attr("href") + .and_then(|url| url.ends_with(&search).then_some(url.deref().to_owned())) + }) +} + +static BEVY_TRAITS: LazyLock> = LazyLock::new(|| { + [ + "Plugin", + "PluginGroup", + "Component", + "Resource", + "Asset", + "Event", + "ScheduleLabel", + "SystemSet", + "SystemParam", + "Relationship", + "RelationshipTarget", + ] + .iter() + .map(|s| s.to_string()) + .collect() +}); + +const STYLE: &str = " +.bevy-tag-container { + padding: 0.5rem 0; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.bevy-tag { + display: flex; + align-items: center; + width: fit-content; + height: 1.5rem; + padding: 0 0.5rem; + border-radius: 0.75rem; + font-size: 1rem; + font-weight: normal; + color: white; + background-color: var(--tag-color); +} + +.component-tag, +.immutable-component-tag { + --tag-color: oklch(50% 27% 95); +} + +.resource-tag { + --tag-color: oklch(50% 27% 110); +} + +.asset-tag { + --tag-color: oklch(50% 27% 0); +} + +.event-tag { + --tag-color: oklch(50% 27% 310); +} + +.plugin-tag, +.plugingroup-tag { + --tag-color: oklch(50% 27% 50); +} + +.schedulelabel-tag, +.systemset-tag { + --tag-color: oklch(50% 27% 270); +} + +.systemparam-tag { + --tag-color: oklch(50% 27% 200); +} + +.relationship-tag, +.relationshiptarget-tag { + --tag-color: oklch(50% 27% 150); +} +";